fix: handle method on interface type objects (#112)
Define a new Method action to detect method calls on interface types. In this case the receiver is resolved from a dynamic type rather than a static one. Handle method calls where receiver is a pointer. In typeAssert, always return an interface value with the concrete type, to fix method lookup on interface objects. Add relevant tests.
This commit is contained in:
committed by
Ludovic Fernandez
parent
80f20189b4
commit
22a6d011f4
@@ -209,6 +209,7 @@ const (
|
||||
Lor
|
||||
Lower
|
||||
LowerEqual
|
||||
Method
|
||||
Mul
|
||||
MulAssign
|
||||
Negate
|
||||
@@ -263,6 +264,7 @@ var actions = [...]string{
|
||||
Land: "&&",
|
||||
Lor: "||",
|
||||
Lower: "<",
|
||||
Method: "Method",
|
||||
Mul: "*",
|
||||
MulAssign: "*=",
|
||||
Negate: "-",
|
||||
|
||||
@@ -956,7 +956,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
|
||||
n.val = ti
|
||||
switch n.typ.cat {
|
||||
case InterfaceT:
|
||||
n.typ = n.typ.fieldSeq(ti)
|
||||
n.gen = getMethodByName
|
||||
n.action = Method
|
||||
case PtrT:
|
||||
n.typ = n.typ.fieldSeq(ti)
|
||||
n.gen = getPtrIndexSeq
|
||||
|
||||
@@ -250,6 +250,38 @@ func TestEvalUnary(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalMethod(t *testing.T) {
|
||||
i := interp.New(interp.Opt{})
|
||||
eval(t, i, `
|
||||
type Root struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type One struct {
|
||||
Root
|
||||
}
|
||||
|
||||
type Hi interface {
|
||||
Hello() string
|
||||
}
|
||||
|
||||
func (r *Root) Hello() string { return "Hello " + r.Name }
|
||||
|
||||
var r = Root{"R"}
|
||||
var o = One{r}
|
||||
var root interface{} = &Root{Name: "test1"}
|
||||
var one interface{} = &One{Root{Name: "test2"}}
|
||||
`)
|
||||
runTests(t, i, []testCase{
|
||||
{src: "r.Hello()", res: "Hello R"},
|
||||
{src: "(&r).Hello()", res: "Hello R"},
|
||||
{src: "o.Hello()", res: "Hello R"},
|
||||
{src: "(&o).Hello()", res: "Hello R"},
|
||||
{src: "root.(Hi).Hello()", res: "Hello test1"},
|
||||
{src: "one.(Hi).Hello()", res: "Hello test2"},
|
||||
})
|
||||
}
|
||||
|
||||
func runTests(t *testing.T, i *interp.Interpreter, tests []testCase) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
|
||||
@@ -114,12 +114,19 @@ func typeAssert(n *Node) {
|
||||
i := n.findex
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if n.child[0].typ.cat == ValueT {
|
||||
switch {
|
||||
case n.child[0].typ.cat == ValueT:
|
||||
n.exec = func(f *Frame) Builtin {
|
||||
f.data[i] = value(f).Elem()
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
case n.child[1].typ.cat == InterfaceT:
|
||||
n.exec = func(f *Frame) Builtin {
|
||||
v := value(f).Interface().(valueInterface)
|
||||
f.data[i] = reflect.ValueOf(valueInterface{v.node, v.value})
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *Frame) Builtin {
|
||||
v := value(f).Interface().(valueInterface)
|
||||
f.data[i] = v.value
|
||||
@@ -134,7 +141,8 @@ func typeAssert2(n *Node) {
|
||||
value1 := genValue(n.anc.child[1]) // returned status
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if n.child[0].typ.cat == ValueT {
|
||||
switch {
|
||||
case n.child[0].typ.cat == ValueT:
|
||||
n.exec = func(f *Frame) Builtin {
|
||||
if value(f).IsValid() && !value(f).IsNil() {
|
||||
value0(f).Set(value(f).Elem())
|
||||
@@ -142,7 +150,14 @@ func typeAssert2(n *Node) {
|
||||
value1(f).SetBool(true)
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
case n.child[1].typ.cat == InterfaceT:
|
||||
n.exec = func(f *Frame) Builtin {
|
||||
v, ok := value(f).Interface().(valueInterface)
|
||||
value0(f).Set(reflect.ValueOf(valueInterface{v.node, v.value}))
|
||||
value1(f).SetBool(ok)
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *Frame) Builtin {
|
||||
v, ok := value(f).Interface().(valueInterface)
|
||||
value0(f).Set(v.value)
|
||||
@@ -453,8 +468,7 @@ func call(n *Node) {
|
||||
// Compute method receiver value
|
||||
values = append(values, genValueRecv(n.child[0]))
|
||||
method = true
|
||||
}
|
||||
if n.child[0].typ.cat == InterfaceT {
|
||||
} else if n.child[0].action == Method {
|
||||
// add a place holder for interface method receiver
|
||||
values = append(values, nil)
|
||||
method = true
|
||||
@@ -529,7 +543,11 @@ func call(n *Node) {
|
||||
if v == nil {
|
||||
src = def.recv.val
|
||||
if len(def.recv.index) > 0 {
|
||||
src = src.FieldByIndex(def.recv.index)
|
||||
if src.Kind() == reflect.Ptr {
|
||||
src = src.Elem().FieldByIndex(def.recv.index)
|
||||
} else {
|
||||
src = src.FieldByIndex(def.recv.index)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
src = v(f)
|
||||
|
||||
Reference in New Issue
Block a user