fix: correct propagation of values during eval (#95)

Eval returns the value of the root node of AST, so ensure
that node values are propagated back to ExprStmt and BlockStmt,
which should be sufficient to handle evaluation of simple
statements.
This commit is contained in:
Marc Vertes
2019-02-19 14:51:44 +01:00
committed by Ludovic Fernandez
parent bd03b47c06
commit 5677e0501e
11 changed files with 155 additions and 53 deletions

View File

@@ -1,11 +0,0 @@
package main
func f() {
println("in goroutine f")
}
func main() {
go f()
//sleep(100)
println("in main")
}

View File

@@ -7,12 +7,13 @@ import (
func main() {
c1 := make(chan string)
c2 := make(chan string)
a := 0
go func() {
toSend := "hello"
select {
case c2 <- toSend:
fmt.Println("Sent", toSend, "to c2")
a++
}
}()
@@ -22,10 +23,9 @@ func main() {
case msg2 := <-c2:
fmt.Println("received from c2:", msg2)
}
fmt.Println("Bye")
fmt.Println("Bye", a)
}
// Output:
// Sent hello to c2
// received from c2: hello
// Bye
// Bye 1

View File

@@ -0,0 +1,9 @@
package bar
type Foo struct {
A string
}
func NewFoo() *Foo {
return &Foo{A: "test"}
}

View File

@@ -0,0 +1,27 @@
package main
import (
"fmt"
"testing"
"github.com/containous/dyngo/interp"
"github.com/containous/dyngo/stdlib"
)
func TestGetFunc(t *testing.T) {
i := interp.New(interp.Opt{
GoPath: "./_gopath/",
})
i.Use(stdlib.Value, stdlib.Type)
if _, err := i.Eval(`import "github.com/foo/bar"`); err != nil {
t.Fatal(err)
}
val, err := i.Eval(`bar.NewFoo`)
if err != nil {
t.Fatal(err)
}
fmt.Println(val.Call(nil))
}

View File

@@ -52,7 +52,6 @@ const (
FuncDecl
FuncLit
FuncType
Go
GoStmt
Goto
Ident
@@ -77,7 +76,6 @@ const (
SelectStmt
SelectorExpr
SelectorImport
SelectorSrc
SendStmt
SliceExpr
StarExpr
@@ -132,7 +130,6 @@ var kinds = [...]string{
FuncDecl: "FuncDecl",
FuncType: "FuncType",
FuncLit: "FuncLit",
Go: "Go",
GoStmt: "GoStmt",
Goto: "Goto",
Ident: "Ident",
@@ -157,7 +154,6 @@ var kinds = [...]string{
SelectStmt: "SelectStmt",
SelectorExpr: "SelectorExpr",
SelectorImport: "SelectorImport",
SelectorSrc: "SelectorSrc",
SendStmt: "SendStmt",
SliceExpr: "SliceExpr",
StarExpr: "StarExpr",

View File

@@ -456,6 +456,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
wireChild(n)
if len(n.child) > 0 {
n.findex = n.lastChild().findex
n.val = n.lastChild().val
n.sym = n.lastChild().sym
n.typ = n.lastChild().typ
}
scope = scope.pop()
@@ -466,6 +469,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
case DeclStmt, ExprStmt, VarDecl, SendStmt:
wireChild(n)
n.findex = n.lastChild().findex
n.val = n.lastChild().val
n.sym = n.lastChild().sym
n.typ = n.lastChild().typ
case Break:
n.tnext = loop
@@ -916,9 +922,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
} else if n.typ.cat == SrcPkgT {
// Resolve source package symbol
if sym, ok := interp.scope[n.child[0].ident].sym[n.child[1].ident]; ok {
n.findex = sym.index
n.val = sym.node
n.gen = nop
n.kind = SelectorSrc
n.typ = sym.typ
} else {
err = n.cfgError("undefined selector: %s", n.child[1].ident)
@@ -974,7 +980,7 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
for _, t := range c.child[:len(c.child)-1] {
tid := t.typ.id()
if usedCase[tid] {
err = c.cfgError("duplicate case %s in type switch", tid)
err = c.cfgError("duplicate case %s in type switch", t.ident)
return
}
usedCase[tid] = true
@@ -1143,7 +1149,7 @@ func (n *Node) isType(scope *Scope) bool {
if p, ok := n.interp.binType[sym.path]; ok && p[name] != nil {
return true // Imported binary type
}
if p, ok := n.interp.scope[sym.path]; ok && p.sym[name] != nil && p.sym[name].kind == Typ {
if p, ok := n.interp.scope[pkg]; ok && p.sym[name] != nil && p.sym[name].kind == Typ {
return true // Imported source type
}
}
@@ -1333,7 +1339,7 @@ func frameTypes(node *Node, size int) ([]reflect.Type, error) {
if n.kind == FuncDecl || n.kind == ImportDecl || n.kind == TypeDecl || n.kind == FuncLit {
return n == node // Do not dive in subtree, except if this is the entry point
}
if n.findex < 0 || n.typ == nil || n.level > 0 || n.kind == BasicLit || n.kind == SelectorSrc || n.typ.cat == BinPkgT {
if n.findex < 0 || n.typ == nil || n.level > 0 || n.kind == BasicLit || n.typ.cat == BinPkgT {
return true
}
if ft[n.findex] == nil {

View File

@@ -201,7 +201,7 @@ func (i *Interpreter) Eval(src string) (reflect.Value, error) {
}
// Global type analysis
if err = i.Gta(root, ""); err != nil {
if err = i.Gta(root, pkgName); err != nil {
return res, err
}
@@ -219,6 +219,10 @@ func (i *Interpreter) Eval(src string) (reflect.Value, error) {
root.types, _ = frameTypes(root, i.fsize+1)
setExec(root.start)
}
if i.universe.sym[pkgName] == nil {
// Make the package visible under a path identical to its name
i.universe.sym[pkgName] = &Symbol{typ: &Type{cat: SrcPkgT}, path: pkgName}
}
if i.CfgDot {
root.CfgDot(DotX())

View File

@@ -0,0 +1,79 @@
package interp_test
import (
"testing"
"github.com/containous/dyngo/interp"
)
func TestEval0(t *testing.T) {
i := interp.New(interp.Opt{})
_, err := i.Eval(`var I int = 2`)
if err != nil {
t.Fatal(err)
}
t1, err := i.Eval(`I`)
if err != nil {
t.Fatal(err)
}
if t1.Interface().(int) != 2 {
t.Fatalf("expected 2, got %v", t1)
}
}
func TestEval1(t *testing.T) {
i := interp.New(interp.Opt{})
_, err := i.Eval(`func Hello() string { return "hello" }`)
if err != nil {
t.Fatal(err)
}
v, err := i.Eval(`Hello`)
if err != nil {
t.Fatal(err)
}
f, ok := v.Interface().(func() string)
if !ok {
t.Fatal("conversion failed")
}
if s := f(); s != "hello" {
t.Fatalf("expected hello, got %v", s)
}
}
func TestEval2(t *testing.T) {
i := interp.New(interp.Opt{})
_, err := i.Eval(`package foo; var I int = 2`)
if err != nil {
t.Fatal(err)
}
t1, err := i.Eval(`foo.I`)
if err != nil {
t.Fatal(err)
}
if t1.Interface().(int) != 2 {
t.Fatalf("expected 2, got %v", t1)
}
}
func TestEval3(t *testing.T) {
i := interp.New(interp.Opt{})
_, err := i.Eval(`package foo; func Hello() string { return "hello" }`)
if err != nil {
t.Fatal(err)
}
v, err := i.Eval(`foo.Hello`)
if err != nil {
t.Fatal(err)
}
f, ok := v.Interface().(func() string)
if !ok {
t.Fatal("conversion failed")
}
if s := f(); s != "hello" {
t.Fatalf("expected hello, got %v", s)
}
}

View File

@@ -2213,28 +2213,6 @@ func main() {
// 34
}
func Example_goroutine() {
src := `
package main
func f() {
println("in goroutine f")
}
func main() {
go f()
//sleep(100)
println("in main")
}`
i := interp.New(interp.Opt{Entry: "main"})
i.Use(stdlib.Value, stdlib.Type)
_, err := i.Eval(src)
if err != nil {
panic(err)
}
}
func Example_heap() {
src := `
// This example demonstrates an integer heap built using the heap interface.
@@ -5655,12 +5633,13 @@ import (
func main() {
c1 := make(chan string)
c2 := make(chan string)
a := 0
go func() {
toSend := "hello"
select {
case c2 <- toSend:
fmt.Println("Sent", toSend, "to c2")
a++
}
}()
@@ -5670,7 +5649,7 @@ func main() {
case msg2 := <-c2:
fmt.Println("received from c2:", msg2)
}
fmt.Println("Bye")
fmt.Println("Bye", a)
}
`
i := interp.New(interp.Opt{Entry: "main"})
@@ -5681,9 +5660,8 @@ func main() {
}
// Output:
// Sent hello to c2
// received from c2: hello
// Bye
// Bye 1
}
func Example_select3() {

View File

@@ -285,7 +285,12 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) (*Type, error) {
case SelectorExpr:
pkgName, typeName := n.child[0].ident, n.child[1].ident
if sym, _, found := scope.lookup(pkgName); found {
if sym.typ != nil && sym.typ.cat == BinPkgT {
if sym.typ == nil {
t.incomplete = true
break
}
switch sym.typ.cat {
case BinPkgT:
pkg := interp.binType[sym.path]
if typ, ok := pkg[typeName]; ok {
t.cat = ValueT
@@ -293,6 +298,11 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) (*Type, error) {
} else {
t.incomplete = true
}
case SrcPkgT:
pkg := interp.scope[pkgName]
if st, ok := pkg.sym[typeName]; ok && st.kind == Typ {
t = st.typ
}
}
} else {
err = n.cfgError("undefined package: %s", pkgName)
@@ -532,7 +542,11 @@ func (t *Type) TypeOf() reflect.Type {
default:
z, _ := t.zero()
return z.Type()
if z.IsValid() {
return z.Type()
}
var empty reflect.Type
return empty
}
}

View File

@@ -43,7 +43,7 @@ func genValueRecv(n *Node) func(*Frame) reflect.Value {
func genValue(n *Node) func(*Frame) reflect.Value {
switch n.kind {
case BasicLit, FuncDecl, SelectorSrc:
case BasicLit, FuncDecl:
var v reflect.Value
if w, ok := n.val.(reflect.Value); ok {
v = w