1
0
mirror of https://github.com/golang/go synced 2024-11-20 02:04:39 -07:00

reflection for methods

R=r
DELTA=156  (135 added, 8 deleted, 13 changed)
OCL=31407
CL=31428
This commit is contained in:
Russ Cox 2009-07-09 17:27:49 -07:00
parent 76fef1deec
commit 12ebbe7463
4 changed files with 149 additions and 22 deletions

View File

@ -744,3 +744,37 @@ func TestFunc(t *testing.T) {
t.Errorf("Call returned %d, %d, %d; want 10, 20, 30", i, j, k); t.Errorf("Call returned %d, %d, %d; want 10, 20, 30", i, j, k);
} }
} }
type Point struct {
x, y int;
}
func (p Point) Dist(scale int) int {
return p.x*p.x*scale + p.y*p.y*scale;
}
func TestMethod(t *testing.T) {
// Non-curried method of type.
p := Point{3, 4};
i := reflect.Typeof(p).Method(0).Func.Call([]Value{NewValue(p), NewValue(10)})[0].(*IntValue).Get();
if i != 250 {
t.Errorf("Type Method returned %d; want 250", i);
}
// Curried method of value.
i = NewValue(p).Method(0).Call([]Value{NewValue(10)})[0].(*IntValue).Get();
if i != 250 {
t.Errorf("Value Method returned %d; want 250", i);
}
// Curried method of interface value.
// Have to wrap interface value in a struct to get at it.
// Passing it to NewValue directly would
// access the underlying Point, not the interface.
var s = struct{x interface{Dist(int) int}}{p};
pv := NewValue(s).(*StructValue).Field(0);
i = pv.Method(0).Call([]Value{NewValue(10)})[0].(*IntValue).Get();
if i != 250 {
t.Errorf("Interface Method returned %d; want 250", i);
}
}

View File

@ -229,7 +229,7 @@ type StructType struct {
type Type interface type Type interface
type addr unsafe.Pointer type addr unsafe.Pointer
type FuncValue struct type FuncValue struct
func newFuncValue(typ Type, addr addr) *FuncValue func newFuncValue(typ Type, addr addr, canSet bool) *FuncValue
// Method represents a single method. // Method represents a single method.
type Method struct { type Method struct {
@ -274,10 +274,16 @@ type Type interface {
// NumMethod returns the number of such methods. // NumMethod returns the number of such methods.
Method(int) Method; Method(int) Method;
NumMethod() int; NumMethod() int;
uncommon() *uncommonType;
} }
func toType(i interface{}) Type func toType(i interface{}) Type
func (t *uncommonType) uncommon() *uncommonType {
return t;
}
func (t *uncommonType) Name() (pkgPath string, name string) { func (t *uncommonType) Name() (pkgPath string, name string) {
if t == nil { if t == nil {
return; return;
@ -320,7 +326,7 @@ func (t *uncommonType) Method(i int) (m Method) {
} }
m.Type = toType(*p.typ).(*FuncType); m.Type = toType(*p.typ).(*FuncType);
fn := p.tfn; fn := p.tfn;
m.Func = newFuncValue(m.Type, addr(&fn)); m.Func = newFuncValue(m.Type, addr(&fn), true);
return; return;
} }
@ -409,7 +415,7 @@ func (t *InterfaceType) Method(i int) (m Method) {
if i < 0 || i >= len(t.methods) { if i < 0 || i >= len(t.methods) {
return; return;
} }
p := t.methods[i]; p := &t.methods[i];
m.Name = *p.name; m.Name = *p.name;
if p.pkgPath != nil { if p.pkgPath != nil {
m.PkgPath = *p.pkgPath; m.PkgPath = *p.pkgPath;

View File

@ -10,13 +10,14 @@ import (
"unsafe"; "unsafe";
) )
const ptrSize = uintptr(unsafe.Sizeof((*byte)(nil)))
const cannotSet = "cannot set value obtained via unexported struct field" const cannotSet = "cannot set value obtained via unexported struct field"
// TODO: This will have to go away when // TODO: This will have to go away when
// the new gc goes in. // the new gc goes in.
func memmove(adst, asrc addr, n uintptr) { func memmove(adst, asrc addr, n uintptr) {
var p uintptr; // dummy for sizeof var p uintptr; // dummy for sizeof
const ptrsize = uintptr(unsafe.Sizeof(p));
dst := uintptr(adst); dst := uintptr(adst);
src := uintptr(asrc); src := uintptr(asrc);
switch { switch {
@ -27,14 +28,14 @@ func memmove(adst, asrc addr, n uintptr) {
i--; i--;
*(*byte)(addr(dst+i)) = *(*byte)(addr(src+i)); *(*byte)(addr(dst+i)) = *(*byte)(addr(src+i));
} }
case (n|src|dst) & (ptrsize-1) != 0: case (n|src|dst) & (ptrSize-1) != 0:
// byte copy forward // byte copy forward
for i := uintptr(0); i < n; i++ { for i := uintptr(0); i < n; i++ {
*(*byte)(addr(dst+i)) = *(*byte)(addr(src+i)); *(*byte)(addr(dst+i)) = *(*byte)(addr(src+i));
} }
default: default:
// word copy forward // word copy forward
for i := uintptr(0); i < n; i += ptrsize { for i := uintptr(0); i < n; i += ptrSize {
*(*uintptr)(addr(dst+i)) = *(*uintptr)(addr(src+i)); *(*uintptr)(addr(dst+i)) = *(*uintptr)(addr(src+i));
} }
} }
@ -62,6 +63,12 @@ type Value interface {
// import the "unsafe" package. // import the "unsafe" package.
Addr() uintptr; Addr() uintptr;
// Method returns a FuncValue corresponding to the value's i'th method.
// The arguments to a Call on the returned FuncValue
// should not include a receiver; the FuncValue will use
// the value as the receiver.
Method(i int) *FuncValue;
getAddr() addr; getAddr() addr;
} }
@ -85,6 +92,8 @@ func (v *value) getAddr() addr {
return v.addr; return v.addr;
} }
func (v *value) Method(i int) *FuncValue
type InterfaceValue struct type InterfaceValue struct
type StructValue struct type StructValue struct
@ -710,7 +719,9 @@ func MakeChan(typ *ChanType, buffer int) *ChanValue {
// A FuncValue represents a function value. // A FuncValue represents a function value.
type FuncValue struct { type FuncValue struct {
value value;
first Value;
isInterface bool;
} }
// IsNil returns whether v is a nil function. // IsNil returns whether v is a nil function.
@ -734,6 +745,21 @@ func (v *FuncValue) Set(x *FuncValue) {
*(*uintptr)(v.addr) = *(*uintptr)(x.addr); *(*uintptr)(v.addr) = *(*uintptr)(x.addr);
} }
// Method returns a FuncValue corresponding to v's i'th method.
// The arguments to a Call on the returned FuncValue
// should not include a receiver; the FuncValue will use v
// as the receiver.
func (v *value) Method(i int) *FuncValue {
t := v.Type().uncommon();
if t == nil || i < 0 || i >= len(t.methods) {
return nil;
}
p := &t.methods[i];
fn := p.tfn;
fv := &FuncValue{value: value{toType(*p.typ), addr(&fn), true}, first: v, isInterface: false};
return fv;
}
// implemented in ../pkg/runtime/*/asm.s // implemented in ../pkg/runtime/*/asm.s
func call(fn, arg *byte, n uint32) func call(fn, arg *byte, n uint32)
@ -741,11 +767,15 @@ type tiny struct { b byte }
// Call calls the function v with input parameters in. // Call calls the function v with input parameters in.
// It returns the function's output parameters as Values. // It returns the function's output parameters as Values.
func (v *FuncValue) Call(in []Value) []Value { func (fv *FuncValue) Call(in []Value) []Value {
var structAlign = Typeof((*tiny)(nil)).(*PtrType).Elem().Size(); var structAlign = Typeof((*tiny)(nil)).(*PtrType).Elem().Size();
t := v.Type().(*FuncType); t := fv.Type().(*FuncType);
if len(in) != t.NumIn() { nin := len(in);
if fv.first != nil && !fv.isInterface {
nin++;
}
if nin != t.NumIn() {
panic("FuncValue: wrong argument count"); panic("FuncValue: wrong argument count");
} }
nout := t.NumOut(); nout := t.NumOut();
@ -755,9 +785,12 @@ func (v *FuncValue) Call(in []Value) []Value {
// and probably wrong for gccgo, but so // and probably wrong for gccgo, but so
// is most of this function. // is most of this function.
size := uintptr(0); size := uintptr(0);
for i, v := range in { if fv.isInterface {
tv := v.Type(); // extra word for interface value
typesMustMatch(t.In(i), tv); size += ptrSize;
}
for i := 0; i < nin; i++ {
tv := t.In(i);
a := uintptr(tv.Align()); a := uintptr(tv.Align());
size = (size + a - 1) &^ (a - 1); size = (size + a - 1) &^ (a - 1);
size += tv.Size(); size += tv.Size();
@ -786,8 +819,26 @@ func (v *FuncValue) Call(in []Value) []Value {
// references for us, so maybe this can be treated // references for us, so maybe this can be treated
// like any stack-to-stack copy. // like any stack-to-stack copy.
off := uintptr(0); off := uintptr(0);
delta := 0;
if v := fv.first; v != nil {
// Hard-wired first argument.
if fv.isInterface {
// v is a single uninterpreted word
memmove(addr(ptr), v.getAddr(), ptrSize);
off = ptrSize;
} else {
// v is a real value
tv := v.Type();
typesMustMatch(t.In(0), tv);
n := tv.Size();
memmove(addr(ptr), v.getAddr(), n);
off = n;
delta = 1;
}
}
for i, v := range in { for i, v := range in {
tv := v.Type(); tv := v.Type();
typesMustMatch(t.In(i+delta), tv);
a := uintptr(tv.Align()); a := uintptr(tv.Align());
off = (off + a - 1) &^ (a - 1); off = (off + a - 1) &^ (a - 1);
n := tv.Size(); n := tv.Size();
@ -797,7 +848,7 @@ func (v *FuncValue) Call(in []Value) []Value {
off = (off + structAlign - 1) &^ (structAlign - 1); off = (off + structAlign - 1) &^ (structAlign - 1);
// Call // Call
call(*(**byte)(v.addr), (*byte)(addr(ptr)), uint32(size)); call(*(**byte)(fv.addr), (*byte)(addr(ptr)), uint32(size));
// Copy return values out of args. // Copy return values out of args.
// //
@ -854,6 +905,27 @@ func (v *InterfaceValue) Set(x interface{}) {
// unsafe.SetInterface(v.typ, v.addr, x); // unsafe.SetInterface(v.typ, v.addr, x);
} }
// Method returns a FuncValue corresponding to v's i'th method.
// The arguments to a Call on the returned FuncValue
// should not include a receiver; the FuncValue will use v
// as the receiver.
func (v *InterfaceValue) Method(i int) *FuncValue {
t := v.Type().(*InterfaceType);
if t == nil || i < 0 || i >= len(t.methods) {
return nil;
}
p := &t.methods[i];
// Interface is two words: itable, data.
tab := *(**runtime.Itable)(v.addr);
data := &value{Typeof((*byte)(nil)), addr(uintptr(v.addr)+ptrSize), true};
// Function pointer is at p.perm in the table.
fn := tab.Fn[p.perm];
fv := &FuncValue{value: value{toType(*p.typ), addr(&fn), true}, first: data, isInterface: true};
return fv;
}
/* /*
* map * map
*/ */
@ -1064,7 +1136,18 @@ func NewValue(i interface{}) Value {
return newValue(toType(t), addr(a), true); return newValue(toType(t), addr(a), true);
} }
func newFuncValue(typ Type, addr addr, canSet bool) *FuncValue {
return &FuncValue{value: value{typ, addr, canSet}};
}
func newValue(typ Type, addr addr, canSet bool) Value { func newValue(typ Type, addr addr, canSet bool) Value {
// FuncValue has a different layout;
// it needs a extra space for the fixed receivers.
if t, ok := typ.(*FuncType); ok {
return newFuncValue(typ, addr, canSet);
}
// All values have same memory layout; // All values have same memory layout;
// build once and convert. // build once and convert.
v := &struct{value}{value{typ, addr, canSet}}; v := &struct{value}{value{typ, addr, canSet}};
@ -1088,8 +1171,6 @@ func newValue(typ Type, addr addr, canSet bool) Value {
return (*Float32Value)(v); return (*Float32Value)(v);
case *Float64Type: case *Float64Type:
return (*Float64Value)(v); return (*Float64Value)(v);
case *FuncType:
return (*FuncValue)(v);
case *IntType: case *IntType:
return (*IntValue)(v); return (*IntValue)(v);
case *Int8Type: case *Int8Type:
@ -1130,10 +1211,6 @@ func newValue(typ Type, addr addr, canSet bool) Value {
panicln("newValue", typ.String()); panicln("newValue", typ.String());
} }
func newFuncValue(typ Type, addr addr) *FuncValue {
return newValue(typ, addr, true).(*FuncValue);
}
// MakeZero returns a zero Value for the specified Type. // MakeZero returns a zero Value for the specified Type.
func MakeZero(typ Type) Value { func MakeZero(typ Type) Value {
// TODO: this will have to move into // TODO: this will have to move into
@ -1146,4 +1223,3 @@ func MakeZero(typ Type) Value {
data := make([]uint8, size); data := make([]uint8, size);
return newValue(typ, addr(&data[0]), true); return newValue(typ, addr(&data[0]), true);
} }

View File

@ -190,3 +190,14 @@ type StructType struct {
fields []structField; // sorted by offset fields []structField; // sorted by offset
} }
/*
* Must match iface.c:/Itab and compilers.
*/
type Itable struct {
Itype *Type; // (*tab.inter).(*InterfaceType) is the interface type
Type *Type;
link *Itable;
bad int32;
unused int32;
Fn [100000]uintptr; // bigger than we'll ever see
}