Skip to content

Commit

Permalink
interp: improve method resolution on embedded fields
Browse files Browse the repository at this point in the history
The capability to dereference pointers has been added to
methodByName(), improving method lookup on binary values.

Wrapping to valueInterface is performed in a missing use case at
return of function calls. It was done in the nested call, but not
at assign.

Fixes #1330.
  • Loading branch information
mvertes committed Dec 20, 2021
1 parent 8323068 commit 2af660c
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 10 deletions.
42 changes: 42 additions & 0 deletions _test/issue-1330.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"fmt"
"io"
"net"
)

type wrappedConn struct {
net.Conn
}

func main() {
_, err := net.Listen("tcp", "127.0.0.1:49153")
if err != nil {
panic(err)
}

dialer := &net.Dialer{
LocalAddr: &net.TCPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 0,
},
}

conn, err := dialer.Dial("tcp", "127.0.0.1:49153")
if err != nil {
panic(err)
}
defer conn.Close()

t := &wrappedConn{conn}
var w io.Writer = t
if n, err := w.Write([]byte("hello")); err != nil {
fmt.Println(err)
} else {
fmt.Println(n)
}
}

// Output:
// 5
1 change: 1 addition & 0 deletions interp/interp_consistent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "io0.go" || // use random number
file.Name() == "issue-1093.go" || // expect error
file.Name() == "issue-1276.go" || // expect error
file.Name() == "issue-1330.go" || // expect error
file.Name() == "op1.go" || // expect error
file.Name() == "op7.go" || // expect error
file.Name() == "op9.go" || // expect error
Expand Down
60 changes: 50 additions & 10 deletions interp/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -1086,7 +1086,7 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
for i, m := range methods {
if m == nil {
// First direct method lookup on field.
if r := methodByName(v, names[i]); r.IsValid() {
if r := methodByName(v, names[i], indexes[i]); r.IsValid() {
w.Field(i + 1).Set(r)
continue
}
Expand All @@ -1111,15 +1111,50 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
}
}

// methodByName return the method corresponding to name on value, or nil if not found.
// methodByName returns the method corresponding to name on value, or nil if not found.
// The search is extended on valueInterface wrapper if present.
func methodByName(value reflect.Value, name string) reflect.Value {
// If valid, the returned value is a method function with the receiver already set
// (no need to pass it at call).
func methodByName(value reflect.Value, name string, index []int) (v reflect.Value) {
if vi, ok := value.Interface().(valueInterface); ok {
if v := getConcreteValue(vi.value).MethodByName(name); v.IsValid() {
return v
if v = getConcreteValue(vi.value).MethodByName(name); v.IsValid() {
return
}
}
if v = value.MethodByName(name); v.IsValid() {
return
}
for value.Kind() == reflect.Ptr {
value = value.Elem()
if checkFieldIndex(value.Type(), index) {
value = value.FieldByIndex(index)
}
if v = value.MethodByName(name); v.IsValid() {
return
}
}
return value.MethodByName(name)
return
}

func checkFieldIndex(typ reflect.Type, index []int) bool {
if len(index) == 0 {
return false
}
t := typ
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return false
}
i := index[0]
if i >= t.NumField() {
return false
}
if len(index) > 1 {
return checkFieldIndex(t.Field(i).Type, index[1:])
}
return true
}

func call(n *node) {
Expand Down Expand Up @@ -1610,11 +1645,16 @@ func callBin(n *node) {
}
out := callFn(value(f), in)
for i := 0; i < len(out); i++ {
if out[i].Kind() == reflect.Func {
getFrame(f, n.level).data[n.findex+i] = out[i]
} else {
getFrame(f, n.level).data[n.findex+i].Set(out[i])
r := out[i]
if r.Kind() == reflect.Func {
getFrame(f, n.level).data[n.findex+i] = r
continue
}
dest := getFrame(f, n.level).data[n.findex+i]
if _, ok := dest.Interface().(valueInterface); ok {
r = reflect.ValueOf(valueInterface{value: r})
}
dest.Set(r)
}
return tnext
}
Expand Down

0 comments on commit 2af660c

Please sign in to comment.