fix: handle use of functions in struct fields
This commit is contained in:
39
_test/struct32.go
Normal file
39
_test/struct32.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
type T0 struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type lookupFunc func(s string) T0
|
||||
|
||||
type T1 struct {
|
||||
name string
|
||||
info lookupFunc
|
||||
}
|
||||
|
||||
func (t T0) F1() bool { println("in F1"); return true }
|
||||
|
||||
type T2 struct {
|
||||
t1 T1
|
||||
}
|
||||
|
||||
func (t2 *T2) f() {
|
||||
info := t2.t1.info("foo")
|
||||
println(info.F1())
|
||||
}
|
||||
|
||||
var t0 = T0{"t0"}
|
||||
|
||||
func main() {
|
||||
t := &T2{T1{
|
||||
"bar", func(s string) T0 { return t0 },
|
||||
}}
|
||||
|
||||
println("hello")
|
||||
println(t.t1.info("foo").F1())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
// in F1
|
||||
// true
|
||||
34
_test/struct33.go
Normal file
34
_test/struct33.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
type T0 struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type lookupFunc func(s string) T0
|
||||
|
||||
type T1 struct {
|
||||
name string
|
||||
info lookupFunc
|
||||
}
|
||||
|
||||
func (t T0) F1() bool { println("in F1"); return true }
|
||||
|
||||
var t0 = T0{"t0"}
|
||||
|
||||
func look(s string) T0 { println("in look"); return t0 }
|
||||
|
||||
var table = []*T1{{
|
||||
name: "bar",
|
||||
info: look,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
info := table[0].info
|
||||
println(info("foo").F1())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in look
|
||||
// in F1
|
||||
// true
|
||||
@@ -777,10 +777,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case isBinCall(n):
|
||||
n.gen = callBin
|
||||
if typ := n.child[0].typ.rtype; typ.NumOut() > 0 {
|
||||
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < typ.NumOut(); i++ {
|
||||
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
|
||||
if funcType := n.child[0].typ.val; funcType != nil {
|
||||
// Use the original unwrapped function type, to allow future field and
|
||||
// methods resolutions, otherwise impossible on the opaque bin type.
|
||||
n.typ = funcType.ret[0]
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < len(funcType.ret); i++ {
|
||||
sc.add(funcType.ret[i])
|
||||
}
|
||||
} else {
|
||||
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < typ.NumOut(); i++ {
|
||||
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -1225,7 +1235,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if n.typ.cat == funcT {
|
||||
// function in a struct field is always wrapped in reflect.Value
|
||||
rtype := n.typ.TypeOf()
|
||||
n.typ = &itype{cat: valueT, rtype: rtype}
|
||||
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
|
||||
}
|
||||
default:
|
||||
n.gen = getIndexSeq
|
||||
@@ -1233,7 +1243,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if n.typ.cat == funcT {
|
||||
// function in a struct field is always wrapped in reflect.Value
|
||||
rtype := n.typ.TypeOf()
|
||||
n.typ = &itype{cat: valueT, rtype: rtype}
|
||||
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
|
||||
}
|
||||
}
|
||||
} else if s, lind, ok := n.typ.lookupBinField(n.child[1].ident); ok {
|
||||
@@ -1741,6 +1751,10 @@ func isKey(n *node) bool {
|
||||
(n.anc.kind == fieldExpr && len(n.anc.child) > 1 && n.anc.child[0] == n)
|
||||
}
|
||||
|
||||
func isField(n *node) bool {
|
||||
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
|
||||
}
|
||||
|
||||
// isNewDefine returns true if node refers to a new definition
|
||||
func isNewDefine(n *node, sc *scope) bool {
|
||||
if n.ident == "_" {
|
||||
|
||||
@@ -525,7 +525,11 @@ func eval(t *testing.T, i *interp.Interpreter, src string) reflect.Value {
|
||||
t.Helper()
|
||||
res, err := i.Eval(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Logf("Error: %v", err)
|
||||
if e, ok := err.(interp.Panic); ok {
|
||||
t.Logf(string(e.Stack))
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -541,7 +545,11 @@ func assertEval(t *testing.T, i *interp.Interpreter, src, expectedError, expecte
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("got an error %v", err)
|
||||
t.Logf("got an error: %v", err)
|
||||
if e, ok := err.(interp.Panic); ok {
|
||||
t.Logf(string(e.Stack))
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if fmt.Sprintf("%v", res) != expectedRes {
|
||||
|
||||
@@ -264,7 +264,9 @@ func assign(n *node) {
|
||||
svalue[i] = genValueInterface(src)
|
||||
case (dest.typ.cat == valueT || dest.typ.cat == errorT) && dest.typ.rtype.Kind() == reflect.Interface:
|
||||
svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
|
||||
case dest.typ.cat == valueT && src.typ.cat == funcT:
|
||||
case src.typ.cat == funcT && dest.typ.cat == valueT:
|
||||
svalue[i] = genFunctionWrapper(src)
|
||||
case src.typ.cat == funcT && isField(dest):
|
||||
svalue[i] = genFunctionWrapper(src)
|
||||
case dest.typ.cat == funcT && src.typ.cat == valueT:
|
||||
svalue[i] = genValueNode(src)
|
||||
@@ -708,19 +710,24 @@ func call(n *node) {
|
||||
}
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
def := value(f).Interface().(*node)
|
||||
var def *node
|
||||
var ok bool
|
||||
bf := value(f)
|
||||
if def, ok = bf.Interface().(*node); ok {
|
||||
bf = def.rval
|
||||
}
|
||||
|
||||
// Call bin func if defined
|
||||
if def.rval.IsValid() {
|
||||
if bf.IsValid() {
|
||||
in := make([]reflect.Value, len(values))
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
}
|
||||
if goroutine {
|
||||
go def.rval.Call(in)
|
||||
go bf.Call(in)
|
||||
return tnext
|
||||
}
|
||||
out := def.rval.Call(in)
|
||||
out := bf.Call(in)
|
||||
for i, v := range rvalues {
|
||||
if v != nil {
|
||||
v(f).Set(out[i])
|
||||
|
||||
@@ -619,6 +619,8 @@ func (t *itype) finalize() (*itype, error) {
|
||||
sym, _, found := t.scope.lookup(t.name)
|
||||
if found && !sym.typ.incomplete {
|
||||
sym.typ.method = append(sym.typ.method, t.method...)
|
||||
t.method = sym.typ.method
|
||||
t.incomplete = false
|
||||
return sym.typ, nil
|
||||
}
|
||||
m := t.method
|
||||
@@ -1017,12 +1019,17 @@ func isInterface(t *itype) bool {
|
||||
func isStruct(t *itype) bool {
|
||||
// Test first for a struct category, because a recursive interpreter struct may be
|
||||
// represented by an interface{} at reflect level.
|
||||
if t.cat == structT {
|
||||
switch t.cat {
|
||||
case structT:
|
||||
return true
|
||||
case aliasT, ptrT:
|
||||
return isStruct(t.val)
|
||||
case valueT:
|
||||
k := t.rtype.Kind()
|
||||
return k == reflect.Struct || (k == reflect.Ptr && t.rtype.Elem().Kind() == reflect.Struct)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
rt := t.TypeOf()
|
||||
k := rt.Kind()
|
||||
return k == reflect.Struct || (k == reflect.Ptr && rt.Elem().Kind() == reflect.Struct)
|
||||
}
|
||||
|
||||
func isBool(t *itype) bool { return t.TypeOf().Kind() == reflect.Bool }
|
||||
|
||||
Reference in New Issue
Block a user