Compare commits

...

38 Commits

Author SHA1 Message Date
Marc Vertes
71f730b3d7 fix: handle forward declaration for aliased type 2020-04-22 10:50:05 +02:00
Marc Vertes
7b2d91bcb5 fix: handling values in map of interfaces 2020-04-22 10:34:05 +02:00
Marc Vertes
3ed4ec3f6f fix: correct type assertion for bin func types (#588) 2020-04-20 23:01:01 +02:00
Marc Vertes
5e142fdedd fix: do not attempt to copy data in empty frame at func call 2020-04-18 18:36:04 +02:00
Marc Vertes
94d44e7265 fix: do not convert literal float to int at parse 2020-04-17 17:54:03 +02:00
Marc Vertes
56925e6fea fix: correct branch control flow graph for parenthesis expressions (#583) 2020-04-17 14:40:56 +02:00
Marc Vertes
c580dfdbc8 fix: correct handling of interface types in composite literals 2020-04-16 19:54:03 +02:00
Marc Vertes
29e1777d82 fix: assign composite literal by reference 2020-04-16 12:24:04 +02:00
Marc Vertes
50a34fd2a7 fix: correct control flow graph for select blocks 2020-04-15 12:24:04 +02:00
Marc Vertes
465cb578e7 fix: lookup embededded field on struct pointer 2020-04-09 01:26:03 +02:00
Marc Vertes
12942b59a0 fix: remove ambiguities in recursive type processing 2020-04-09 01:14:03 +02:00
Marc Vertes
3e76267f8e fix: method search on struct pointer in interface wrapper 2020-04-07 17:22:04 +02:00
Marc Vertes
988f0c9672 fix: better handling of recursive types and forward declarations 2020-04-07 13:06:03 +02:00
Marc Vertes
b0053c874f fix: incomplete type analysis 2020-04-03 04:14:04 +02:00
Marc Vertes
b20ad3a01d fix: checks that value implements a binary type in type assert 2020-04-03 04:02:04 +02:00
Marc Vertes
e78650d359 fix: correct branch action in parenthesis expression 2020-03-26 12:10:04 +01:00
Marc Vertes
7327ff2811 fix: correct comparison of interface type to nil 2020-03-25 13:56:05 +01:00
Marc Vertes
ebde09b47d fix: correct control flow graph for switch statement 2020-03-25 12:40:04 +01:00
Marc Vertes
4995654e04 fix: correct control flow graph for constant conditional statements 2020-03-25 12:18:05 +01:00
Marc Vertes
0a99eb48c3 fix: do not pass twice the receiver in deferred method calls 2020-03-23 13:40:03 +01:00
Ludovic Fernandez
4a22635585 doc: update supported Go version in the readme. 2020-03-20 12:36:04 +01:00
Marc Vertes
b52dd8cc08 fix: substitute recursive struct type by interface{} in function arguments 2020-03-19 12:42:05 +01:00
Marc Vertes
daaeac6e2c fix: convert literal nil to interface types 2020-03-18 10:34:05 +01:00
Marc Vertes
ca68c6cd95 fix: resolve embedded method on pointer types 2020-03-17 18:14:04 +01:00
Marc Vertes
953b122e67 fix: avoid infinite recursion in genFunctionWrapper() 2020-03-17 18:02:05 +01:00
Marc Vertes
9b07e73b5e fix: resolve receiver for binary methods on non interface types 2020-03-12 14:42:04 +01:00
Marc Vertes
78bbcda1f8 fix: do not overwrite input for assign operators 2020-03-12 12:24:04 +01:00
Marc Vertes
6e33f89146 fix: correct control flow graph for some switch statements 2020-03-09 18:20:04 +01:00
Dmitrii Okunev
d29b0a48ff Add option "-e"
Option "-e" allows to set the command to be executed:

```
echo '6001d5ff0000000003000000000107000000dcff' | \
	yaegi -e 'import "fmt"; import "os"; import "encoding/hex"; import "io/ioutil"; func main() { in, _ := ioutil.ReadAll(os.Stdin); decoded, _ := hex.DecodeString(string(in));fmt.Println(string(decoded)) }' 2>/dev/null | \
	hexdump -C
00000000  60 01 d5 ff 00 00 00 00  03 00 00 00 00 01 07 00  |`...............|
00000010  00 00 dc ff 0a                                    |.....|
00000015
```
2020-03-09 18:07:04 +01:00
Marc Vertes
c7c1bea7ef fix: do not attempt to store data in _ var 2020-03-09 13:22:04 +01:00
Marc Vertes
1ae2649655 fix: correct control flow graph for defer statements 2020-03-09 10:52:05 +01:00
Traefiker Bot
0ace9244c4 fix: correctly init variables from index expressions 2020-03-05 14:28:06 +01:00
Traefiker Bot
2edd18a0c0 fix: handle use of functions in struct fields 2020-03-05 13:40:05 +01:00
Marc Vertes
cfb73445a2 fix: handle interface values in map and arrays 2020-03-03 18:32:04 +01:00
Ludovic Fernandez
94e0b582ea Update stdlib for go1.14 2020-02-26 15:06:06 +01:00
Dan Kortschak
3548c8744e interp: weaken panics to errors and return panicked values 2020-02-25 13:12:05 +01:00
Marc Vertes
d8bdc6670b fix: detect field names in struct pointer 2020-02-22 15:24:06 +01:00
Marc Vertes
27520f6dae fix: re-apply GTA until all global types/constants are defined 2020-02-20 12:44:04 +01:00
289 changed files with 9209 additions and 1967 deletions

View File

@@ -37,6 +37,7 @@
"funlen",
"gocognit",
"stylecheck",
"gomnd",
]
[issues]

View File

@@ -19,8 +19,8 @@ cache:
matrix:
fast_finish: true
include:
- go: 1.12.x
- go: 1.13.x
- go: 1.14.x
env: STABLE=true
env:

View File

@@ -18,7 +18,7 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
* Works everywhere Go works
* All Go & runtime resources accessible from script (with control)
* Security: `unsafe` and `syscall` packages neither used nor exported by default
* Support Go 1.12 and Go 1.13 (the latest 2 major releases)
* Support Go 1.13 and Go 1.14 (the latest 2 major releases)
## Install

View File

@@ -9,5 +9,5 @@ func main() {
fmt.Println(buf)
}
// Output
// Output:
// []

View File

@@ -1,4 +1,4 @@
println("Hello")
// Error:
// _test/bad0.go:1:1: expected 'package', found println
// 1:1: expected 'package', found println

12
_test/bin3.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
str := "part1"
str += fmt.Sprintf("%s", "part2")
fmt.Println(str)
}
// Output:
// part1part2

20
_test/chan9.go Normal file
View File

@@ -0,0 +1,20 @@
package main
type Channel chan string
type T struct {
Channel
}
func send(c Channel) { c <- "ping" }
func main() {
t := &T{}
t.Channel = make(Channel)
go send(t.Channel)
msg := <-t.Channel
println(msg)
}
// Output:
// ping

59
_test/cli2.go Normal file
View File

@@ -0,0 +1,59 @@
package main
import (
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
)
type T struct {
ln net.Listener
}
func (t *T) Close() {
t.ln.Close()
}
func client(uri string) {
resp, err := http.Get(uri)
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
}
func server(ln net.Listener, ready chan bool) {
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
var r1 *http.Request = r
fmt.Fprintln(w, "Welcome to my website!", r1.RequestURI)
})
go http.Serve(ln, nil)
ready <- true
}
func main() {
ln, err := net.Listen("tcp", "localhost:0")
t := &T{ln}
if err != nil {
log.Fatal(err)
}
defer t.Close()
// defer ln.Close()
ready := make(chan bool)
go server(ln, ready)
<-ready
client(fmt.Sprintf("http://%s/hello", ln.Addr().String()))
http.DefaultServeMux = &http.ServeMux{}
}
// Output:
// Welcome to my website! /hello

View File

@@ -7,5 +7,5 @@ func main() {
fmt.Printf("%T %v\n", s, s)
}
// Output
// Output:
// int 2

20
_test/composite7.go Normal file
View File

@@ -0,0 +1,20 @@
package main
type T struct {
name string
}
var tab = []*T{{
name: "foo",
}, {
name: "bar",
}}
func main() {
println(len(tab))
println(tab[0].name)
}
// Output:
// 2
// foo

16
_test/composite8.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type T struct{ I int }
func main() {
t := []*T{}
s := []int{1, 2}
for _, e := range s {
x := &T{e}
t = append(t, x)
}
println(t[0].I, t[1].I)
}
// Output:
// 1 2

17
_test/const10.go Normal file
View File

@@ -0,0 +1,17 @@
package main
const (
a = 2
b = c + d
c = a + d
d = e + f
e = 3
f = 4
)
func main() {
println(b)
}
// Output:
// 16

15
_test/const8.go Normal file
View File

@@ -0,0 +1,15 @@
package main
const (
a = 2
b = c + d
c = 4
d = 5
)
func main() {
println(a, b, c, d)
}
// Output:
// 2 9 4 5

17
_test/const9.go Normal file
View File

@@ -0,0 +1,17 @@
package main
const (
a = 2
b = c + d
c = a + d
d = e + f
e = b + 2
f = 4
)
func main() {
println(b)
}
// Error:
// 5:2: constant definition loop

23
_test/defer4.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import "sync"
type T struct {
mu sync.RWMutex
name string
}
func (t *T) get() string {
t.mu.RLock()
defer t.mu.RUnlock()
return t.name
}
var d = T{name: "test"}
func main() {
println(d.get())
}
// Output:
// test

13
_test/for10.go Normal file
View File

@@ -0,0 +1,13 @@
package main
func main() {
for a := 0; false; {
println("nok", a)
a++
break
}
println("bye")
}
// Output:
// bye

14
_test/for11.go Normal file
View File

@@ -0,0 +1,14 @@
package main
func main() {
a := 0
for ; true; a++ {
println("nok", a)
break
}
println("bye", a)
}
// Output:
// nok 0
// bye 0

12
_test/for12.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
for a := 0; false; a++ {
println("nok", a)
break
}
println("bye")
}
// Output:
// bye

13
_test/for13.go Normal file
View File

@@ -0,0 +1,13 @@
package main
func main() {
a := 0
for ; false; a++ {
println("nok", a)
break
}
println("bye", a)
}
// Output:
// bye 0

16
_test/for14.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func main() {
for a := 0; true; a++ {
println(a)
if a > 0 {
break
}
}
println("bye")
}
// Output:
// 0
// 1
// bye

12
_test/for9.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
for false {
println("nok")
break
}
println("bye")
}
// Output:
// bye

12
_test/fun12.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func use(interface{}) {}
func main() {
z := map[string]interface{}{"a": 5}
use(z)
println("bye")
}
// Output:
// bye

17
_test/if3.go Normal file
View File

@@ -0,0 +1,17 @@
package main
func main() {
a := 0
if false {
println("false")
a = 1
} else {
println("true")
a = -1
}
println(a)
}
// Output:
// true
// -1

19
_test/if4.go Normal file
View File

@@ -0,0 +1,19 @@
package main
const bad = false
func main() {
a := 0
if bad {
println("false")
a = 1
} else {
println("true")
a = -1
}
println(a)
}
// Output:
// true
// -1

12
_test/if5.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
if true {
println("ok")
}
println("bye")
}
// Output:
// ok
// bye

11
_test/if6.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func main() {
if false {
println("nok")
}
println("bye")
}
// Output:
// bye

15
_test/if7.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func main() {
a := 0
b := false
if (b) {
a = 1
} else {
a = -1
}
println(a)
}
// Output:
// -1

12
_test/interface20.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
var a interface{}
a = string("A")
fmt.Println(a)
}
// Output:
// A

12
_test/interface21.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
s := make([]interface{}, 1)
s[0] = 1
fmt.Println(s[0])
}
// Output:
// 1

12
_test/interface22.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
s := make([]interface{}, 0)
s = append(s, 1)
fmt.Println(s[0])
}
// Output:
// 1

12
_test/interface23.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
m := make(map[string]interface{})
m["A"] = string("A")
fmt.Println(m["A"])
}
// Output:
// A

11
_test/interface24.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
m := make(map[string]interface{})
fmt.Println(m["B"])
}
// Output:
// <nil>

14
_test/interface25.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
m := make(map[string]interface{})
m["A"] = 1
for _, v := range m {
fmt.Println(v)
}
}
// Output:
// 1

14
_test/interface26.go Normal file
View File

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

12
_test/interface27.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
var errs = map[int]error{0: nil}
func main() {
fmt.Println(errs)
}
// Output:
// map[0:<nil>]

12
_test/interface28.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
var errs = []error{nil}
func main() {
fmt.Println(errs)
}
// Output:
// [<nil>]

9
_test/interface29.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
var a interface{}
println(a == nil)
}
// Output:
// true

9
_test/interface30.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
var a interface{}
println(a != nil)
}
// Output:
// false

11
_test/interface31.go Normal file
View File

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

11
_test/interface32.go Normal file
View File

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

11
_test/interface33.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
var a = map[string]interface{}{"test": "test"}
fmt.Println(a["test"])
}
// Output:
// test

11
_test/interface34.go Normal file
View File

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

15
_test/interface35.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
type T struct {
I interface{}
}
func main() {
t := T{"test"}
fmt.Println(t)
}
// Output:
// {test}

55
_test/issue-558.go Normal file
View File

@@ -0,0 +1,55 @@
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"strings"
)
type readAutoCloser struct {
r io.ReadCloser
}
func (a readAutoCloser) Read(b []byte) (n int, err error) {
if a.r == nil {
return 0, io.EOF
}
n, err = a.r.Read(b)
if err == io.EOF {
a.Close()
}
return n, err
}
func (a readAutoCloser) Close() error {
if a.r == nil {
return nil
}
return a.r.(io.Closer).Close()
}
type pipe struct {
Reader readAutoCloser
}
func newReadAutoCloser(r io.Reader) readAutoCloser {
if _, ok := r.(io.Closer); !ok {
return readAutoCloser{ioutil.NopCloser(r)}
}
return readAutoCloser{r.(io.ReadCloser)}
}
func main() {
p := &pipe{}
p.Reader = newReadAutoCloser(strings.NewReader("test"))
b, err := ioutil.ReadAll(p.Reader)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
}
// Output:
// test

View File

@@ -14,3 +14,6 @@ func main() {
m := cmap{}
fmt.Println(m)
}
// Output:
// {map[]}

15
_test/map21.go Normal file
View File

@@ -0,0 +1,15 @@
package main
var m = map[int]string{
1: "foo",
}
func main() {
var ok bool
if _, ok = m[1]; ok {
println("ok")
}
}
// Output:
// ok

14
_test/map22.go Normal file
View File

@@ -0,0 +1,14 @@
package main
var m = map[int]string{
1: "foo",
}
func main() {
var s string
s, _ = m[1]
println(s)
}
// Output:
// foo

13
_test/map23.go Normal file
View File

@@ -0,0 +1,13 @@
package main
var m = map[int]string{
1: "foo",
}
func main() {
_, _ = m[1]
println("ok")
}
// Output:
// ok

18
_test/map24.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"encoding/json"
"fmt"
)
func main() {
jb := []byte(`{"property": "test"}`)
params := map[string]interface{}{"foo": 1}
if err := json.Unmarshal(jb, &params); err != nil {
panic("marshal failed.")
}
fmt.Println(params["foo"], params["property"])
}
// Output:
// 1 test

26
_test/map25.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"encoding/json"
"strconv"
)
func main() {
jb := []byte(`{"num": "2"}`)
params := map[string]interface{}{"foo": "1"}
if err := json.Unmarshal(jb, &params); err != nil {
panic(err)
}
sum := 0
for _, v := range params {
i, err := strconv.Atoi(v.(string))
if err != nil {
panic(err)
}
sum += i
}
println(sum)
}
// Output:
// 3

16
_test/not0.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func main() {
a := 0
b := true
c := false
if (b && c) {
a = 1
} else {
a = -1
}
println(a)
}
// Output:
// -1

15
_test/not1.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func main() {
a := 0
b := true
if (!b) {
a = 1
} else {
a = -1
}
println(a)
}
// Output:
// -1

10
_test/num0.go Normal file
View File

@@ -0,0 +1,10 @@
package main
import "fmt"
func main() {
fmt.Printf("%g\n", 1.0)
}
// Output:
// 1

View File

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

12
_test/op5.go Normal file
View File

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

17
_test/select4.go Normal file
View File

@@ -0,0 +1,17 @@
package main
func main() {
c1 := make(chan string)
go func() { c1 <- "done" }()
select {
case msg1 := <-c1:
println("received from c1:", msg1)
}
println("Bye")
}
// Output:
// received from c1: done
// Bye

22
_test/select5.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type T struct {
c1 chan string
}
func main() {
t := &T{}
t.c1 = make(chan string)
go func(c chan string) { c <- "done" }(t.c1)
select {
case msg1 := <-t.c1:
println("received from c1:", msg1)
}
println("Bye")
}
// Output:
// received from c1: done
// Bye

22
_test/select6.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type T struct {
c1 chan string
}
func main() {
t := &T{}
t.c1 = make(chan string)
go func(c chan string) { c <- "done" }(t.c1)
select {
case <-t.c1:
println("received from c1")
}
println("Bye")
}
// Output:
// received from c1
// Bye

24
_test/select7.go Normal file
View File

@@ -0,0 +1,24 @@
package main
type T struct {
c1 chan string
}
func main() {
t := &T{}
t.c1 = make(chan string)
a := 0
go func() {
select {
case t.c1 <- "done":
a++
}
}()
msg1 := <-t.c1
println("received from c1:", msg1)
}
// Output:
// received from c1: done

24
_test/select8.go Normal file
View File

@@ -0,0 +1,24 @@
package main
type T struct {
c1 chan string
c2 chan string
}
func main() {
t := &T{}
t.c1 = make(chan string)
go func(c chan string) { c <- "done" }(t.c1)
select {
case msg := <-t.c1:
println("received from c1:", msg)
case <-t.c2:
}
println("Bye")
}
// Output:
// received from c1: done
// Bye

22
_test/select9.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type T struct {
c1 chan string
}
func main() {
t := &T{}
t.c1 = make(chan string)
go func() {
select {
case t.c1 <- "done":
}
}()
msg1 := <-t.c1
println("received from c1:", msg1)
}
// Output:
// received from c1: done

39
_test/struct32.go Normal file
View File

@@ -0,0 +1,39 @@
package main
type T0 struct {
name string
}
type lookupFunc func(s string) T0
type T1 struct {
name string
info lookupFunc
}
func (t T0) F1() bool { println("in F1"); return true }
type T2 struct {
t1 T1
}
func (t2 *T2) f() {
info := t2.t1.info("foo")
println(info.F1())
}
var t0 = T0{"t0"}
func main() {
t := &T2{T1{
"bar", func(s string) T0 { return t0 },
}}
println("hello")
println(t.t1.info("foo").F1())
}
// Output:
// hello
// in F1
// true

34
_test/struct33.go Normal file
View File

@@ -0,0 +1,34 @@
package main
type T0 struct {
name string
}
type lookupFunc func(s string) T0
type T1 struct {
name string
info lookupFunc
}
func (t T0) F1() bool { println("in F1"); return true }
var t0 = T0{"t0"}
func look(s string) T0 { println("in look"); return t0 }
var table = []*T1{{
name: "bar",
info: look,
},
}
func main() {
info := table[0].info
println(info("foo").F1())
}
// Output:
// in look
// in F1
// true

16
_test/struct34.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type T struct {
f func(*T)
}
func f1(t *T) { t.f = f2 }
func f2(t *T) { t.f = f1 }
func main() {
println("ok")
}
// Output:
// ok

16
_test/struct35.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type T struct {
f func(*T)
}
func f1(t *T) { t.f = f1 }
func main() {
t := &T{}
f1(t)
println(t.f != nil)
}
// Output:
// true

21
_test/struct36.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"net/http"
"strings"
)
type S struct {
http.Client
}
func main() {
var s S
if _, err := s.Get("url"); err != nil {
println(strings.Contains(err.Error(), "unsupported protocol scheme"))
}
return
}
// Output:
// true

17
_test/struct37.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import (
"net/http"
"strings"
)
type MyHttpClient struct {
*http.Client
}
func main() {
c := new(MyHttpClient)
c.Client = new(http.Client)
_, err := c.Get("url")
println(strings.Contains(err.Error(), "unsupported protocol scheme"))
}

24
_test/struct38.go Normal file
View File

@@ -0,0 +1,24 @@
package main
type T struct {
f func(t *T1)
y *xxx
}
type T1 struct {
T
}
type xxx struct{}
var (
x1 *T1 = x
x = &T1{}
)
func main() {
println("ok")
}
// Output:
// ok

22
_test/struct39.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type T struct {
t *T1
y *xxx
}
type T1 struct {
T
}
var x = &T1{}
var t = &T{}
type xxx struct{}
func main() {
println("ok")
}
// Output:
// ok

23
_test/struct40.go Normal file
View File

@@ -0,0 +1,23 @@
package main
type T struct {
t *T1
y *xxx
}
type T1 struct {
T
}
func f(t *T) { println("in f") }
var x = &T1{}
type xxx struct{}
func main() {
println("ok")
}
// Output:
// ok

26
_test/struct41.go Normal file
View File

@@ -0,0 +1,26 @@
package main
type Ti func(*T)
type T1 struct {
t Ti
}
type T struct {
t Ti
y *xxx
}
func f(t *T) { println("in f") }
type xxx struct{}
var x = &T1{t: f}
func main() {
x.t = f
println("ok")
}
// Output:
// ok

19
_test/struct42.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type T struct {
t func(*T)
y *xxx
}
func f(t *T) { println("in f") }
var x = &T{t: f}
type xxx struct{}
func main() {
println("ok")
}
// Output:
// ok

19
_test/struct43.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type T struct {
t func(*T)
y *xxx
}
func f(t *T) { println("in f") }
type xxx struct{}
func main() {
x := &T{}
x.t = f
println("ok")
}
// Output:
// ok

27
_test/struct44.go Normal file
View File

@@ -0,0 +1,27 @@
package main
type Ti func(*T) X
type T1 struct {
t Ti
}
type T struct {
t Ti
y *xxx
}
func f(t *T) X { println("in f"); return X{} }
type X struct{ Name string }
type xxx struct{}
var x = &T1{t: f}
func main() {
println("ok")
}
// Output:
// ok

17
_test/struct45.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type T struct {
b bool
}
type T1 struct {
T
}
func main() {
t := &T1{}
t.b = true
println(t.b)
}
// Output: true

15
_test/switch23.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func getType() string { return "T1" }
func main() {
switch getType() {
case "T1":
println("T1")
default:
println("default")
}
}
// Output:
// T1

16
_test/switch24.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func main() {
a := 3
switch a + 2 {
case 5:
println(5)
default:
println("default")
}
println("bye")
}
// Output:
// 5
// bye

18
_test/switch25.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
a := 2
switch {
case a == 1:
println(1)
case a == 2:
println(2)
default:
println("default")
}
println("bye")
}
// Output:
// 2
// bye

18
_test/switch26.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
a := 1
switch a := 2; {
case a == 1:
println(1)
case a == 2:
println(2)
default:
println("default")
}
println(a)
}
// Output:
// 2
// 1

16
_test/switch27.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func main() {
//a := false
switch false {
case true:
println("true")
case false:
println("false")
}
println("bye")
}
// Output:
// false
// bye

15
_test/switch28.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func main() {
switch {
case true:
println("true")
case false:
println("false")
}
println("bye")
}
// Output:
// true
// bye

14
_test/switch29.go Normal file
View File

@@ -0,0 +1,14 @@
package main
func main() {
a := 3
switch a {
case 3:
println("three")
}
println("bye")
}
// Output:
// three
// bye

16
_test/switch30.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func main() {
a := 3
switch a {
default:
//println("default")
case 3:
println("three")
}
println("bye")
}
// Output:
// three
// bye

10
_test/switch31.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func main() {
switch {
}
println("bye")
}
// Output:
// bye

11
_test/switch32.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func main() {
a := 1
switch a {
}
println("bye", a)
}
// Output:
// bye 1

11
_test/switch33.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func main() {
var a interface{}
switch a.(type) {
}
println("bye")
}
// Output:
// bye

20
_test/type18.go Normal file
View File

@@ -0,0 +1,20 @@
package main
type T struct {
name string
size int
}
var table = []*T{{
name: "foo",
size: 2,
}}
var s = table[0].size
func main() {
println(s)
}
// Output:
// 2

21
_test/type19.go Normal file
View File

@@ -0,0 +1,21 @@
package main
type T struct {
name string
size int
}
var table = map[int]*T{
0: {
name: "foo",
size: 2,
}}
var s = table[0].size
func main() {
println(s)
}
// Output:
// 2

18
_test/type20.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"io"
"strings"
)
func isCloser(r io.Reader) bool {
_, ok := r.(io.Closer)
return ok
}
func main() {
println(isCloser(strings.NewReader("test")))
}
// Output:
// false

16
_test/type21.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"reflect"
"time"
)
func main() {
t := time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
v := reflect.ValueOf(t.String)
f := v.Interface().(func() string)
println(f())
}
// Output:
// 2009-11-10 23:04:05 +0000 UTC

15
_test/type22.go Normal file
View File

@@ -0,0 +1,15 @@
package main
type T1 T
func foo() T1 {
return T1(T{"foo"})
}
type T struct {
Name string
}
func main() {
println(foo().Name)
}

View File

@@ -47,8 +47,10 @@ import (
func main() {
var interactive bool
var tags string
var cmd string
flag.BoolVar(&interactive, "i", false, "start an interactive REPL")
flag.StringVar(&tags, "tags", "", "set a list of build tags")
flag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
flag.Usage = func() {
fmt.Println("Usage:", os.Args[0], "[options] [script] [args]")
fmt.Println("Options:")
@@ -62,31 +64,41 @@ func main() {
i.Use(stdlib.Symbols)
i.Use(interp.Symbols)
if len(args) > 0 {
// Skip first os arg to set command line as expected by interpreted main
os.Args = os.Args[1:]
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
if cmd != `` {
i.REPL(strings.NewReader(cmd), os.Stderr)
}
b, err := ioutil.ReadFile(args[0])
if err != nil {
log.Fatal("Could not read file: ", args[0])
}
s := string(b)
if s[:2] == "#!" {
// Allow executable go scripts, but fix them prior to parse
s = strings.Replace(s, "#!", "//", 1)
}
i.Name = args[0]
if _, err := i.Eval(s); err != nil {
fmt.Println(err)
}
if interactive {
if len(args) == 0 {
if interactive || cmd == `` {
i.REPL(os.Stdin, os.Stdout)
}
} else {
return
}
// Skip first os arg to set command line as expected by interpreted main
os.Args = os.Args[1:]
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
b, err := ioutil.ReadFile(args[0])
if err != nil {
log.Fatal("Could not read file: ", args[0])
}
s := string(b)
if s[:2] == "#!" {
// Allow executable go scripts, but fix them prior to parse
s = strings.Replace(s, "#!", "//", 1)
}
i.Name = args[0]
if _, err := i.Eval(s); err != nil {
fmt.Println(err)
if p, ok := err.(interp.Panic); ok {
fmt.Println(string(p.Stack))
}
}
if interactive {
i.REPL(os.Stdin, os.Stdout)
}
}

View File

@@ -6,7 +6,6 @@ import (
"go/parser"
"go/scanner"
"go/token"
"math"
"reflect"
"strconv"
"sync/atomic"
@@ -196,11 +195,11 @@ const (
aAndNot
aAndNotAssign
aBitNot
aBranch
aCall
aCase
aCompositeLit
aDec
aDefer
aEqual
aGreater
aGreaterEqual
@@ -254,11 +253,11 @@ var actions = [...]string{
aAndNot: "&^",
aAndNotAssign: "&^=",
aBitNot: "^",
aBranch: "branch",
aCall: "call",
aCase: "case",
aCompositeLit: "compositeLit",
aDec: "--",
aDefer: "defer",
aEqual: "==",
aGreater: ">",
aGetFunc: "getFunc",
@@ -466,11 +465,7 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
n.rval = reflect.ValueOf(v)
case token.FLOAT:
v, _ := strconv.ParseFloat(a.Value, 64)
if math.Trunc(v) == v {
n.rval = reflect.ValueOf(int(v))
} else {
n.rval = reflect.ValueOf(v)
}
n.rval = reflect.ValueOf(v)
case token.IMAG:
v, _ := strconv.ParseFloat(a.Value[:len(a.Value)-1], 64)
n.rval = reflect.ValueOf(complex(0, v))
@@ -569,7 +564,7 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
st.push(addChild(&root, anc, pos, declStmt, aNop), nod)
case *ast.DeferStmt:
st.push(addChild(&root, anc, pos, deferStmt, aDefer), nod)
st.push(addChild(&root, anc, pos, deferStmt, aNop), nod)
case *ast.Ellipsis:
st.push(addChild(&root, anc, pos, ellipsisExpr, aNop), nod)

View File

@@ -10,7 +10,12 @@ import (
)
// A cfgError represents an error during CFG build stage
type cfgError error
type cfgError struct {
*node
error
}
func (c *cfgError) Error() string { return c.error.Error() }
var constOp = map[action]func(*node){
aAdd: addConst,
@@ -201,13 +206,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
sc = sc.pushBloc()
if n.child[0].action == aAssign {
ch := n.child[0].child[1].child[0]
if sym, _, ok := sc.lookup(ch.ident); ok {
assigned := n.child[0].child[0]
index := sc.add(sym.typ.val)
sc.sym[assigned.ident] = &symbol{index: index, kind: varSym, typ: sym.typ.val}
assigned.findex = index
assigned.typ = sym.typ.val
var typ *itype
if typ, err = nodeType(interp, sc, ch); err != nil {
return false
}
if !isChan(typ) {
err = n.cfgErrorf("invalid operation: receive from non-chan type")
return false
}
elem := chanElement(typ)
assigned := n.child[0].child[0]
index := sc.add(elem)
sc.sym[assigned.ident] = &symbol{index: index, kind: varSym, typ: elem}
assigned.findex = index
assigned.typ = elem
}
case compositeLitExpr:
@@ -312,7 +324,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
sc = sc.pushBloc()
case switchStmt, switchIfStmt, typeSwitch:
// Make sure default clause is in last position
// Make sure default clause is in last position.
c := n.lastChild().child
if i, l := getDefault(n), len(c)-1; i >= 0 && i != l {
c[i], c[l] = c[l], c[i]
@@ -459,7 +471,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
}
default:
// Detect invalid float truncate
// Detect invalid float truncate.
if isInt(t0) && isFloat(t1) {
err = src.cfgErrorf("invalid float truncate")
return
@@ -470,7 +482,8 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
// Propagate type
// TODO: Check that existing destination type matches source type
switch {
case n.action == aAssign && src.action == aCall:
case n.action == aAssign && src.action == aCall && dest.typ.cat != interfaceT:
// Call action may perform the assignment directly.
n.gen = nop
src.level = level
src.findex = dest.findex
@@ -478,7 +491,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
src.typ = dest.typ
}
case n.action == aAssign && src.action == aRecv:
// Assign by reading from a receiving channel
// Assign by reading from a receiving channel.
n.gen = nop
src.findex = dest.findex // Set recv address to LHS
dest.typ = src.typ
@@ -487,16 +500,16 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
src.findex = dest.findex
src.level = level
case src.kind == basicLit:
// TODO: perform constant folding and propagation here
// TODO: perform constant folding and propagation here.
switch {
case dest.typ.cat == interfaceT:
case isComplex(dest.typ.TypeOf()):
// value set in genValue
// Value set in genValue.
case !src.rval.IsValid():
// Assign to nil
// Assign to nil.
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
default:
// Convert literal value to destination type
// Convert literal value to destination type.
src.rval = src.rval.Convert(dest.typ.TypeOf())
src.typ = dest.typ
}
@@ -577,10 +590,15 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if !(isNumber(t0) && isNumber(t1)) {
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
}
case aRem, aAnd, aOr, aXor, aAndNot:
case aAnd, aOr, aXor, aAndNot:
if !(isInt(t0) && isInt(t1)) {
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
}
case aRem:
if !(c0.isInteger() && c1.isInteger()) {
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
}
n.typ = c0.typ
case aShl, aShr:
if !(c0.isInteger() && c1.isNatural()) {
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
@@ -641,7 +659,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
wireChild(n)
t := n.child[0].typ
switch t.cat {
case ptrT:
case aliasT, ptrT:
n.typ = t.val
if t.val.cat == valueT {
n.typ = &itype{cat: valueT, rtype: t.val.rtype.Elem()}
@@ -771,10 +789,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
case isBinCall(n):
n.gen = callBin
if typ := n.child[0].typ.rtype; typ.NumOut() > 0 {
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
n.findex = sc.add(n.typ)
for i := 1; i < typ.NumOut(); i++ {
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
if funcType := n.child[0].typ.val; funcType != nil {
// Use the original unwrapped function type, to allow future field and
// methods resolutions, otherwise impossible on the opaque bin type.
n.typ = funcType.ret[0]
n.findex = sc.add(n.typ)
for i := 1; i < len(funcType.ret); i++ {
sc.add(funcType.ret[i])
}
} else {
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
n.findex = sc.add(n.typ)
for i := 1; i < typ.NumOut(); i++ {
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
}
}
}
default:
@@ -815,7 +843,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
} else {
n.start = n.child[0].start // default clause
}
n.lastChild().tnext = n.anc.anc // exit node is SelectStmt
n.lastChild().tnext = n.anc.anc // exit node is selectStmt
sc = sc.pop()
case compositeLitExpr:
@@ -824,7 +852,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
n.findex = sc.add(n.typ)
}
// TODO: Check that composite literal expr matches corresponding type
n.gen = compositeGenerator(n)
n.gen = compositeGenerator(n, sc)
case fallthroughtStmt:
if n.anc.kind != caseBody {
@@ -847,10 +875,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = cond.start
cond.tnext = body.start
cond.fnext = n
body.tnext = cond.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
n.start = body.start
body.tnext = body.start
}
} else {
n.start = cond.start
cond.tnext = body.start
body.tnext = cond.start
}
setFNext(cond, n)
sc = sc.pop()
case forStmt2: // for init; cond; {}
@@ -859,10 +895,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = init.start
init.tnext = cond.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
init.tnext = body.start
body.tnext = body.start
} else {
init.tnext = n
}
} else {
init.tnext = cond.start
body.tnext = cond.start
}
cond.tnext = body.start
cond.fnext = n
body.tnext = cond.start
setFNext(cond, n)
sc = sc.pop()
case forStmt3: // for ; cond; post {}
@@ -870,14 +916,22 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = cond.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
n.start = body.start
post.tnext = body.start
}
} else {
n.start = cond.start
post.tnext = cond.start
}
cond.tnext = body.start
cond.fnext = n
setFNext(cond, n)
body.tnext = post.start
post.tnext = cond.start
sc = sc.pop()
case forStmt3a: // for int; ; post {}
case forStmt3a: // for init; ; post {}
init, post, body := n.child[0], n.child[1], n.child[2]
n.start = init.start
init.tnext = body.start
@@ -891,16 +945,26 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = init.start
init.tnext = cond.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
init.tnext = body.start
post.tnext = body.start
} else {
init.tnext = n
}
} else {
init.tnext = cond.start
post.tnext = cond.start
}
cond.tnext = body.start
cond.fnext = n
setFNext(cond, n)
body.tnext = post.start
post.tnext = cond.start
sc = sc.pop()
case forRangeStmt:
n.start = n.child[0].start
n.child[0].fnext = n
setFNext(n.child[0], n)
sc = sc.pop()
case funcDecl:
@@ -920,7 +984,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
sc = sc.pop()
err = genRun(n)
case goStmt:
case deferStmt, goStmt:
wireChild(n)
case identExpr:
@@ -951,12 +1015,6 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
err = n.cfgErrorf("use of builtin %s not in function call", n.ident)
}
}
if sym.kind == varSym && sym.typ != nil && sym.typ.TypeOf().Kind() == reflect.Bool {
switch n.anc.kind {
case ifStmt0, ifStmt1, ifStmt2, ifStmt3, forStmt1, forStmt2, forStmt3, forStmt4:
n.gen = branch
}
}
}
if n.sym != nil {
n.recv = n.sym.recv
@@ -970,9 +1028,16 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = cond.start
cond.tnext = tbody.start
cond.fnext = n
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
n.start = tbody.start
}
} else {
n.start = cond.start
cond.tnext = tbody.start
}
setFNext(cond, n)
tbody.tnext = n
sc = sc.pop()
@@ -981,9 +1046,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = cond.start
cond.tnext = tbody.start
cond.fnext = fbody.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test and the useless branch.
if cond.rval.Bool() {
n.start = tbody.start
} else {
n.start = fbody.start
}
} else {
n.start = cond.start
cond.tnext = tbody.start
setFNext(cond, fbody.start)
}
tbody.tnext = n
fbody.tnext = n
sc = sc.pop()
@@ -994,10 +1068,19 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = init.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
init.tnext = tbody.start
} else {
init.tnext = n
}
} else {
init.tnext = cond.start
cond.tnext = tbody.start
}
tbody.tnext = n
init.tnext = cond.start
cond.tnext = tbody.start
cond.fnext = n
setFNext(cond, n)
sc = sc.pop()
case ifStmt3: // if init; cond {} else {}
@@ -1006,9 +1089,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = init.start
init.tnext = cond.start
cond.tnext = tbody.start
cond.fnext = fbody.start
if cond.rval.IsValid() {
// Condition is known at compile time, bypass test.
if cond.rval.Bool() {
init.tnext = tbody.start
} else {
init.tnext = fbody.start
}
} else {
init.tnext = cond.start
cond.tnext = tbody.start
setFNext(cond, fbody.start)
}
tbody.tnext = n
fbody.tnext = n
sc = sc.pop()
@@ -1019,7 +1111,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
case landExpr:
n.start = n.child[0].start
n.child[0].tnext = n.child[1].start
n.child[0].fnext = n
setFNext(n.child[0], n)
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
@@ -1030,7 +1122,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
case lorExpr:
n.start = n.child[0].start
n.child[0].tnext = n
n.child[0].fnext = n.child[1].start
setFNext(n.child[0], n.child[1].start)
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
@@ -1198,11 +1290,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
n.recv = &receiver{node: n.child[0], index: lind}
}
} else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
if isPtr {
if isPtr && n.typ.fieldSeq(lind).cat != ptrT {
n.gen = getIndexSeqPtrMethod
} else {
n.gen = getIndexSeqMethod
}
n.recv = &receiver{node: n.child[0], index: lind}
n.val = append([]int{m.Index}, lind...)
n.typ = &itype{cat: valueT, rtype: m.Type}
} else if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
@@ -1219,7 +1312,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if n.typ.cat == funcT {
// function in a struct field is always wrapped in reflect.Value
rtype := n.typ.TypeOf()
n.typ = &itype{cat: valueT, rtype: rtype}
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
}
default:
n.gen = getIndexSeq
@@ -1227,7 +1320,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if n.typ.cat == funcT {
// function in a struct field is always wrapped in reflect.Value
rtype := n.typ.TypeOf()
n.typ = &itype{cat: valueT, rtype: rtype}
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
}
}
} else if s, lind, ok := n.typ.lookupBinField(n.child[1].ident); ok {
@@ -1245,9 +1338,40 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
case selectStmt:
wireChild(n)
// Move action to block statement, so select node can be an exit point
// Move action to block statement, so select node can be an exit point.
n.child[0].gen = _select
n.start = n.child[0]
// Chain channel init actions in commClauses prior to invoke select.
var cur *node
for _, c := range n.child[0].child {
var an *node // channel init action node
c0 := c.child[0]
switch {
case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv:
an = c0.child[0].child[0]
case c0.action == aAssign:
an = c0.lastChild().child[0]
case c0.kind == sendStmt:
an = c0.child[0]
}
if an != nil {
if cur == nil {
// First channel init action, the entry point for the select block.
n.start = an.start
} else {
// Chain channel init action to the previous one.
cur.tnext = an.start
}
}
cur = an
}
// Invoke select action
if cur == nil {
// There is no channel init action, call select directly.
n.start = n.child[0]
} else {
// Select is called after the last channel init action.
cur.tnext = n.child[0]
}
case starExpr:
switch {
@@ -1291,39 +1415,57 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
fallthrough
case switchStmt:
sc = sc.pop()
sbn := n.lastChild() // switch block node
clauses := sbn.child
l := len(clauses)
// Chain case clauses
if l == 0 {
// Switch is empty
break
}
// Chain case clauses.
for i, c := range clauses[:l-1] {
c.fnext = clauses[i+1] // chain to next clause
body := c.lastChild()
c.tnext = body.start
if len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
if n.kind == typeSwitch {
err = body.lastChild().cfgErrorf("cannot fallthrough in type switch")
}
body.tnext = clauses[i+1].lastChild().start
// Chain to next clause.
setFNext(c, clauses[i+1])
if len(c.child) == 0 {
c.tnext = n // Clause body is empty, exit.
} else {
body.tnext = n
body := c.lastChild()
c.tnext = body.start
if len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
if n.kind == typeSwitch {
err = body.lastChild().cfgErrorf("cannot fallthrough in type switch")
}
if len(clauses[i+1].child) == 0 {
body.tnext = n // Fallthrough to next with empty body, just exit.
} else {
body.tnext = clauses[i+1].lastChild().start
}
} else {
body.tnext = n // Exit switch at end of clause body.
}
}
}
c := clauses[l-1]
c.tnext = c.lastChild().start
if n.child[0].action == aAssign &&
(n.child[0].child[0].kind != typeAssertExpr || len(n.child[0].child[0].child) > 1) {
// switch init statement is defined
n.start = n.child[0].start
n.child[0].tnext = sbn.start
c := clauses[l-1] // Last clause.
if len(c.child) == 0 {
c.tnext = n // Clause body is empty, exit.
} else {
n.start = sbn.start
body := c.lastChild()
c.tnext = body.start
body.tnext = n
}
sc = sc.pop()
n.start = n.child[0].start
n.child[0].tnext = sbn.start
case switchIfStmt: // like an if-else chain
sc = sc.pop()
sbn := n.lastChild() // switch block node
clauses := sbn.child
l := len(clauses)
if l == 0 {
// Switch is empty
break
}
// Wire case clauses in reverse order so the next start node is already resolved when used.
for i := l - 1; i >= 0; i-- {
c := clauses[i]
@@ -1333,9 +1475,9 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
cond := c.child[0]
cond.tnext = body.start
if i == l-1 {
cond.fnext = n
setFNext(cond, n)
} else {
cond.fnext = clauses[i+1].start
setFNext(cond, clauses[i+1].start)
}
c.start = cond.start
} else {
@@ -1344,17 +1486,13 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
// If last case body statement is a fallthrough, then jump to next case body
if i < l-1 && len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
body.tnext = clauses[i+1].lastChild().start
} else {
body.tnext = n
}
}
sbn.start = clauses[0].start
if n.child[0].action == aAssign {
// switch init statement is defined
n.start = n.child[0].start
n.child[0].tnext = sbn.start
} else {
n.start = sbn.start
}
sc = sc.pop()
n.start = n.child[0].start
n.child[0].tnext = sbn.start
case typeAssertExpr:
if len(n.child) > 1 {
@@ -1365,7 +1503,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
}
}
if n.anc.action != aAssignX {
n.typ = n.child[1].typ
if n.child[0].typ.cat == valueT {
// Avoid special wrapping of interfaces and func types.
n.typ = &itype{cat: valueT, rtype: n.child[1].typ.TypeOf()}
} else {
n.typ = n.child[1].typ
}
n.findex = sc.add(n.typ)
}
} else {
@@ -1539,13 +1682,13 @@ func childPos(n *node) int {
return -1
}
func (n *node) cfgErrorf(format string, a ...interface{}) cfgError {
func (n *node) cfgErrorf(format string, a ...interface{}) *cfgError {
a = append([]interface{}{n.interp.fset.Position(n.pos)}, a...)
return cfgError(fmt.Errorf("%s: "+format, a...))
return &cfgError{n, fmt.Errorf("%s: "+format, a...)}
}
func genRun(nod *node) error {
var err cfgError
var err error
nod.Walk(func(n *node) bool {
if err != nil {
@@ -1568,11 +1711,31 @@ func genRun(nod *node) error {
return err
}
// Find default case clause index of a switch statement, if any
// setFnext sets the cond fnext field to next, propagates it for parenthesis blocks
// and sets the action to branch.
func setFNext(cond, next *node) {
if cond.action == aNop {
cond.action = aBranch
cond.gen = branch
cond.fnext = next
}
if cond.kind == parenExpr {
setFNext(cond.lastChild(), next)
return
}
cond.fnext = next
}
// GetDefault return the index of default case clause in a switch statement, or -1.
func getDefault(n *node) int {
for i, c := range n.lastChild().child {
if len(c.child) == 1 {
switch len(c.child) {
case 0:
return i
case 1:
if c.child[0].kind == caseBody {
return i
}
}
}
return -1
@@ -1735,6 +1898,10 @@ func isKey(n *node) bool {
(n.anc.kind == fieldExpr && len(n.anc.child) > 1 && n.anc.child[0] == n)
}
func isField(n *node) bool {
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
}
// isNewDefine returns true if node refers to a new definition
func isNewDefine(n *node, sc *scope) bool {
if n.ident == "_" {
@@ -1849,21 +2016,32 @@ func gotoLabel(s *symbol) {
}
}
func compositeGenerator(n *node) (gen bltnGenerator) {
func compositeGenerator(n *node, sc *scope) (gen bltnGenerator) {
switch n.typ.cat {
case aliasT, ptrT:
n.typ.val.untyped = n.typ.untyped
n.typ = n.typ.val
gen = compositeGenerator(n)
gen = compositeGenerator(n, sc)
case arrayT:
gen = arrayLit
case mapT:
gen = mapLit
case structT:
if len(n.child) > 0 && n.lastChild().kind == keyValueExpr {
gen = compositeSparse
} else {
gen = compositeLit
switch {
case len(n.child) == 0:
gen = compositeLitNotype
case n.lastChild().kind == keyValueExpr:
if n.child[0].isType(sc) {
gen = compositeSparse
} else {
gen = compositeSparseNotype
}
default:
if n.child[0].isType(sc) {
gen = compositeLit
} else {
gen = compositeLitNotype
}
}
case valueT:
switch k := n.typ.rtype.Kind(); k {
@@ -1875,7 +2053,7 @@ func compositeGenerator(n *node) (gen bltnGenerator) {
log.Panic(n.cfgErrorf("compositeGenerator not implemented for type kind: %s", k))
}
}
return
return gen
}
// arrayTypeLen returns the node's array length. If the expression is an

View File

@@ -1,8 +1,6 @@
package interp
import (
"reflect"
)
import "reflect"
// gta performs a global types analysis on the AST, registering types,
// variables and functions symbols at package level, prior to CFG.
@@ -23,7 +21,10 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
iotaValue = 0
// Early parse of constDecl subtree, to compute all constant
// values which may be necessary in further declarations.
_, err = interp.cfg(n, pkgID)
if _, err = interp.cfg(n, pkgID); err != nil {
// No error processing here, to allow recovery in subtree nodes.
err = nil
}
case blockStmt:
if n != root {
@@ -47,6 +48,14 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
for i := 0; i < n.nleft; i++ {
dest, src := n.child[i], n.child[sbase+i]
val := reflect.ValueOf(iotaValue)
if n.anc.kind == constDecl {
if _, err2 := interp.cfg(n, pkgID); err2 != nil {
// Constant value can not be computed yet.
// Come back when child dependencies are known.
revisit = append(revisit, n)
return false
}
}
typ := atyp
if typ == nil {
if typ, err = nodeType(interp, sc, src); err != nil {
@@ -54,7 +63,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
}
val = src.rval
}
if typ.incomplete {
if !typ.isComplete() {
// Come back when type is known.
revisit = append(revisit, n)
return false
@@ -64,7 +73,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
return false
}
if typ.isBinMethod {
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true}
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true, scope: sc}
}
if sc.sym[dest.ident] == nil {
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
@@ -85,7 +94,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
if n.typ, err = nodeType(interp, sc, n.child[l]); err != nil {
return false
}
if n.typ.incomplete {
if !n.typ.isComplete() {
// Come back when type is known.
revisit = append(revisit, n)
return false
@@ -130,7 +139,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
// Add a function symbol in the package name space
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
}
if n.typ.incomplete {
if !n.typ.isComplete() {
revisit = append(revisit, n)
}
return false
@@ -154,10 +163,10 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
if isBinType(v) {
typ = typ.Elem()
}
sc.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ}, rval: v}
sc.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ, scope: sc}, rval: v}
}
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, scope: sc}}
}
} else if err = interp.importSrc(rpath, ipath); err == nil {
sc.types = interp.universe.types
@@ -170,7 +179,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
}
}
default: // import symbols in package namespace
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath}}
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath, scope: sc}}
}
} else {
err = n.cfgErrorf("import %q error: %v", ipath, err)
@@ -183,7 +192,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
return false
}
if n.child[1].kind == identExpr {
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: rpath, field: typ.field, incomplete: typ.incomplete}
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: rpath, field: typ.field, incomplete: typ.incomplete, scope: sc, node: n.child[0]}
copy(n.typ.method, typ.method)
} else {
n.typ = typ
@@ -197,7 +206,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
n.typ.method = append(n.typ.method, sc.sym[typeName].typ.method...)
}
sc.sym[typeName].typ = n.typ
if n.typ.incomplete {
if !n.typ.isComplete() {
revisit = append(revisit, n)
}
return false
@@ -210,3 +219,42 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
}
return revisit, err
}
// gtaRetry (re)applies gta until all global constants and types are defined.
func (interp *Interpreter) gtaRetry(nodes []*node, rpath, pkgID string) error {
revisit := []*node{}
for {
for _, n := range nodes {
list, err := interp.gta(n, rpath, pkgID)
if err != nil {
return err
}
revisit = append(revisit, list...)
}
if len(revisit) == 0 || equalNodes(nodes, revisit) {
break
}
nodes = revisit
revisit = []*node{}
}
if len(revisit) > 0 {
return revisit[0].cfgErrorf("constant definition loop")
}
return nil
}
// equalNodes returns true if two slices of nodes are identical.
func equalNodes(a, b []*node) bool {
if len(a) != len(b) {
return false
}
for i, n := range a {
if n != b[i] {
return false
}
}
return true
}

View File

@@ -11,6 +11,8 @@ import (
"os"
"os/signal"
"reflect"
"runtime"
"runtime/debug"
"strconv"
"sync"
"sync/atomic"
@@ -158,6 +160,24 @@ type _error struct {
func (w _error) Error() string { return w.WError() }
// Panic is an error recovered from a panic call in interpreted code.
type Panic struct {
// Value is the recovered value of a call to panic.
Value interface{}
// Callers is the call stack obtained from the recover call.
// It may be used as the parameter to runtime.CallersFrames.
Callers []uintptr
// Stack is the call stack buffer for debug.
Stack []byte
}
// TODO: Capture interpreter stack frames also and remove
// fmt.Println(n.cfgErrorf("panic")) in runCfg.
func (e Panic) Error() string { return fmt.Sprint(e.Value) }
// Walk traverses AST n in depth first order, call cbin function
// at node entry and cbout function at node exit.
func (n *node) Walk(in func(n *node) bool, out func(n *node)) {
@@ -291,10 +311,17 @@ func (interp *Interpreter) main() *node {
// Eval evaluates Go code represented as a string. It returns a map on
// current interpreted package exported symbols
func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
var res reflect.Value
func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
defer func() {
r := recover()
if r != nil {
var pc [64]uintptr // 64 frames should be enough.
n := runtime.Callers(1, pc[:])
err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()}
}
}()
// Parse source to AST
// Parse source to AST.
pkgName, root, err := interp.ast(src, interp.Name)
if err != nil || root == nil {
return res, err
@@ -307,16 +334,10 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
}
}
// Global type analysis
revisit, err := interp.gta(root, pkgName, interp.Name)
if err != nil {
// Perform global types analysis.
if err = interp.gtaRetry([]*node{root}, pkgName, interp.Name); err != nil {
return res, err
}
for _, n := range revisit {
if _, err = interp.gta(n, pkgName, interp.Name); err != nil {
return res, err
}
}
// Annotate AST with CFG infos
initNodes, err := interp.cfg(root, interp.Name)
@@ -446,12 +467,15 @@ func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
v, err := interp.EvalWithContext(ctx, src)
signal.Reset()
if err != nil {
switch err.(type) {
switch e := err.(type) {
case scanner.ErrorList:
// Early failure in the scanner: the source is incomplete
// and no AST could be produced, neither compiled / run.
// Get one more line, and retry
continue
case Panic:
fmt.Fprintln(out, e.Value)
fmt.Fprintln(out, string(e.Stack))
default:
fmt.Fprintln(out, err)
}

View File

@@ -33,6 +33,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
for _, file := range files {
if filepath.Ext(file.Name()) != ".go" ||
file.Name() == "bad0.go" || // expect error
file.Name() == "const9.go" || // expect error
file.Name() == "export1.go" || // non-main package
file.Name() == "export0.go" || // non-main package
file.Name() == "for7.go" || // expect error
@@ -138,6 +139,11 @@ func TestInterpErrorConsistency(t *testing.T) {
expectedInterp: "1:1: expected 'package', found println",
expectedExec: "1:1: expected 'package', found println",
},
{
fileName: "const9.go",
expectedInterp: "5:2: constant definition loop",
expectedExec: "5:2: constant definition loop",
},
{
fileName: "if2.go",
expectedInterp: "7:5: non-bool used as if condition",

View File

@@ -525,7 +525,11 @@ func eval(t *testing.T, i *interp.Interpreter, src string) reflect.Value {
t.Helper()
res, err := i.Eval(src)
if err != nil {
t.Fatal(err)
t.Logf("Error: %v", err)
if e, ok := err.(interp.Panic); ok {
t.Logf(string(e.Stack))
}
t.FailNow()
}
return res
}
@@ -541,7 +545,11 @@ func assertEval(t *testing.T, i *interp.Interpreter, src, expectedError, expecte
}
if err != nil {
t.Fatalf("got an error %v", err)
t.Logf("got an error: %v", err)
if e, ok := err.(interp.Panic); ok {
t.Logf(string(e.Stack))
}
t.FailNow()
}
if fmt.Sprintf("%v", res) != expectedRes {

View File

@@ -29,7 +29,6 @@ var builtin = [...]bltnGenerator{
aCase: _case,
aCompositeLit: arrayLit,
aDec: dec,
aDefer: _defer,
aEqual: equal,
aGetFunc: getFunc,
aGreater: greater,
@@ -122,30 +121,33 @@ func runCfg(n *node, f *frame) {
}
func typeAssertStatus(n *node) {
value := genValue(n.child[0]) // input value
c0, c1 := n.child[0], n.child[1]
value := genValue(c0) // input value
value1 := genValue(n.anc.child[1]) // returned status
typ := c1.typ.rtype // type to assert
next := getExec(n.tnext)
switch {
case n.child[0].typ.cat == valueT:
case c0.typ.cat == valueT:
n.exec = func(f *frame) bltn {
if !value(f).IsValid() || value(f).IsNil() {
v := value(f)
if !v.IsValid() || v.IsNil() {
value1(f).SetBool(false)
}
value1(f).SetBool(true)
value1(f).SetBool(v.Type().Implements(typ))
return next
}
case n.child[1].typ.cat == interfaceT:
case c1.typ.cat == interfaceT:
n.exec = func(f *frame) bltn {
_, ok := value(f).Interface().(valueInterface)
//value0(f).Set(reflect.ValueOf(valueInterface{v.node, v.value}))
// TODO: verify that value(f) implements asserted type.
value1(f).SetBool(ok)
return next
}
default:
n.exec = func(f *frame) bltn {
_, ok := value(f).Interface().(valueInterface)
//value0(f).Set(v.value)
// TODO: verify that value(f) implements asserted type.
value1(f).SetBool(ok)
return next
}
@@ -153,25 +155,29 @@ func typeAssertStatus(n *node) {
}
func typeAssert(n *node) {
value := genValue(n.child[0]) // input value
dest := genValue(n) // returned result
c0, c1 := n.child[0], n.child[1]
value := genValue(c0) // input value
dest := genValue(n) // returned result
next := getExec(n.tnext)
switch {
case n.child[0].typ.cat == valueT:
case c0.typ.cat == valueT:
n.exec = func(f *frame) bltn {
dest(f).Set(value(f).Elem())
v := value(f)
dest(f).Set(v.Elem())
return next
}
case n.child[1].typ.cat == interfaceT:
case c1.typ.cat == interfaceT:
n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface)
// TODO: verify that value(f) implements asserted type.
dest(f).Set(reflect.ValueOf(valueInterface{v.node, v.value}))
return next
}
default:
n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface)
// TODO: verify that value(f) implements asserted type.
dest(f).Set(v.value)
return next
}
@@ -196,6 +202,7 @@ func typeAssert2(n *node) {
case n.child[1].typ.cat == interfaceT:
n.exec = func(f *frame) bltn {
v, ok := value(f).Interface().(valueInterface)
// TODO: verify that value(f) implements asserted type.
value0(f).Set(reflect.ValueOf(valueInterface{v.node, v.value}))
value1(f).SetBool(ok)
return next
@@ -203,6 +210,7 @@ func typeAssert2(n *node) {
default:
n.exec = func(f *frame) bltn {
v, ok := value(f).Interface().(valueInterface)
// TODO: verify that value(f) implements asserted type.
value0(f).Set(v.value)
value1(f).SetBool(ok)
return next
@@ -264,7 +272,9 @@ func assign(n *node) {
svalue[i] = genValueInterface(src)
case (dest.typ.cat == valueT || dest.typ.cat == errorT) && dest.typ.rtype.Kind() == reflect.Interface:
svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
case dest.typ.cat == valueT && src.typ.cat == funcT:
case src.typ.cat == funcT && dest.typ.cat == valueT:
svalue[i] = genFunctionWrapper(src)
case src.typ.cat == funcT && isField(dest):
svalue[i] = genFunctionWrapper(src)
case dest.typ.cat == funcT && src.typ.cat == valueT:
svalue[i] = genValueNode(src)
@@ -481,7 +491,6 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
if def, ok = n.val.(*node); !ok {
return genValueAsFunctionWrapper(n)
}
setExec(def.child[3].start)
start := def.child[3].start
numRet := len(def.typ.ret)
var rcvr func(*frame) reflect.Value
@@ -493,12 +502,13 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
rcvr = genValueRecv(n)
}
}
funcType := n.typ.TypeOf()
return func(f *frame) reflect.Value {
if n.frame != nil { // Use closure context if defined
f = n.frame
}
return reflect.MakeFunc(n.typ.TypeOf(), func(in []reflect.Value) []reflect.Value {
return reflect.MakeFunc(funcType, func(in []reflect.Value) []reflect.Value {
// Allocate and init local frame. All values to be settable and addressable.
fr := newFrame(f, len(def.types), f.runid())
d := fr.data
@@ -571,17 +581,22 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
return func(f *frame) reflect.Value {
v := value(f)
vv := v
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
if v.IsNil() {
return reflect.New(typ).Elem()
}
if v.Kind() == reflect.Ptr {
vv = v.Elem()
}
}
w := reflect.New(wrap).Elem()
for i, m := range methods {
if m == nil {
if r := v.FieldByIndex(indexes[i]).MethodByName(names[i]); r.IsValid() {
w.Field(i).Set(v.FieldByIndex(indexes[i]).MethodByName(names[i]))
o := vv.FieldByIndex(indexes[i])
if r := o.MethodByName(names[i]); r.IsValid() {
w.Field(i).Set(o.MethodByName(names[i]))
} else {
log.Println(n.cfgErrorf("genInterfaceWrapper error, no method %s", names[i]))
}
@@ -595,47 +610,6 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
}
}
func _defer(n *node) {
tnext := getExec(n.tnext)
values := make([]func(*frame) reflect.Value, len(n.child[0].child))
var method func(*frame) reflect.Value
for i, c := range n.child[0].child {
if c.typ.cat == funcT {
values[i] = genFunctionWrapper(c)
} else {
if c.recv != nil {
// defer a method on a binary obj
mi := c.val.(int)
m := genValue(c.child[0])
method = func(f *frame) reflect.Value { return m(f).Method(mi) }
}
values[i] = genValue(c)
}
}
if method != nil {
n.exec = func(f *frame) bltn {
val := make([]reflect.Value, len(values))
val[0] = method(f)
for i, v := range values[1:] {
val[i+1] = v(f)
}
f.deferred = append([][]reflect.Value{val}, f.deferred...)
return tnext
}
} else {
n.exec = func(f *frame) bltn {
val := make([]reflect.Value, len(values))
for i, v := range values {
val[i] = v(f)
}
f.deferred = append([][]reflect.Value{val}, f.deferred...)
return tnext
}
}
}
func call(n *node) {
goroutine := n.anc.kind == goStmt
var method bool
@@ -707,20 +681,44 @@ func call(n *node) {
}
}
if n.anc.kind == deferStmt {
// Store function call in frame for deferred execution.
value = genFunctionWrapper(n.child[0])
if method {
// The receiver is already passed in the function wrapper, skip it.
values = values[1:]
}
n.exec = func(f *frame) bltn {
val := make([]reflect.Value, len(values)+1)
val[0] = value(f)
for i, v := range values {
val[i+1] = v(f)
}
f.deferred = append([][]reflect.Value{val}, f.deferred...)
return tnext
}
return
}
n.exec = func(f *frame) bltn {
def := value(f).Interface().(*node)
var def *node
var ok bool
bf := value(f)
if def, ok = bf.Interface().(*node); ok {
bf = def.rval
}
// Call bin func if defined
if def.rval.IsValid() {
if bf.IsValid() {
in := make([]reflect.Value, len(values))
for i, v := range values {
in[i] = v(f)
}
if goroutine {
go def.rval.Call(in)
go bf.Call(in)
return tnext
}
out := def.rval.Call(in)
out := bf.Call(in)
for i, v := range rvalues {
if v != nil {
v(f).Set(out[i])
@@ -764,39 +762,40 @@ func call(n *node) {
}
// Copy input parameters from caller
dest := nf.data[numRet:]
for i, v := range values {
switch {
case method && i == 0:
// compute receiver
var src reflect.Value
if v == nil {
src = def.recv.val
if len(def.recv.index) > 0 {
if src.Kind() == reflect.Ptr {
src = src.Elem().FieldByIndex(def.recv.index)
} else {
src = src.FieldByIndex(def.recv.index)
if dest := nf.data[numRet:]; len(dest) > 0 {
for i, v := range values {
switch {
case method && i == 0:
// compute receiver
var src reflect.Value
if v == nil {
src = def.recv.val
if len(def.recv.index) > 0 {
if src.Kind() == reflect.Ptr {
src = src.Elem().FieldByIndex(def.recv.index)
} else {
src = src.FieldByIndex(def.recv.index)
}
}
}
} else {
src = v(f)
}
// Accommodate to receiver type
d := dest[0]
if ks, kd := src.Kind(), d.Kind(); ks != kd {
if kd == reflect.Ptr {
d.Set(src.Addr())
} else {
d.Set(src.Elem())
src = v(f)
}
} else {
d.Set(src)
// Accommodate to receiver type
d := dest[0]
if ks, kd := src.Kind(), d.Kind(); ks != kd {
if kd == reflect.Ptr {
d.Set(src.Addr())
} else {
d.Set(src.Elem())
}
} else {
d.Set(src)
}
case variadic >= 0 && i >= variadic:
vararg.Set(reflect.Append(vararg, v(f)))
default:
dest[i].Set(v(f))
}
case variadic >= 0 && i >= variadic:
vararg.Set(reflect.Append(vararg, v(f)))
default:
dest[i].Set(v(f))
}
}
@@ -823,7 +822,7 @@ func pindex(i, variadic int) int {
return variadic
}
// Call a function from a bin import, accessible through reflect
// Callbin calls a function from a bin import, accessible through reflect.
func callBin(n *node) {
tnext := getExec(n.tnext)
fnext := getExec(n.fnext)
@@ -835,9 +834,9 @@ func callBin(n *node) {
if funcType.IsVariadic() {
variadic = funcType.NumIn() - 1
}
// method signature obtained from reflect.Type include receiver as 1st arg, except for interface types
// A method signature obtained from reflect.Type includes receiver as 1st arg, except for interface types.
rcvrOffset := 0
if recv := n.child[0].recv; recv != nil && recv.node.typ.TypeOf().Kind() != reflect.Interface {
if recv := n.child[0].recv; recv != nil && !isInterface(recv.node.typ) {
if funcType.NumIn() > len(child) {
rcvrOffset = 1
}
@@ -886,8 +885,19 @@ func callBin(n *node) {
l := len(values)
switch {
case n.anc.kind == deferStmt:
// Store function call in frame for deferred execution.
n.exec = func(f *frame) bltn {
val := make([]reflect.Value, l+1)
val[0] = value(f)
for i, v := range values {
val[i+1] = v(f)
}
f.deferred = append([][]reflect.Value{val}, f.deferred...)
return tnext
}
case n.anc.kind == goStmt:
// Execute function in a goroutine, discard results
// Execute function in a goroutine, discard results.
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
@@ -897,7 +907,7 @@ func callBin(n *node) {
return tnext
}
case fnext != nil:
// Handle branching according to boolean result
// Handle branching according to boolean result.
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
@@ -910,8 +920,10 @@ func callBin(n *node) {
return fnext
}
default:
switch n.anc.kind {
case defineStmt, assignStmt, defineXStmt, assignXStmt:
switch n.anc.action {
case aAssign, aAssignX:
// The function call is part of an assign expression, we write results direcly
// to assigned location, to avoid an additional assign operation.
rvalues := make([]func(*frame) reflect.Value, funcType.NumOut())
for i := range rvalues {
c := n.anc.child[i]
@@ -1020,17 +1032,21 @@ func getIndexArray(n *node) {
}
}
// valueInterfaceType is the reflection type of valueInterface
var valueInterfaceType = reflect.TypeOf((*valueInterface)(nil)).Elem()
// getIndexMap retrieves map value from index
func getIndexMap(n *node) {
dest := genValue(n)
value0 := genValue(n.child[0]) // map
tnext := getExec(n.tnext)
z := reflect.New(n.child[0].typ.TypeOf().Elem()).Elem()
z := reflect.New(n.child[0].typ.frameType().Elem()).Elem()
if n.child[1].rval.IsValid() { // constant map index
mi := n.child[1].rval
if n.fnext != nil {
switch {
case n.fnext != nil:
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if v := value0(f).MapIndex(mi); v.IsValid() && v.Bool() {
@@ -1040,7 +1056,21 @@ func getIndexMap(n *node) {
dest(f).Set(z)
return fnext
}
} else {
case n.typ.cat == interfaceT:
z = reflect.New(n.child[0].typ.val.frameType()).Elem()
n.exec = func(f *frame) bltn {
if v := value0(f).MapIndex(mi); v.IsValid() {
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
dest(f).Set(e)
} else {
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
}
} else {
dest(f).Set(z)
}
return tnext
}
default:
n.exec = func(f *frame) bltn {
if v := value0(f).MapIndex(mi); v.IsValid() {
dest(f).Set(v)
@@ -1053,7 +1083,8 @@ func getIndexMap(n *node) {
} else {
value1 := genValue(n.child[1]) // map index
if n.fnext != nil {
switch {
case n.fnext != nil:
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if v := value0(f).MapIndex(value1(f)); v.IsValid() && v.Bool() {
@@ -1063,7 +1094,21 @@ func getIndexMap(n *node) {
dest(f).Set(z)
return fnext
}
} else {
case n.typ.cat == interfaceT:
z = reflect.New(n.child[0].typ.val.frameType()).Elem()
n.exec = func(f *frame) bltn {
if v := value0(f).MapIndex(value1(f)); v.IsValid() {
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
dest(f).Set(e)
} else {
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
}
} else {
dest(f).Set(z)
}
return tnext
}
default:
n.exec = func(f *frame) bltn {
if v := value0(f).MapIndex(value1(f)); v.IsValid() {
dest(f).Set(v)
@@ -1082,26 +1127,85 @@ func getIndexMap2(n *node) {
value0 := genValue(n.child[0]) // map
value2 := genValue(n.anc.child[1]) // status
next := getExec(n.tnext)
typ := n.anc.child[0].typ
doValue := n.anc.child[0].ident != "_"
doStatus := n.anc.child[1].ident != "_"
if !doValue && !doStatus {
nop(n)
return
}
if n.child[1].rval.IsValid() { // constant map index
mi := n.child[1].rval
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(mi)
if v.IsValid() {
dest(f).Set(v)
switch {
case !doValue:
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(mi)
value2(f).SetBool(v.IsValid())
return next
}
case typ.cat == interfaceT:
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(mi)
if v.IsValid() {
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
dest(f).Set(e)
} else {
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
}
}
if doStatus {
value2(f).SetBool(v.IsValid())
}
return next
}
default:
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(mi)
if v.IsValid() {
dest(f).Set(v)
}
if doStatus {
value2(f).SetBool(v.IsValid())
}
return next
}
value2(f).SetBool(v.IsValid())
return next
}
} else {
value1 := genValue(n.child[1]) // map index
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(value1(f))
if v.IsValid() {
dest(f).Set(v)
switch {
case !doValue:
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(value1(f))
value2(f).SetBool(v.IsValid())
return next
}
case typ.cat == interfaceT:
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(value1(f))
if v.IsValid() {
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
dest(f).Set(e)
} else {
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
}
}
if doStatus {
value2(f).SetBool(v.IsValid())
}
return next
}
default:
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(value1(f))
if v.IsValid() {
dest(f).Set(v)
}
if doStatus {
value2(f).SetBool(v.IsValid())
}
return next
}
value2(f).SetBool(v.IsValid())
return next
}
}
}
@@ -1496,11 +1600,19 @@ func arrayLit(n *node) {
for i, c := range child {
if c.kind == keyValueExpr {
convertLiteralValue(c.child[1], rtype)
values[i] = genValue(c.child[1])
if n.typ.val.cat == interfaceT {
values[i] = genValueInterface(c.child[1])
} else {
values[i] = genValue(c.child[1])
}
index[i] = int(vInt(c.child[0].rval))
} else {
convertLiteralValue(c, rtype)
values[i] = genValue(c)
if n.typ.val.cat == interfaceT {
values[i] = genValueInterface(c)
} else {
values[i] = genValue(c)
}
index[i] = prev
}
prev = index[i] + 1
@@ -1509,14 +1621,14 @@ func arrayLit(n *node) {
}
}
var a reflect.Value
if n.typ.sizedef {
a, _ = n.typ.zero()
} else {
a = reflect.MakeSlice(n.typ.TypeOf(), max, max)
}
typ := n.typ.frameType()
n.exec = func(f *frame) bltn {
var a reflect.Value
if n.typ.sizedef {
a, _ = n.typ.zero()
} else {
a = reflect.MakeSlice(typ, max, max)
}
for i, v := range values {
a.Index(index[i]).Set(v(f))
}
@@ -1538,8 +1650,16 @@ func mapLit(n *node) {
for i, c := range child {
convertLiteralValue(c.child[0], n.typ.key.TypeOf())
convertLiteralValue(c.child[1], n.typ.val.TypeOf())
keys[i] = genValue(c.child[0])
values[i] = genValue(c.child[1])
if n.typ.key.cat == interfaceT {
keys[i] = genValueInterface(c.child[0])
} else {
keys[i] = genValue(c.child[0])
}
if n.typ.val.cat == interfaceT {
values[i] = genValueInterface(c.child[1])
} else {
values[i] = genValue(c.child[1])
}
}
n.exec = func(f *frame) bltn {
@@ -1628,12 +1748,12 @@ func destType(n *node) *itype {
}
}
// compositeLit creates and populates a struct object
func compositeLit(n *node) {
// doCompositeLit creates and populates a struct object
func doCompositeLit(n *node, hasType bool) {
value := valueGenerator(n, n.findex)
next := getExec(n.tnext)
child := n.child
if !n.typ.untyped {
if hasType {
child = n.child[1:]
}
destInterface := destType(n).cat == interfaceT
@@ -1648,6 +1768,7 @@ func compositeLit(n *node) {
}
}
i := n.findex
n.exec = func(f *frame) bltn {
a := reflect.New(n.typ.TypeOf()).Elem()
for i, v := range values {
@@ -1659,18 +1780,21 @@ func compositeLit(n *node) {
case destInterface:
d.Set(reflect.ValueOf(valueInterface{n, a}))
default:
d.Set(a)
f.data[i] = a
}
return next
}
}
// compositeSparse creates a struct Object, filling fields from sparse key-values
func compositeSparse(n *node) {
func compositeLit(n *node) { doCompositeLit(n, true) }
func compositeLitNotype(n *node) { doCompositeLit(n, false) }
// doCompositeSparse creates a struct Object, filling fields from sparse key-values
func doCompositeSparse(n *node, hasType bool) {
value := valueGenerator(n, n.findex)
next := getExec(n.tnext)
child := n.child
if !n.typ.untyped {
if hasType {
child = n.child[1:]
}
@@ -1700,6 +1824,9 @@ func compositeSparse(n *node) {
}
}
func compositeSparse(n *node) { doCompositeSparse(n, true) }
func compositeSparseNotype(n *node) { doCompositeSparse(n, false) }
func empty(n *node) {}
var rat = reflect.ValueOf((*[]rune)(nil)).Type().Elem() // runes array type
@@ -1785,14 +1912,30 @@ func rangeMap(n *node) {
if len(n.child) == 4 {
index1 := n.child[1].findex // map value location in frame
value = genValue(n.child[2]) // map
n.exec = func(f *frame) bltn {
iter := f.data[index2].Interface().(*reflect.MapIter)
if !iter.Next() {
return fnext
if n.child[1].typ.cat == interfaceT {
n.exec = func(f *frame) bltn {
iter := f.data[index2].Interface().(*reflect.MapIter)
if !iter.Next() {
return fnext
}
f.data[index0].Set(iter.Key())
if e := iter.Value().Elem(); e.Type().AssignableTo(valueInterfaceType) {
f.data[index1].Set(e)
} else {
f.data[index1].Set(reflect.ValueOf(valueInterface{n, e}))
}
return tnext
}
} else {
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
}
f.data[index0].Set(iter.Key())
f.data[index1].Set(iter.Value())
return tnext
}
} else {
value = genValue(n.child[1]) // map
@@ -1960,6 +2103,8 @@ func _append(n *node) {
values := make([]func(*frame) reflect.Value, l)
for i, arg := range args {
switch {
case n.typ.val.cat == interfaceT:
values[i] = genValueInterface(arg)
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
values[i] = genValueInterfacePtr(arg)
case arg.typ.untyped:
@@ -1980,6 +2125,8 @@ func _append(n *node) {
} else {
var value0 func(*frame) reflect.Value
switch {
case n.typ.val.cat == interfaceT:
value0 = genValueInterface(n.child[2])
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
value0 = genValueInterfacePtr(n.child[2])
case n.child[2].typ.untyped:
@@ -2113,7 +2260,7 @@ func _new(n *node) {
func _make(n *node) {
dest := genValue(n)
next := getExec(n.tnext)
typ := n.child[1].typ.TypeOf()
typ := n.child[1].typ.frameType()
switch typ.Kind() {
case reflect.Array, reflect.Slice:
@@ -2306,14 +2453,16 @@ func recv2(n *node) {
}
func convertLiteralValue(n *node, t reflect.Type) {
// Skip non-constant values, undefined target type or interface target type.
if !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface {
return
}
if n.rval.IsValid() {
switch {
case n.typ.cat == nilT:
// Create a zero value of target type.
n.rval = reflect.New(t).Elem()
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface:
// Skip non-constant values, undefined target type or interface target type.
case n.rval.IsValid():
// Convert constant value to target type.
n.rval = n.rval.Convert(t)
} else {
default:
// Create a zero value of target type.
n.rval = reflect.New(t).Elem()
}
@@ -2391,7 +2540,9 @@ func _select(n *node) {
cases := make([]reflect.SelectCase, nbClause+1)
for i := 0; i < nbClause; i++ {
if len(n.child[i].child) > 1 {
switch c0 := n.child[i].child[0]; {
case len(n.child[i].child) > 1:
// The comm clause contains a channel operation and a clause body.
clause[i] = getExec(n.child[i].child[1].start)
chans[i], assigned[i], ok[i], cases[i].Dir = clauseChanDir(n.child[i])
chanValues[i] = genValue(chans[i])
@@ -2401,8 +2552,18 @@ func _select(n *node) {
if ok[i] != nil {
okValues[i] = genValue(ok[i])
}
} else {
clause[i] = getExec(n.child[i].child[0].start)
case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv:
// The comm clause has an empty body clause after channel receive.
chanValues[i] = genValue(c0.child[0].child[0])
cases[i].Dir = reflect.SelectRecv
case c0.kind == sendStmt:
// The comm clause as an empty body clause after channel send.
chanValues[i] = genValue(c0.child[0])
cases[i].Dir = reflect.SelectSend
assignedValues[i] = genValue(c0.child[1])
default:
// The comm clause has a default clause.
clause[i] = getExec(c0.start)
cases[i].Dir = reflect.SelectDefault
}
}
@@ -2501,56 +2662,94 @@ func slice0(n *node) {
func isNil(n *node) {
var value func(*frame) reflect.Value
if n.child[0].typ.cat == funcT {
value = genValueAsFunctionWrapper(n.child[0])
c0 := n.child[0]
if c0.typ.cat == funcT {
value = genValueAsFunctionWrapper(c0)
} else {
value = genValue(n.child[0])
value = genValue(c0)
}
tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(true)
return tnext
if c0.typ.cat == interfaceT {
n.exec = func(f *frame) bltn {
if (value(f).Interface().(valueInterface) == valueInterface{}) {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
dest(f).SetBool(value(f).IsNil())
return tnext
if c0.typ.cat == interfaceT {
n.exec = func(f *frame) bltn {
dest(f).SetBool(value(f).Interface().(valueInterface) == valueInterface{})
return tnext
}
} else {
n.exec = func(f *frame) bltn {
dest(f).SetBool(value(f).IsNil())
return tnext
}
}
}
}
func isNotNil(n *node) {
var value func(*frame) reflect.Value
if n.child[0].typ.cat == funcT {
value = genValueAsFunctionWrapper(n.child[0])
c0 := n.child[0]
if c0.typ.cat == funcT {
value = genValueAsFunctionWrapper(c0)
} else {
value = genValue(n.child[0])
value = genValue(c0)
}
tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(false)
return fnext
if c0.typ.cat == interfaceT {
n.exec = func(f *frame) bltn {
if (value(f).Interface().(valueInterface) == valueInterface{}) {
dest(f).SetBool(false)
return fnext
}
dest(f).SetBool(true)
return tnext
}
} else {
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(false)
return fnext
}
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(true)
return tnext
}
} else {
n.exec = func(f *frame) bltn {
dest(f).SetBool(!value(f).IsNil())
return tnext
if c0.typ.cat == interfaceT {
n.exec = func(f *frame) bltn {
dest(f).SetBool(!(value(f).Interface().(valueInterface) == valueInterface{}))
return tnext
}
} else {
n.exec = func(f *frame) bltn {
dest(f).SetBool(!value(f).IsNil())
return tnext
}
}
}
}

View File

@@ -47,7 +47,7 @@ func (interp *Interpreter) importSrc(rPath, path string) error {
var root *node
var pkgName string
// Parse source files
// Parse source files.
for _, file := range files {
name := file.Name()
if skipFile(&interp.context, name) {
@@ -86,12 +86,10 @@ func (interp *Interpreter) importSrc(rPath, path string) error {
revisit[subRPath] = append(revisit[subRPath], list...)
}
// revisit incomplete nodes where GTA could not complete
// Revisit incomplete nodes where GTA could not complete.
for pkg, nodes := range revisit {
for _, n := range nodes {
if _, err = interp.gta(n, pkg, path); err != nil {
return err
}
if err = interp.gtaRetry(nodes, pkg, path); err != nil {
return err
}
}

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