Commit 3be70366 authored by Russ Cox's avatar Russ Cox

reflect: implement method values

Fixes #1517.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/7906043
parent 178d8d4f
......@@ -1458,7 +1458,7 @@ func (p Point) AnotherMethod(scale int) int {
// This will be index 1.
func (p Point) Dist(scale int) int {
// println("Point.Dist", p.x, p.y, scale)
//println("Point.Dist", p.x, p.y, scale)
return p.x*p.x*scale + p.y*p.y*scale
}
......@@ -1474,23 +1474,23 @@ func TestMethod(t *testing.T) {
if !ok {
t.Fatalf("method by name failed")
}
m.Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Type MethodByName returned %d; want 250", i)
i = m.Func.Call([]Value{ValueOf(p), ValueOf(11)})[0].Int()
if i != 275 {
t.Errorf("Type MethodByName returned %d; want 275", i)
}
i = TypeOf(&p).Method(1).Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Pointer Type Method returned %d; want 250", i)
i = TypeOf(&p).Method(1).Func.Call([]Value{ValueOf(&p), ValueOf(12)})[0].Int()
if i != 300 {
t.Errorf("Pointer Type Method returned %d; want 300", i)
}
m, ok = TypeOf(&p).MethodByName("Dist")
if !ok {
t.Fatalf("ptr method by name failed")
}
i = m.Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Pointer Type MethodByName returned %d; want 250", i)
i = m.Func.Call([]Value{ValueOf(&p), ValueOf(13)})[0].Int()
if i != 325 {
t.Errorf("Pointer Type MethodByName returned %d; want 325", i)
}
// Curried method of value.
......@@ -1499,7 +1499,74 @@ func TestMethod(t *testing.T) {
if tt := v.Type(); tt != tfunc {
t.Errorf("Value Method Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(10)})[0].Int()
i = v.Call([]Value{ValueOf(14)})[0].Int()
if i != 350 {
t.Errorf("Value Method returned %d; want 350", i)
}
v = ValueOf(p).MethodByName("Dist")
if tt := v.Type(); tt != tfunc {
t.Errorf("Value MethodByName Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(15)})[0].Int()
if i != 375 {
t.Errorf("Value MethodByName returned %d; want 375", i)
}
// Curried method of pointer.
v = ValueOf(&p).Method(1)
if tt := v.Type(); tt != tfunc {
t.Errorf("Pointer Value Method Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(16)})[0].Int()
if i != 400 {
t.Errorf("Pointer Value Method returned %d; want 400", i)
}
v = ValueOf(&p).MethodByName("Dist")
if tt := v.Type(); tt != tfunc {
t.Errorf("Pointer Value MethodByName Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(17)})[0].Int()
if i != 425 {
t.Errorf("Pointer Value MethodByName returned %d; want 425", i)
}
// Curried method of interface value.
// Have to wrap interface value in a struct to get at it.
// Passing it to ValueOf directly would
// access the underlying Point, not the interface.
var x interface {
Dist(int) int
} = p
pv := ValueOf(&x).Elem()
v = pv.Method(0)
if tt := v.Type(); tt != tfunc {
t.Errorf("Interface Method Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(18)})[0].Int()
if i != 450 {
t.Errorf("Interface Method returned %d; want 450", i)
}
v = pv.MethodByName("Dist")
if tt := v.Type(); tt != tfunc {
t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(19)})[0].Int()
if i != 475 {
t.Errorf("Interface MethodByName returned %d; want 475", i)
}
}
func TestMethodValue(t *testing.T) {
p := Point{3, 4}
var i int64
// Curried method of value.
tfunc := TypeOf((func(int) int)(nil))
v := ValueOf(p).Method(1)
if tt := v.Type(); tt != tfunc {
t.Errorf("Value Method Type is %s; want %s", tt, tfunc)
}
i = ValueOf(v.Interface()).Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Value Method returned %d; want 250", i)
}
......@@ -1507,9 +1574,9 @@ func TestMethod(t *testing.T) {
if tt := v.Type(); tt != tfunc {
t.Errorf("Value MethodByName Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Value MethodByName returned %d; want 250", i)
i = ValueOf(v.Interface()).Call([]Value{ValueOf(11)})[0].Int()
if i != 275 {
t.Errorf("Value MethodByName returned %d; want 275", i)
}
// Curried method of pointer.
......@@ -1517,17 +1584,17 @@ func TestMethod(t *testing.T) {
if tt := v.Type(); tt != tfunc {
t.Errorf("Pointer Value Method Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Pointer Value Method returned %d; want 250", i)
i = ValueOf(v.Interface()).Call([]Value{ValueOf(12)})[0].Int()
if i != 300 {
t.Errorf("Pointer Value Method returned %d; want 300", i)
}
v = ValueOf(&p).MethodByName("Dist")
if tt := v.Type(); tt != tfunc {
t.Errorf("Pointer Value MethodByName Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Pointer Value MethodByName returned %d; want 250", i)
i = ValueOf(v.Interface()).Call([]Value{ValueOf(13)})[0].Int()
if i != 325 {
t.Errorf("Pointer Value MethodByName returned %d; want 325", i)
}
// Curried method of interface value.
......@@ -1544,20 +1611,203 @@ func TestMethod(t *testing.T) {
if tt := v.Type(); tt != tfunc {
t.Errorf("Interface Method Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Interface Method returned %d; want 250", i)
i = ValueOf(v.Interface()).Call([]Value{ValueOf(14)})[0].Int()
if i != 350 {
t.Errorf("Interface Method returned %d; want 350", i)
}
v = pv.MethodByName("Dist")
if tt := v.Type(); tt != tfunc {
t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc)
}
i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Interface MethodByName returned %d; want 250", i)
i = ValueOf(v.Interface()).Call([]Value{ValueOf(15)})[0].Int()
if i != 375 {
t.Errorf("Interface MethodByName returned %d; want 375", i)
}
}
// Reflect version of $GOROOT/test/method5.go
// Concrete types implementing M method.
// Smaller than a word, word-sized, larger than a word.
// Value and pointer receivers.
type Tinter interface {
M(int, byte) (byte, int)
}
type Tsmallv byte
func (v Tsmallv) M(x int, b byte) (byte, int) { return b, x + int(v) }
type Tsmallp byte
func (p *Tsmallp) M(x int, b byte) (byte, int) { return b, x + int(*p) }
type Twordv uintptr
func (v Twordv) M(x int, b byte) (byte, int) { return b, x + int(v) }
type Twordp uintptr
func (p *Twordp) M(x int, b byte) (byte, int) { return b, x + int(*p) }
type Tbigv [2]uintptr
func (v Tbigv) M(x int, b byte) (byte, int) { return b, x + int(v[0]) + int(v[1]) }
type Tbigp [2]uintptr
func (p *Tbigp) M(x int, b byte) (byte, int) { return b, x + int(p[0]) + int(p[1]) }
// Again, with an unexported method.
type tsmallv byte
func (v tsmallv) m(x int, b byte) (byte, int) { return b, x + int(v) }
type tsmallp byte
func (p *tsmallp) m(x int, b byte) (byte, int) { return b, x + int(*p) }
type twordv uintptr
func (v twordv) m(x int, b byte) (byte, int) { return b, x + int(v) }
type twordp uintptr
func (p *twordp) m(x int, b byte) (byte, int) { return b, x + int(*p) }
type tbigv [2]uintptr
func (v tbigv) m(x int, b byte) (byte, int) { return b, x + int(v[0]) + int(v[1]) }
type tbigp [2]uintptr
func (p *tbigp) m(x int, b byte) (byte, int) { return b, x + int(p[0]) + int(p[1]) }
type tinter interface {
m(int, byte) (byte, int)
}
// Embedding via pointer.
type Tm1 struct {
Tm2
}
type Tm2 struct {
*Tm3
}
type Tm3 struct {
*Tm4
}
type Tm4 struct {
}
func (t4 Tm4) M(x int, b byte) (byte, int) { return b, x + 40 }
func TestMethod5(t *testing.T) {
CheckF := func(name string, f func(int, byte) (byte, int), inc int) {
b, x := f(1000, 99)
if b != 99 || x != 1000+inc {
t.Errorf("%s(1000, 99) = %v, %v, want 99, %v", name, b, x, 1000+inc)
}
}
CheckV := func(name string, i Value, inc int) {
bx := i.Method(0).Call([]Value{ValueOf(1000), ValueOf(byte(99))})
b := bx[0].Interface()
x := bx[1].Interface()
if b != byte(99) || x != 1000+inc {
t.Errorf("direct %s.M(1000, 99) = %v, %v, want 99, %v", name, b, x, 1000+inc)
}
CheckF(name+".M", i.Method(0).Interface().(func(int, byte) (byte, int)), inc)
}
var TinterType = TypeOf(new(Tinter)).Elem()
var tinterType = TypeOf(new(tinter)).Elem()
CheckI := func(name string, i interface{}, inc int) {
v := ValueOf(i)
CheckV(name, v, inc)
CheckV("(i="+name+")", v.Convert(TinterType), inc)
}
sv := Tsmallv(1)
CheckI("sv", sv, 1)
CheckI("&sv", &sv, 1)
sp := Tsmallp(2)
CheckI("&sp", &sp, 2)
wv := Twordv(3)
CheckI("wv", wv, 3)
CheckI("&wv", &wv, 3)
wp := Twordp(4)
CheckI("&wp", &wp, 4)
bv := Tbigv([2]uintptr{5, 6})
CheckI("bv", bv, 11)
CheckI("&bv", &bv, 11)
bp := Tbigp([2]uintptr{7, 8})
CheckI("&bp", &bp, 15)
t4 := Tm4{}
t3 := Tm3{&t4}
t2 := Tm2{&t3}
t1 := Tm1{t2}
CheckI("t4", t4, 40)
CheckI("&t4", &t4, 40)
CheckI("t3", t3, 40)
CheckI("&t3", &t3, 40)
CheckI("t2", t2, 40)
CheckI("&t2", &t2, 40)
CheckI("t1", t1, 40)
CheckI("&t1", &t1, 40)
methodShouldPanic := func(name string, i interface{}) {
v := ValueOf(i)
m := v.Method(0)
shouldPanic(func() { m.Call([]Value{ValueOf(1000), ValueOf(byte(99))}) })
shouldPanic(func() { m.Interface() })
v = v.Convert(tinterType)
m = v.Method(0)
shouldPanic(func() { m.Call([]Value{ValueOf(1000), ValueOf(byte(99))}) })
shouldPanic(func() { m.Interface() })
}
_sv := tsmallv(1)
methodShouldPanic("_sv", _sv)
methodShouldPanic("&_sv", &_sv)
_sp := tsmallp(2)
methodShouldPanic("&_sp", &_sp)
_wv := twordv(3)
methodShouldPanic("_wv", _wv)
methodShouldPanic("&_wv", &_wv)
_wp := twordp(4)
methodShouldPanic("&_wp", &_wp)
_bv := tbigv([2]uintptr{5, 6})
methodShouldPanic("_bv", _bv)
methodShouldPanic("&_bv", &_bv)
_bp := tbigp([2]uintptr{7, 8})
methodShouldPanic("&_bp", &_bp)
var tnil Tinter
vnil := ValueOf(&tnil).Elem()
shouldPanic(func() { vnil.Method(0) })
}
func TestInterfaceSet(t *testing.T) {
p := &Point{3, 4}
......
......@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in value.go
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
TEXT ·makeFuncStub(SB),7,$8
MOVL DX, 0(SP)
......@@ -11,3 +11,13 @@ TEXT ·makeFuncStub(SB),7,$8
MOVL CX, 4(SP)
CALL ·callReflect(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
TEXT ·methodValueCall(SB),7,$8
MOVL DX, 0(SP)
LEAL arg+0(FP), CX
MOVL CX, 4(SP)
CALL ·callMethod(SB)
RET
......@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in value.go
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
TEXT ·makeFuncStub(SB),7,$16
MOVQ DX, 0(SP)
......@@ -11,3 +11,13 @@ TEXT ·makeFuncStub(SB),7,$16
MOVQ CX, 8(SP)
CALL ·callReflect(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
TEXT ·methodValueCall(SB),7,$16
MOVQ DX, 0(SP)
LEAQ arg+0(FP), CX
MOVQ CX, 8(SP)
CALL ·callMethod(SB)
RET
......@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// makeFuncStub is jumped to by the code generated by MakeFunc.
// See the comment on the declaration of makeFuncStub in value.go
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
TEXT ·makeFuncStub(SB),7,$8
MOVW R7, 4(R13)
......@@ -11,3 +11,13 @@ TEXT ·makeFuncStub(SB),7,$8
MOVW R1, 8(R13)
BL ·callReflect(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
TEXT ·methodValueCall(SB),7,$8
MOVW R7, 4(R13)
MOVW $arg+0(FP), R1
MOVW R1, 8(R13)
BL ·callMethod(SB)
RET
......@@ -118,8 +118,6 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
// Normal equality suffices
return valueInterface(v1, false) == valueInterface(v2, false)
}
panic("Not reached")
}
// DeepEqual tests for deep equality. It uses normal == equality where
......
......@@ -48,8 +48,8 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
t := typ.common()
ftyp := (*funcType)(unsafe.Pointer(t))
// indirect Go func value (dummy) to obtain
// actual code address. (A Go func is a pointer
// Indirect Go func value (dummy) to obtain
// actual code address. (A Go func value is a pointer
// to a C function pointer. http://golang.org/s/go11func.)
dummy := makeFuncStub
code := **(**uintptr)(unsafe.Pointer(&dummy))
......@@ -65,3 +65,56 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
// where ctxt is the context register and frame is a pointer to the first
// word in the passed-in argument frame.
func makeFuncStub()
type methodValue struct {
fn uintptr
method int
rcvr Value
}
// makeMethodValue converts v from the rcvr+method index representation
// of a method value to an actual method func value, which is
// basically the receiver value with a special bit set, into a true
// func value - a value holding an actual func. The output is
// semantically equivalent to the input as far as the user of package
// reflect can tell, but the true func representation can be handled
// by code like Convert and Interface and Assign.
func makeMethodValue(op string, v Value) Value {
if v.flag&flagMethod == 0 {
panic("reflect: internal error: invalid use of makePartialFunc")
}
// Ignoring the flagMethod bit, v describes the receiver, not the method type.
fl := v.flag & (flagRO | flagAddr | flagIndir)
fl |= flag(v.typ.Kind()) << flagKindShift
rcvr := Value{v.typ, v.val, fl}
// v.Type returns the actual type of the method value.
funcType := v.Type().(*rtype)
// Indirect Go func value (dummy) to obtain
// actual code address. (A Go func value is a pointer
// to a C function pointer. http://golang.org/s/go11func.)
dummy := methodValueCall
code := **(**uintptr)(unsafe.Pointer(&dummy))
fv := &methodValue{
fn: code,
method: int(v.flag) >> flagMethodShift,
rcvr: rcvr,
}
// Cause panic if method is not appropriate.
// The panic would still happen during the call if we omit this,
// but we want Interface() and other operations to fail early.
methodReceiver(op, fv.rcvr, fv.method)
return Value{funcType, unsafe.Pointer(fv), v.flag&flagRO | flag(Func)<<flagKindShift}
}
// methodValueCall is an assembly function that is the code half of
// the function returned from makeMethodValue. It expects a *methodValue
// as its context register, and its job is to invoke callMethod(ctxt, frame)
// where ctxt is the context register and frame is a pointer to the first
// word in the passed-in argument frame.
func methodValueCall()
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment