Compare commits

...

8 Commits

Author SHA1 Message Date
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
29 changed files with 597 additions and 75 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

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

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

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

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

View File

@@ -317,7 +317,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]
@@ -863,10 +863,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 +883,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 +904,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 +933,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 +1004,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 +1019,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 +1037,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 +1059,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 +1080,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()
@@ -1308,33 +1375,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 +1452,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 +1665,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.

View File

@@ -669,6 +669,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)
@@ -2393,14 +2397,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()
}
@@ -2588,56 +2594,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

@@ -905,6 +905,9 @@ var interf = reflect.TypeOf(new(interface{})).Elem()
func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.Type {
if t.rtype != nil {
if wrapRecursive && t.cat == structT && defined[t.name] != nil {
return interf
}
return t.rtype
}