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:
committed by
Ludovic Fernandez
parent
bd03b47c06
commit
5677e0501e
@@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
func f() {
|
||||
println("in goroutine f")
|
||||
}
|
||||
|
||||
func main() {
|
||||
go f()
|
||||
//sleep(100)
|
||||
println("in main")
|
||||
}
|
||||
@@ -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
|
||||
|
||||
9
example/getfunc/_gopath/src/github.com/foo/bar/foobar.go
Normal file
9
example/getfunc/_gopath/src/github.com/foo/bar/foobar.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package bar
|
||||
|
||||
type Foo struct {
|
||||
A string
|
||||
}
|
||||
|
||||
func NewFoo() *Foo {
|
||||
return &Foo{A: "test"}
|
||||
}
|
||||
27
example/getfunc/getfunc_test.go
Normal file
27
example/getfunc/getfunc_test.go
Normal 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))
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
|
||||
79
interp/interp_eval_test.go
Normal file
79
interp/interp_eval_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user