Compare commits

..

17 Commits

Author SHA1 Message Date
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
51 changed files with 1348 additions and 147 deletions

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

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

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

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

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/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

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

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

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

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

View File

@@ -206,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:
@@ -317,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]
@@ -831,7 +838,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:
@@ -863,10 +870,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
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
}
cond.fnext = n
body.tnext = cond.start
sc = sc.pop()
case forStmt2: // for init; cond; {}
@@ -875,10 +890,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
sc = sc.pop()
case forStmt3: // for ; cond; post {}
@@ -886,14 +911,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
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
@@ -907,11 +940,21 @@ 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
body.tnext = post.start
post.tnext = cond.start
sc = sc.pop()
case forRangeStmt:
@@ -968,10 +1011,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
}
}
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
}
fixBranch(n)
}
}
if n.sym != nil {
@@ -986,8 +1026,15 @@ 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
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
}
cond.fnext = n
tbody.tnext = n
sc = sc.pop()
@@ -997,9 +1044,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
cond.fnext = fbody.start
}
tbody.tnext = n
fbody.tnext = n
sc = sc.pop()
@@ -1010,9 +1066,18 @@ 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
sc = sc.pop()
@@ -1022,9 +1087,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
cond.fnext = fbody.start
}
tbody.tnext = n
fbody.tnext = n
sc = sc.pop()
@@ -1262,9 +1336,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 {
@@ -1308,33 +1413,56 @@ 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
c.fnext = clauses[i+1] // Chain to next clause.
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
c := clauses[l-1] // Last clause.
if len(c.child) == 0 {
c.tnext = n // Clause body is empty, exit.
} else {
body := c.lastChild()
c.tnext = body.start
body.tnext = n
}
n.start = n.child[0].start
n.child[0].tnext = sbn.start
sc = sc.pop()
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]
@@ -1362,7 +1490,6 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
sbn.start = clauses[0].start
n.start = n.child[0].start
n.child[0].tnext = sbn.start
sc = sc.pop()
case typeAssertExpr:
if len(n.child) > 1 {
@@ -1576,11 +1703,27 @@ func genRun(nod *node) error {
return err
}
// Find default case clause index of a switch statement, if any
// FixBranch sets the branch action to the identExpr node if it is a bool
// used in a conditional expression.
func fixBranch(n *node) {
switch n.anc.kind {
case ifStmt0, ifStmt1, ifStmt2, ifStmt3, forStmt1, forStmt2, forStmt3, forStmt4:
n.gen = branch
case parenExpr:
fixBranch(n.anc)
}
}
// 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

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.
@@ -65,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
@@ -75,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}
@@ -96,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
@@ -141,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
@@ -165,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
@@ -181,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)
@@ -194,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}
copy(n.typ.method, typ.method)
} else {
n.typ = typ
@@ -208,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

View File

@@ -121,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
}
@@ -152,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
}
@@ -195,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
@@ -202,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
@@ -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]))
}
@@ -669,6 +684,10 @@ 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)
@@ -1561,11 +1580,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
@@ -1574,14 +1601,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))
}
@@ -1603,8 +1630,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 {
@@ -1713,6 +1748,7 @@ func doCompositeLit(n *node, hasType bool) {
}
}
i := n.findex
n.exec = func(f *frame) bltn {
a := reflect.New(n.typ.TypeOf()).Elem()
for i, v := range values {
@@ -1724,7 +1760,7 @@ func doCompositeLit(n *node, hasType bool) {
case destInterface:
d.Set(reflect.ValueOf(valueInterface{n, a}))
default:
d.Set(a)
f.data[i] = a
}
return next
}
@@ -2393,14 +2429,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()
}
@@ -2478,7 +2516,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])
@@ -2488,8 +2528,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
}
}
@@ -2588,56 +2638,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

@@ -278,7 +278,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
case isFloat64(t0) && isFloat64(t1):
t = sc.getType("complex128")
case nt0.untyped && isNumber(t0) && nt1.untyped && isNumber(t1):
t = &itype{cat: valueT, rtype: complexType}
t = &itype{cat: valueT, rtype: complexType, scope: sc}
case nt0.untyped && isFloat32(t1) || nt1.untyped && isFloat32(t0):
t = sc.getType("complex64")
case nt0.untyped && isFloat64(t1) || nt1.untyped && isFloat64(t0):
@@ -301,7 +301,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
case k == reflect.Complex128:
t = sc.getType("float64")
case t.untyped && isNumber(t.TypeOf()):
t = &itype{cat: valueT, rtype: floatType, untyped: true}
t = &itype{cat: valueT, rtype: floatType, untyped: true, scope: sc}
default:
err = n.cfgErrorf("invalid complex type %s", k)
}
@@ -312,7 +312,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t, err = nodeType(interp, sc, n.child[1])
case "new":
t, err = nodeType(interp, sc, n.child[1])
t = &itype{cat: ptrT, val: t, incomplete: t.incomplete}
t = &itype{cat: ptrT, val: t, incomplete: t.incomplete, scope: sc}
case "recover":
t = sc.getType("interface{}")
}
@@ -326,7 +326,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
switch t.cat {
case valueT:
if t.rtype.NumOut() == 1 {
t = &itype{cat: valueT, rtype: t.rtype.Out(0)}
t = &itype{cat: valueT, rtype: t.rtype.Out(0), scope: sc}
}
default:
if len(t.ret) == 1 {
@@ -497,11 +497,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if m, _ := lt.lookupMethod(name); m != nil {
t, err = nodeType(interp, sc, m.child[2])
} else if bm, _, _, ok := lt.lookupBinMethod(name); ok {
t = &itype{cat: valueT, rtype: bm.Type, isBinMethod: true}
t = &itype{cat: valueT, rtype: bm.Type, isBinMethod: true, scope: sc}
} else if ti := lt.lookupField(name); len(ti) > 0 {
t = lt.fieldSeq(ti)
} else if bs, _, ok := lt.lookupBinField(name); ok {
t = &itype{cat: valueT, rtype: bs.Type}
t = &itype{cat: valueT, rtype: bs.Type, scope: sc}
} else {
err = lt.node.cfgErrorf("undefined selector %s", name)
}
@@ -627,7 +627,10 @@ func init() {
zeroValues[uintptrT] = reflect.ValueOf(uintptr(0))
}
// if type is incomplete, re-parse it.
// Finalize returns a type pointer and error. It reparses a type from the
// partial AST if necessary (after missing dependecy data is available).
// If error is nil, the type is guarranteed to be completely defined and
// usable for CFG.
func (t *itype) finalize() (*itype, error) {
var err error
if t.incomplete {
@@ -654,6 +657,99 @@ func (t *itype) finalize() (*itype, error) {
return t, err
}
// ReferTo returns true if the type contains a reference to a
// full type name. It allows to asses a type recursive status.
func (t *itype) referTo(name string, seen map[*itype]bool) bool {
if t.path+"/"+t.name == name {
return true
}
if seen[t] {
return false
}
seen[t] = true
switch t.cat {
case aliasT, arrayT, chanT, ptrT:
return t.val.referTo(name, seen)
case funcT:
for _, a := range t.arg {
if a.referTo(name, seen) {
return true
}
}
for _, a := range t.ret {
if a.referTo(name, seen) {
return true
}
}
case mapT:
return t.key.referTo(name, seen) || t.val.referTo(name, seen)
case structT, interfaceT:
for _, f := range t.field {
if f.typ.referTo(name, seen) {
return true
}
}
}
return false
}
// IsRecursive returns true if type is recursive.
// Only a named struct or interface can be recursive.
func (t *itype) isRecursive() bool {
if t.name == "" {
return false
}
switch t.cat {
case structT, interfaceT:
for _, f := range t.field {
if f.typ.referTo(t.path+"/"+t.name, map[*itype]bool{}) {
return true
}
}
}
return false
}
// isComplete returns true if type definition is complete.
func (t *itype) isComplete() bool { return isComplete(t, map[string]bool{}) }
func isComplete(t *itype, visited map[string]bool) bool {
if t.incomplete {
return false
}
name := t.path + "/" + t.name
if visited[name] {
return !t.incomplete
}
if t.name != "" {
visited[name] = true
}
switch t.cat {
case aliasT, arrayT, chanT, ptrT:
return isComplete(t.val, visited)
case funcT:
complete := true
for _, a := range t.arg {
complete = complete && isComplete(a, visited)
}
for _, a := range t.ret {
complete = complete && isComplete(a, visited)
}
return complete
case interfaceT, structT:
complete := true
for _, f := range t.field {
complete = complete && isComplete(f.typ, visited)
}
return complete
case mapT:
return isComplete(t.key, visited) && isComplete(t.val, visited)
case nilT:
return false
}
return true
}
// 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); {
@@ -748,7 +844,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
v, err = t.val.zero()
case arrayT, ptrT, structT:
v = reflect.New(t.TypeOf()).Elem()
v = reflect.New(t.frameType()).Elem()
case valueT:
v = reflect.New(t.rtype).Elem()
@@ -761,7 +857,8 @@ func (t *itype) zero() (v reflect.Value, err error) {
// fieldIndex returns the field index from name in a struct, or -1 if not found
func (t *itype) fieldIndex(name string) int {
if t.cat == ptrT {
switch t.cat {
case aliasT, ptrT:
return t.val.fieldIndex(name)
}
for i, field := range t.field {
@@ -786,6 +883,10 @@ func (t *itype) fieldSeq(seq []int) *itype {
// lookupField returns a list of indices, i.e. a path to access a field in a struct object
func (t *itype) lookupField(name string) []int {
switch t.cat {
case aliasT, ptrT:
return t.val.lookupField(name)
}
if fi := t.fieldIndex(name); fi >= 0 {
return []int{fi}
}
@@ -824,7 +925,7 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int,
return s, index, ok
}
// methodCallType returns a method function type without the receiver defined.
// 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{}
@@ -840,7 +941,7 @@ func (t *itype) methodCallType() reflect.Type {
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 {
for _, m := range t.method {
if name == m.ident {
@@ -850,8 +951,8 @@ func (t *itype) getMethod(name string) *node {
return nil
}
// lookupMethod returns a pointer to method definition associated to type t
// and the list of indices to access the right struct field, in case of an embedded method
// LookupMethod returns a pointer to method definition associated to type t
// and the list of indices to access the right struct field, in case of an embedded method.
func (t *itype) lookupMethod(name string) (*node, []int) {
if t.cat == ptrT {
return t.val.lookupMethod(name)
@@ -871,7 +972,7 @@ func (t *itype) lookupMethod(name string) (*node, []int) {
return m, index
}
// lookupBinMethod returns a method and a path to access a field in a struct object (the receiver)
// LookupBinMethod returns a method and a path to access a field in a struct object (the receiver).
func (t *itype) lookupBinMethod(name string) (m reflect.Method, index []int, isPtr bool, ok bool) {
if t.cat == ptrT {
return t.val.lookupBinMethod(name)
@@ -901,24 +1002,48 @@ func exportName(s string) string {
return "X" + s
}
var interf = reflect.TypeOf(new(interface{})).Elem()
var interf = reflect.TypeOf((*interface{})(nil)).Elem()
// RefType returns a reflect.Type representation from an interpereter type.
// In simple cases, reflect types are directly mapped from the interpreter
// counterpart.
// For recursive named struct or interfaces, as reflect does not permit to
// create a recursive named struct, an interface{} is returned in place to
// avoid infinitely nested structs.
func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.Type {
if t.rtype != nil {
return t.rtype
}
if t.incomplete || t.cat == nilT {
var err error
if t, err = t.finalize(); err != nil {
panic(err)
}
}
if t.val != nil && defined[t.val.name] != nil && !t.val.incomplete && t.val.rtype == nil {
recursive := false
name := t.path + "/" + t.name
// Predefined types from universe or runtime may have a nil scope.
if t.scope != nil {
if st := t.scope.sym[t.name]; st != nil {
// Update the type recursive status. Several copies of type
// may exist per symbol, as a new type is created at each GTA
// pass (several needed due to out of order declarations), and
// a node can still point to a previous copy.
st.typ.recursive = st.typ.recursive || st.typ.isRecursive()
recursive = st.typ.isRecursive()
}
}
if wrapRecursive && t.recursive {
return interf
}
if t.rtype != nil {
return t.rtype
}
if defined[name] != nil && defined[name].rtype != nil {
return defined[name].rtype
}
if t.val != nil && defined[t.val.path+"/"+t.val.name] != nil && t.val.rtype == nil {
// Replace reference to self (direct or indirect) by an interface{} to handle
// recursive types with reflect.
t.val.rtype = interf
defined[t.val.name].recursive = true
recursive = true
}
switch t.cat {
case aliasT:
@@ -934,9 +1059,11 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
case errorT:
t.rtype = reflect.TypeOf(new(error)).Elem()
case funcT:
if t.name != "" {
defined[name] = t
}
in := make([]reflect.Type, len(t.arg))
out := make([]reflect.Type, len(t.ret))
//wrap := false
for i, v := range t.arg {
in[i] = v.refType(defined, true)
}
@@ -952,14 +1079,17 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
t.rtype = reflect.PtrTo(t.val.refType(defined, wrapRecursive))
case structT:
if t.name != "" {
defined[t.name] = t
if defined[name] != nil {
recursive = true
}
defined[name] = t
}
var fields []reflect.StructField
for _, f := range t.field {
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.refType(defined, wrapRecursive), Tag: reflect.StructTag(f.tag)}
fields = append(fields, field)
}
if t.recursive && wrapRecursive {
if recursive && wrapRecursive {
t.rtype = interf
} else {
t.rtype = reflect.StructOf(fields)
@@ -1027,6 +1157,24 @@ func isShiftNode(n *node) bool {
return false
}
// chanElement returns the channel element type.
func chanElement(t *itype) *itype {
switch t.cat {
case aliasT:
return chanElement(t.val)
case chanT:
return t.val
case valueT:
return &itype{cat: valueT, rtype: t.rtype.Elem(), node: t.node, scope: t.scope}
}
return nil
}
// isChan returns true if type is of channel kind.
func isChan(t *itype) bool {
return t.TypeOf().Kind() == reflect.Chan
}
func isInterfaceSrc(t *itype) bool {
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
}