fix: correct handling of func in struct fields (#99)

Wrap functions in struct fields in `reflect.Value` to have a consistent
representation of function inside and outside the interpreter.

Teach assign to wrap a function node if destination is reflect.Value

The same needs to be done for composite literal, in a next commit.
This commit is contained in:
Marc Vertes
2019-02-21 14:03:28 +01:00
committed by Ludovic Fernandez
parent 80527bb903
commit abe384a765
3 changed files with 65 additions and 3 deletions

View File

@@ -951,8 +951,14 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
n.typ = n.typ.fieldSeq(ti)
n.gen = getPtrIndexSeq
default:
n.typ = n.typ.fieldSeq(ti)
n.gen = getIndexSeq
n.typ = n.typ.fieldSeq(ti)
if n.typ.cat == FuncT {
// function in a struct field is always wrapped in reflect.Value
rtype := n.typ.TypeOf()
n.typ = &Type{cat: ValueT, rtype: rtype}
n.fsize = rtype.NumOut()
}
}
} else {
err = n.cfgError("undefined selector: %s", n.child[1].ident)

View File

@@ -1,6 +1,7 @@
package interp_test
import (
"log"
"reflect"
"testing"
@@ -98,6 +99,55 @@ func TestEvalNil2(t *testing.T) {
}
}
func TestEvalStruct0(t *testing.T) {
i := interp.New(interp.Opt{})
evalCheck(t, i, `
type Fromage struct {
Name string
Call func(string) string
}
func f() string {
a := Fromage{}
a.Name = "test"
a.Call = func(s string) string { return s }
return a.Call(a.Name)
}
`)
v := evalCheck(t, i, `f()`)
if v.Interface().(string) != "test" {
t.Fatalf("expected test, got %v", v)
}
}
func TestEvalStruct1(t *testing.T) {
t.Skip("not yet implemented")
log.SetFlags(log.Lshortfile)
i := interp.New(interp.Opt{})
evalCheck(t, i, `
type Fromage struct {
Name string
Call func(string) string
}
func f() string {
a := Fromage{
"test",
func(s string) string { return s },
}
return a.Call(a.Name)
}
`)
v := evalCheck(t, i, `f()`)
if v.Interface().(string) != "test" {
t.Fatalf("expected test, got %v", v)
}
}
func TestEvalComposite0(t *testing.T) {
i := interp.New(interp.Opt{})
evalCheck(t, i, `

View File

@@ -192,7 +192,6 @@ func assignX(n *Node) {
// assign implements single value assignment
func assign(n *Node) {
value := genValue(n)
next := getExec(n.tnext)
if n.child[0].typ.cat == InterfaceT {
@@ -203,7 +202,14 @@ func assign(n *Node) {
return next
}
} else {
value1 := genValue(n.lastChild())
value := genValue(n)
dest, src := n.child[0], n.lastChild()
var value1 func(*Frame) reflect.Value
if dest.typ.cat == ValueT && src.typ.cat == FuncT {
value1 = genNodeWrapper(src)
} else {
value1 = genValue(src)
}
n.exec = func(f *Frame) Builtin {
value(f).Set(value1(f))
return next