fix: correct embedded method handling on hybrid struct (#162)

This commit is contained in:
Marc Vertes
2019-04-19 18:30:04 +02:00
committed by Ludovic Fernandez
parent 800ad0e557
commit 37f93f0392
6 changed files with 110 additions and 3 deletions

34
_test/method17.go Normal file
View File

@@ -0,0 +1,34 @@
package main
import (
"fmt"
"time"
)
type MyTime struct {
time.Time
index int
}
func (m MyTime) Foo() {
minute := m.Minute()
fmt.Println("minute:", minute)
}
func (m *MyTime) Bar() {
second := m.Second()
fmt.Println("second:", second)
}
func main() {
t := MyTime{}
t.Time = time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
t.Foo()
t.Bar()
(&t).Bar()
}
// Output:
// minute: 4
// second: 5
// second: 5

28
_test/method18.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
"compress/gzip"
"fmt"
"net/http"
)
type GzipResponseWriter struct {
http.ResponseWriter
index int
gw *gzip.Writer
}
type GzipResponseWriterWithCloseNotify struct {
*GzipResponseWriter
}
func (w GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
}
func main() {
fmt.Println("hello")
}
// Output:
// hello

View File

@@ -1027,6 +1027,10 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
n.typ = m.typ
n.recv = &Receiver{node: n.child[0], index: lind}
}
} else if m, lind, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
n.gen = getIndexSeqMethod
n.val = append([]int{m.Index}, lind...)
n.typ = &Type{cat: ValueT, rtype: m.Type}
} else if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
// Handle struct field
n.val = ti

View File

@@ -39,8 +39,8 @@ type Node struct {
// Receiver stores method receiver object access path
type Receiver struct {
node *Node // receiver value for alias and struct types
val reflect.Value // receiver value for interface type
index []int // path in receiver value for interface type
val reflect.Value // receiver value for interface type and value type
index []int // path in receiver value for interface or value type
}
// Frame contains values for the current execution level (a function context)

View File

@@ -882,6 +882,27 @@ func getPtrIndexSeq(n *Node) {
}
}
func getIndexSeqMethod(n *Node) {
value := genValue(n.child[0])
index := n.val.([]int)
fi := index[1:]
mi := index[0]
i := n.findex
next := getExec(n.tnext)
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
n.exec = func(f *Frame) Builtin {
f.data[i] = value(f).Elem().FieldByIndex(fi).Method(mi)
return next
}
} else {
n.exec = func(f *Frame) Builtin {
f.data[i] = value(f).FieldByIndex(fi).Method(mi)
return next
}
}
}
func negate(n *Node) {
i := n.findex
value := genValue(n.child[0])

View File

@@ -503,7 +503,7 @@ func (t *Type) getMethod(name string) *Node {
}
// lookupMethod returns a pointer to method definition associated to type t
// and the list of indices to access the right struct field, in case of a promoted method
// and the list of indices to access the right struct field, in case of an embedded method
func (t *Type) lookupMethod(name string) (*Node, []int) {
if t.cat == PtrT {
return t.val.lookupMethod(name)
@@ -523,6 +523,26 @@ func (t *Type) lookupMethod(name string) (*Node, []int) {
return m, index
}
// lookupBinMethod returns a method and a path to access a field in a struct object (the receiver)
func (t *Type) lookupBinMethod(name string) (reflect.Method, []int, bool) {
if t.cat == PtrT {
return t.val.lookupBinMethod(name)
}
var index []int
m, ok := t.TypeOf().MethodByName(name)
if !ok {
for i, f := range t.field {
if f.embed {
if m2, index2, ok2 := f.typ.lookupBinMethod(name); ok2 {
index = append([]int{i}, index2...)
return m2, index, ok2
}
}
}
}
return m, index, ok
}
func exportName(s string) string {
if canExport(s) {
return s