1
0
mirror of https://github.com/golang/go synced 2024-10-02 12:18:33 -06:00

reflection for functions

add channel send type check (thanks austin).
fix type mismatch message.

R=r
DELTA=241  (225 added, 5 deleted, 11 changed)
OCL=31370
CL=31375
This commit is contained in:
Russ Cox 2009-07-08 18:16:09 -07:00
parent a68b1da3cc
commit bba278a43b
8 changed files with 235 additions and 15 deletions

View File

@ -725,3 +725,22 @@ func TestChan(t *testing.T) {
}
}
// Difficult test for function call because of
// implicit padding between arguments.
func dummy(b byte, c int, d byte) (i byte, j int, k byte){
return b, c, d;
}
func TestFunc(t *testing.T) {
ret := NewValue(dummy).(*FuncValue).Call([]Value{NewValue(byte(10)), NewValue(20), NewValue(byte(30))});
if len(ret) != 3 {
t.Fatalf("Call returned %d values, want 3", len(ret));
}
i := ret[0].(*Uint8Value).Get();
j := ret[1].(*IntValue).Get();
k := ret[2].(*Uint8Value).Get();
if i != 10 || j != 20 || k != 30 {
t.Errorf("Call returned %d, %d, %d; want 10, 20, 30", i, j, k);
}
}

View File

@ -548,4 +548,9 @@ type ArrayOrSliceType interface {
Elem() Type;
}
// Typeof returns the reflection Type of the value in the interface{}.
func Typeof(i interface{}) Type {
return toType(unsafe.Typeof(i));
}

View File

@ -14,9 +14,11 @@ const cannotSet = "cannot set value obtained via unexported struct field"
// TODO: This will have to go away when
// the new gc goes in.
func memmove(dst, src, n uintptr) {
func memmove(adst, asrc addr, n uintptr) {
var p uintptr; // dummy for sizeof
const ptrsize = uintptr(unsafe.Sizeof(p));
dst := uintptr(adst);
src := uintptr(asrc);
switch {
case src < dst && src+n > dst:
// byte copy backward
@ -424,7 +426,7 @@ func (v *UnsafePointerValue) Set(x unsafe.Pointer) {
func typesMustMatch(t1, t2 reflect.Type) {
if t1 != t2 {
panicln("type mismatch:", t1, "!=", t2);
panicln("type mismatch:", t1.String(), "!=", t2.String());
}
}
@ -456,7 +458,7 @@ func ArrayCopy(dst, src ArrayOrSliceValue) int {
if xn := src.Len(); n > xn {
n = xn;
}
memmove(uintptr(dst.addr()), uintptr(src.addr()), uintptr(n) * de.Size());
memmove(dst.addr(), src.addr(), uintptr(n) * de.Size());
return n;
}
@ -642,6 +644,7 @@ func (v *ChanValue) send(x Value, b *bool) {
if t.Dir() & SendDir == 0{
panic("send on recv-only channel");
}
typesMustMatch(t.Elem(), x.Type());
ch := *(**byte)(v.addr);
chansend(ch, (*byte)(x.getAddr()), b);
}
@ -731,12 +734,88 @@ func (v *FuncValue) Set(x *FuncValue) {
*(*uintptr)(v.addr) = *(*uintptr)(x.addr);
}
// implemented in ../pkg/runtime/*/asm.s
func call(fn, arg *byte, n uint32)
type tiny struct { b byte }
// Call calls the function v with input parameters in.
// It returns the function's output parameters as Values.
func (v *FuncValue) Call(in []Value) []Value {
panic("unimplemented: function Call");
}
var structAlign = Typeof((*tiny)(nil)).(*PtrType).Elem().Size();
t := v.Type().(*FuncType);
if len(in) != t.NumIn() {
panic("FuncValue: wrong argument count");
}
nout := t.NumOut();
// Compute arg size & allocate.
// This computation is 6g/8g-dependent
// and probably wrong for gccgo, but so
// is most of this function.
size := uintptr(0);
for i, v := range in {
tv := v.Type();
typesMustMatch(t.In(i), tv);
a := uintptr(tv.Align());
size = (size + a - 1) &^ (a - 1);
size += tv.Size();
}
size = (size + structAlign - 1) &^ (structAlign - 1);
for i := 0; i < nout; i++ {
tv := t.Out(i);
a := uintptr(tv.Align());
size = (size + a - 1) &^ (a - 1);
size += tv.Size();
}
// size must be > 0 in order for &args[0] to be valid.
// the argument copying is going to round it up to
// a multiple of 8 anyway, so make it 8 to begin with.
if size < 8 {
size = 8;
}
args := make([]byte, size);
ptr := uintptr(unsafe.Pointer(&args[0]));
// Copy into args.
//
// TODO(rsc): revisit when reference counting happens.
// This one may be fine. The values are holding up the
// references for us, so maybe this can be treated
// like any stack-to-stack copy.
off := uintptr(0);
for i, v := range in {
tv := v.Type();
a := uintptr(tv.Align());
off = (off + a - 1) &^ (a - 1);
n := tv.Size();
memmove(addr(ptr+off), v.getAddr(), n);
off += n;
}
off = (off + structAlign - 1) &^ (structAlign - 1);
// Call
call(*(**byte)(v.addr), (*byte)(addr(ptr)), uint32(size));
// Copy return values out of args.
//
// TODO(rsc): revisit like above.
ret := make([]Value, nout);
for i := 0; i < nout; i++ {
tv := t.Out(i);
a := uintptr(tv.Align());
off = (off + a - 1) &^ (a - 1);
v := MakeZero(tv);
n := tv.Size();
memmove(v.getAddr(), addr(ptr+off), n);
ret[i] = v;
off += n;
}
return ret;
}
/*
* interface
@ -953,7 +1032,7 @@ func (v *StructValue) Set(x *StructValue) {
panic(cannotSet);
}
typesMustMatch(v.typ, x.typ);
memmove(uintptr(v.addr), uintptr(x.addr), v.typ.Size());
memmove(v.addr, x.addr, v.typ.Size());
}
// Field returns the i'th field of the struct.
@ -975,11 +1054,6 @@ func (v *StructValue) NumField() int {
* constructors
*/
// Typeof returns the reflection Type of the value in the interface{}.
func Typeof(i interface{}) Type {
return toType(unsafe.Typeof(i));
}
// NewValue returns a new Value initialized to the concrete value
// stored in the interface i. NewValue(nil) returns nil.
func NewValue(i interface{}) Value {
@ -1072,3 +1146,4 @@ func MakeZero(typ Type) Value {
data := make([]uint8, size);
return newValue(typ, addr(&data[0]), true);
}

View File

@ -152,6 +152,7 @@ TEXT sys·morestack(SB),7,$0
MOVL DI, (m_morebuf+gobuf_pc)(BX)
LEAL 8(SP), CX // f's caller's SP
MOVL CX, (m_morebuf+gobuf_sp)(BX)
MOVL CX, (m_morefp)(BX)
MOVL g, SI
MOVL SI, (m_morebuf+gobuf_g)(BX)
@ -167,6 +168,47 @@ TEXT sys·morestack(SB),7,$0
MOVL $0, 0x1003 // crash if newstack returns
RET
// Called from reflection library. Mimics morestack,
// reuses stack growth code to create a frame
// with the desired args running the desired function.
//
// func call(fn *byte, arg *byte, argsize uint32).
TEXT reflect·call(SB), 7, $0
MOVL m, BX
// Save our caller's state as the PC and SP to
// restore when returning from f.
MOVL 0(SP), AX // our caller's PC
MOVL AX, (m_morebuf+gobuf_pc)(BX)
LEAL 4(SP), AX // our caller's SP
MOVL AX, (m_morebuf+gobuf_sp)(BX)
MOVL g, AX
MOVL AX, (m_morebuf+gobuf_g)(BX)
// Set up morestack arguments to call f on a new stack.
// We set f's frame size to zero, meaning
// allocate a standard sized stack segment.
// If it turns out that f needs a larger frame than this,
// f's usual stack growth prolog will allocate
// a new segment (and recopy the arguments).
MOVL 4(SP), AX // fn
MOVL 8(SP), DX // arg frame
MOVL 12(SP), CX // arg size
MOVL AX, m_morepc(BX) // f's PC
MOVL DX, m_morefp(BX) // argument frame pointer
MOVL CX, m_moreargs(BX) // f's argument size
MOVL $0, m_moreframe(BX) // f's frame size
// Call newstack on m's scheduling stack.
MOVL m_g0(BX), BP
MOVL BP, g
MOVL (m_sched+gobuf_sp)(BX), SP
CALL newstack(SB)
MOVL $0, 0x1103 // crash if newstack returns
RET
// Return point when leaving stack.
TEXT sys·lessstack(SB), 7, $0
// Save return value in m->cret

View File

@ -115,6 +115,7 @@ TEXT sys·morestack(SB),7,$0
MOVQ AX, (m_morebuf+gobuf_pc)(m)
LEAQ 16(SP), AX // f's caller's SP
MOVQ AX, (m_morebuf+gobuf_sp)(m)
MOVQ AX, (m_morefp)(m)
MOVQ g, (m_morebuf+gobuf_g)(m)
// Set m->morepc to f's PC.
@ -128,6 +129,42 @@ TEXT sys·morestack(SB),7,$0
MOVQ $0, 0x1003 // crash if newstack returns
RET
// Called from reflection library. Mimics morestack,
// reuses stack growth code to create a frame
// with the desired args running the desired function.
//
// func call(fn *byte, arg *byte, argsize uint32).
TEXT reflect·call(SB), 7, $0
// Save our caller's state as the PC and SP to
// restore when returning from f.
MOVQ 0(SP), AX // our caller's PC
MOVQ AX, (m_morebuf+gobuf_pc)(m)
LEAQ 8(SP), AX // our caller's SP
MOVQ AX, (m_morebuf+gobuf_sp)(m)
MOVQ g, (m_morebuf+gobuf_g)(m)
// Set up morestack arguments to call f on a new stack.
// We set f's frame size to zero, meaning
// allocate a standard sized stack segment.
// If it turns out that f needs a larger frame than this,
// f's usual stack growth prolog will allocate
// a new segment (and recopy the arguments).
MOVQ 8(SP), AX // fn
MOVQ 16(SP), BX // arg frame
MOVL 24(SP), CX // arg size
MOVQ AX, m_morepc(m) // f's PC
MOVQ BX, m_morefp(m) // argument frame pointer
MOVL CX, m_moreargs(m) // f's argument size
MOVL $0, m_moreframe(m) // f's frame size
// Call newstack on m's scheduling stack.
MOVQ m_g0(m), g
MOVQ (m_sched+gobuf_sp)(m), SP
CALL newstack(SB)
MOVQ $0, 0x1103 // crash if newstack returns
RET
// Return point when leaving stack.
TEXT sys·lessstack(SB), 7, $0
// Save return value in m->cret

View File

@ -145,6 +145,7 @@ TEXT sys·morestack(SB),7,$-4
// Set m->morebuf to f's caller.
MOVW R3, (m_morebuf+gobuf_pc)(m) // f's caller's PC
MOVW SP, (m_morebuf+gobuf_sp)(m) // f's caller's SP
MOVW SP, m_morefp(m) // f's caller's SP
MOVW g, (m_morebuf+gobuf_g)(m)
MOVW R0, (m_morebuf+gobuf_r0)(m)
@ -156,6 +157,40 @@ TEXT sys·morestack(SB),7,$-4
MOVW (m_sched+gobuf_sp)(m), SP
B newstack(SB)
// Called from reflection library. Mimics morestack,
// reuses stack growth code to create a frame
// with the desired args running the desired function.
//
// func call(fn *byte, arg *byte, argsize uint32).
TEXT reflect·call(SB), 7, $-4
// Save our caller's state as the PC and SP to
// restore when returning from f.
MOVW LR, (m_morebuf+gobuf_pc)(m) // our caller's PC
MOVW SP, (m_morebuf+gobuf_sp)(m) // our caller's SP
MOVW R0, (m_morebuf+gobuf_r0)(m)
MOVQ g, (m_morebuf+gobuf_g)(m)
// Set up morestack arguments to call f on a new stack.
// We set f's frame size to zero, meaning
// allocate a standard sized stack segment.
// If it turns out that f needs a larger frame than this,
// f's usual stack growth prolog will allocate
// a new segment (and recopy the arguments).
MOVW 4(SP), R0 // fn
MOVW 8(SP), R1 // arg frame
MOVW 12(SP), R2 // arg size
MOVW R0, m_morepc(m) // f's PC
MOVW R1, m_morefp(m) // argument frame pointer
MOVW R2, m_moreargs(m) // f's argument size
MOVW $0, R3
MOVW R3, m_moreframe(m) // f's frame size
// Call newstack on m's scheduling stack.
MOVW m_g0(m), g
MOVW (m_sched+gobuf_sp)(m), SP
B newstack(SB)
// Return point when leaving stack.
// using frame size $-4 means do not save LR on stack.
TEXT sys·lessstack(SB), 7, $-4

View File

@ -619,7 +619,7 @@ oldstack(void)
args = old.args;
if(args > 0) {
sp -= args;
mcpy(top->gobuf.sp, sp, args);
mcpy(top->fp, sp, args);
}
stackfree((byte*)g1->stackguard - StackGuard);
@ -650,13 +650,14 @@ newstack(void)
frame += 1024; // for more functions, Stktop.
stk = stackalloc(frame);
//printf("newstack frame=%d args=%d morepc=%p gobuf=%p, %p newstk=%p\n", frame, args, m->morepc, g->sched.pc, g->sched.sp, stk);
//printf("newstack frame=%d args=%d morepc=%p morefp=%p gobuf=%p, %p newstk=%p\n", frame, args, m->morepc, m->morefp, g->sched.pc, g->sched.sp, stk);
g1 = m->curg;
top = (Stktop*)(stk+frame-sizeof(*top));
top->stackbase = g1->stackbase;
top->stackguard = g1->stackguard;
top->gobuf = m->morebuf;
top->fp = m->morefp;
top->args = args;
g1->stackbase = (byte*)top;
@ -665,7 +666,7 @@ newstack(void)
sp = (byte*)top;
if(args > 0) {
sp -= args;
mcpy(sp, top->gobuf.sp, args);
mcpy(sp, m->morefp, args);
}
// Continue as if lessstack had just called m->morepc

View File

@ -171,6 +171,7 @@ struct M
// The offsets of these fields are known to (hard-coded in) libmach.
G* g0; // goroutine with scheduling stack
void (*morepc)(void);
void* morefp; // frame pointer for more stack
Gobuf morebuf; // gobuf arg to morestack
// Fields not known to debuggers.
@ -200,6 +201,11 @@ struct Stktop
uint8* stackbase;
Gobuf gobuf;
uint32 args;
// Frame pointer: where args start in old frame.
// fp == gobuf.sp except in the case of a reflected
// function call, which uses an off-stack argument frame.
uint8* fp;
};
struct Alg
{