diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-12-12 01:08:52 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-12-12 01:08:52 +0000 |
commit | a4a14eb29309a20c3b21ed4fc79d9f29abf162bc (patch) | |
tree | 6d22f3392a75c3aaa5aa1da655668fca943b1d10 | |
parent | dc2977939ca7d738e1330c11deae1c97602a36ae (diff) | |
download | gcc-a4a14eb29309a20c3b21ed4fc79d9f29abf162bc.tar.gz |
compiler, reflect, runtime: Implement method values in reflect.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@205913 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r-- | gcc/go/gofrontend/types.cc | 49 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 6 | ||||
-rw-r--r-- | libgo/go/reflect/all_test.go | 20 | ||||
-rw-r--r-- | libgo/go/reflect/makefunc.go | 90 | ||||
-rw-r--r-- | libgo/go/reflect/makefuncgo_386.go | 2 | ||||
-rw-r--r-- | libgo/go/reflect/makefuncgo_amd64.go | 2 | ||||
-rw-r--r-- | libgo/go/reflect/type.go | 2 | ||||
-rw-r--r-- | libgo/go/reflect/value.go | 43 | ||||
-rw-r--r-- | libgo/runtime/go-recover.c | 5 |
9 files changed, 155 insertions, 64 deletions
diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 8c5d038a0b1..33c23e56092 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -2261,26 +2261,9 @@ Type::method_constructor(Gogo*, Type* method_type, ++p; go_assert(p->is_field_name("typ")); - if (!only_value_methods && m->is_value_method()) - { - // This is a value method on a pointer type. Change the type of - // the method to use a pointer receiver. The implementation - // always uses a pointer receiver anyhow. - Type* rtype = mtype->receiver()->type(); - Type* prtype = Type::make_pointer_type(rtype); - Typed_identifier* receiver = - new Typed_identifier(mtype->receiver()->name(), prtype, - mtype->receiver()->location()); - mtype = Type::make_function_type(receiver, - (mtype->parameters() == NULL - ? NULL - : mtype->parameters()->copy()), - (mtype->results() == NULL - ? NULL - : mtype->results()->copy()), - mtype->location()); - } - vals->push_back(Expression::make_type_descriptor(mtype, bloc)); + bool want_pointer_receiver = !only_value_methods && m->is_value_method(); + nonmethod_type = mtype->copy_with_receiver_as_param(want_pointer_receiver); + vals->push_back(Expression::make_type_descriptor(nonmethod_type, bloc)); ++p; go_assert(p->is_field_name("tfn")); @@ -4008,6 +3991,32 @@ Function_type::copy_with_receiver(Type* receiver_type) const return ret; } +// Make a copy of a function type with the receiver as the first +// parameter. + +Function_type* +Function_type::copy_with_receiver_as_param(bool want_pointer_receiver) const +{ + go_assert(this->is_method()); + Typed_identifier_list* new_params = new Typed_identifier_list(); + Type* rtype = this->receiver_->type(); + if (want_pointer_receiver) + rtype = Type::make_pointer_type(rtype); + Typed_identifier receiver(this->receiver_->name(), rtype, + this->receiver_->location()); + new_params->push_back(receiver); + const Typed_identifier_list* orig_params = this->parameters_; + if (orig_params != NULL && !orig_params->empty()) + { + for (Typed_identifier_list::const_iterator p = orig_params->begin(); + p != orig_params->end(); + ++p) + new_params->push_back(*p); + } + return Type::make_function_type(NULL, new_params, this->results_, + this->location_); +} + // Make a copy of a function type ignoring any receiver and adding a // closure parameter. diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 1bd8ce6cf8f..9f965916131 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -1797,6 +1797,12 @@ class Function_type : public Type Function_type* copy_with_receiver(Type*) const; + // Return a copy of this type with the receiver treated as the first + // parameter. If WANT_POINTER_RECEIVER is true, the receiver is + // forced to be a pointer. + Function_type* + copy_with_receiver_as_param(bool want_pointer_receiver) const; + // Return a copy of this type ignoring any receiver and using dummy // names for all parameters. This is used for thunks for method // values. diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 918adce887f..f9700ce2fb5 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -1631,9 +1631,13 @@ func TestMethod(t *testing.T) { } } -/* Not yet implemented for gccgo - func TestMethodValue(t *testing.T) { + switch runtime.GOARCH { + case "amd64", "386": + default: + t.Skip("reflect method values not implemented for " + runtime.GOARCH) + } + p := Point{3, 4} var i int64 @@ -1721,8 +1725,6 @@ func TestMethodValue(t *testing.T) { } } -*/ - // Reflect version of $GOROOT/test/method5.go // Concrete types implementing M method. @@ -1807,14 +1809,18 @@ type Tm4 struct { func (t4 Tm4) M(x int, b byte) (byte, int) { return b, x + 40 } func TestMethod5(t *testing.T) { - /* Not yet used for gccgo + switch runtime.GOARCH { + case "amd64", "386": + default: + t.Skip("reflect method values not implemented for " + runtime.GOARCH) + } + 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))}) @@ -1824,9 +1830,7 @@ func TestMethod5(t *testing.T) { t.Errorf("direct %s.M(1000, 99) = %v, %v, want 99, %v", name, b, x, 1000+inc) } - /* Not yet implemented for gccgo CheckF(name+".M", i.Method(0).Interface().(func(int, byte) (byte, int)), inc) - */ } var TinterType = TypeOf(new(Tinter)).Elem() diff --git a/libgo/go/reflect/makefunc.go b/libgo/go/reflect/makefunc.go index 505c543a082..935a3d3be02 100644 --- a/libgo/go/reflect/makefunc.go +++ b/libgo/go/reflect/makefunc.go @@ -17,6 +17,11 @@ type makeFuncImpl struct { code uintptr typ *funcType fn func([]Value) []Value + + // For gccgo we use the same entry point for functions and for + // method values. + method int + rcvr Value } // MakeFunc returns a new function of the given Type @@ -61,7 +66,7 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { dummy := makeFuncStub code := **(**uintptr)(unsafe.Pointer(&dummy)) - impl := &makeFuncImpl{code: code, typ: ftyp, fn: fn} + impl := &makeFuncImpl{code: code, typ: ftyp, fn: fn, method: -1} return Value{t, unsafe.Pointer(&impl), flag(Func<<flagKindShift) | flagIndir} } @@ -85,15 +90,94 @@ func makeMethodValue(op string, v Value) Value { panic("reflect: internal error: invalid use of makePartialFunc") } + switch runtime.GOARCH { + case "amd64", "386": + default: + panic("reflect.makeMethodValue not implemented for " + runtime.GOARCH) + } + // 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. + ft := 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 := makeFuncStub + code := **(**uintptr)(unsafe.Pointer(&dummy)) + // 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, rcvr, int(v.flag)>>flagMethodShift) + t, _, _ := methodReceiver(op, rcvr, int(v.flag)>>flagMethodShift) + + fv := &makeFuncImpl{ + code: code, + typ: (*funcType)(unsafe.Pointer(t)), + method: int(v.flag) >> flagMethodShift, + rcvr: rcvr, + } + + return Value{ft, unsafe.Pointer(&fv), v.flag&flagRO | flag(Func)<<flagKindShift | flagIndir} +} + +// makeValueMethod takes a method function and returns a function that +// takes a value receiver and calls the real method with a pointer to +// it. +func makeValueMethod(v Value) Value { + typ := v.typ + if typ.Kind() != Func { + panic("reflect: call of makeValueMethod with non-Func type") + } + if v.flag&flagMethodFn == 0 { + panic("reflect: call of makeValueMethod with non-MethodFn") + } - panic("reflect makeMethodValue not implemented") + switch runtime.GOARCH { + case "amd64", "386": + default: + panic("reflect.makeValueMethod not implemented for " + runtime.GOARCH) + } + + t := typ.common() + ftyp := (*funcType)(unsafe.Pointer(t)) + + // 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)) + + impl := &makeFuncImpl{ + code: code, + typ: ftyp, + method: -2, + rcvr: v, + } + + return Value{t, unsafe.Pointer(&impl), flag(Func<<flagKindShift) | flagIndir} +} + +// Call the function represented by a makeFuncImpl. +func (c *makeFuncImpl) call(in []Value) []Value { + if c.method == -1 { + return c.fn(in) + } else if c.method == -2 { + if c.typ.IsVariadic() { + return c.rcvr.CallSlice(in) + } else { + return c.rcvr.Call(in) + } + } else { + m := c.rcvr.Method(c.method) + if c.typ.IsVariadic() { + return m.CallSlice(in) + } else { + return m.Call(in) + } + } } diff --git a/libgo/go/reflect/makefuncgo_386.go b/libgo/go/reflect/makefuncgo_386.go index 7559af6f6ac..71957b61b3d 100644 --- a/libgo/go/reflect/makefuncgo_386.go +++ b/libgo/go/reflect/makefuncgo_386.go @@ -80,7 +80,7 @@ func MakeFuncStubGo(regs *i386Regs, c *makeFuncImpl) { // Call the real function. - out := c.fn(in) + out := c.call(in) if len(out) != len(ftyp.out) { panic("reflect: wrong return count from function created by MakeFunc") diff --git a/libgo/go/reflect/makefuncgo_amd64.go b/libgo/go/reflect/makefuncgo_amd64.go index ecc50a42520..42fe03a9310 100644 --- a/libgo/go/reflect/makefuncgo_amd64.go +++ b/libgo/go/reflect/makefuncgo_amd64.go @@ -319,7 +319,7 @@ argloop: // All the real arguments have been found and turned into // Value's. Call the real function. - out := c.fn(in) + out := c.call(in) if len(out) != len(ftyp.out) { panic("reflect: wrong return count from function created by MakeFunc") diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index aaac2c3487f..a930d64607a 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -517,7 +517,7 @@ func (t *uncommonType) Method(i int) (m Method) { m.Type = toType(mt) x := new(unsafe.Pointer) *x = unsafe.Pointer(&p.tfn) - m.Func = Value{mt, unsafe.Pointer(x), fl | flagIndir} + m.Func = Value{mt, unsafe.Pointer(x), fl | flagIndir | flagMethodFn} m.Index = i return } diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index 216ee3f1ca2..fc7dfae23d2 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -98,6 +98,7 @@ const ( flagIndir flagAddr flagMethod + flagMethodFn // gccgo: first fn parameter is always pointer flagKindShift = iota flagKindWidth = 5 // there are 27 kinds flagKindMask flag = 1<<flagKindWidth - 1 @@ -433,7 +434,7 @@ func (v Value) call(op string, in []Value) []Value { if v.flag&flagMethod != 0 { nin++ } - firstPointer := len(in) > 0 && t.In(0).Kind() != Ptr && v.flag&flagMethod == 0 && isMethod(v.typ) + firstPointer := len(in) > 0 && t.In(0).Kind() != Ptr && v.flag&flagMethodFn != 0 params := make([]unsafe.Pointer, nin) off := 0 if v.flag&flagMethod != 0 { @@ -484,33 +485,6 @@ func (v Value) call(op string, in []Value) []Value { return ret } -// gccgo specific test to see if typ is a method. We can tell by -// looking at the string to see if there is a receiver. We need this -// because for gccgo all methods take pointer receivers. -func isMethod(t *rtype) bool { - if Kind(t.kind) != Func { - return false - } - s := *t.string - parens := 0 - params := 0 - sawRet := false - for i, c := range s { - if c == '(' { - if parens == 0 { - params++ - } - parens++ - } else if c == ')' { - parens-- - } else if parens == 0 && c == ' ' && s[i+1] != '(' && !sawRet { - params++ - sawRet = true - } - } - return params > 2 -} - // methodReceiver returns information about the receiver // described by v. The Value v may or may not have the // flagMethod bit set, so the kind cached in v.flag should @@ -873,6 +847,16 @@ func valueInterface(v Value, safe bool) interface{} { v = makeMethodValue("Interface", v) } + if v.flag&flagMethodFn != 0 { + if v.typ.Kind() != Func { + panic("reflect: MethodFn of non-Func") + } + ft := (*funcType)(unsafe.Pointer(v.typ)) + if ft.in[0].Kind() != Ptr { + v = makeValueMethod(v) + } + } + k := v.kind() if k == Interface { // Special case: return the element inside the interface. @@ -1187,8 +1171,7 @@ func (v Value) Pointer() uintptr { // created via reflect have the same underlying code pointer, // so their Pointers are equal. The function used here must // match the one used in makeMethodValue. - // This is not properly implemented for gccgo. - f := Zero + f := makeFuncStub return **(**uintptr)(unsafe.Pointer(&f)) } p := v.val diff --git a/libgo/runtime/go-recover.c b/libgo/runtime/go-recover.c index 6cef2660ea4..ceb9b572582 100644 --- a/libgo/runtime/go-recover.c +++ b/libgo/runtime/go-recover.c @@ -84,6 +84,11 @@ __go_can_recover (const void *retaddr) if (name[0] == 'f' && name[1] == 'f' && name[2] == 'i' && name[3] == '_') return 1; + /* We may also be called by reflect.makeFuncImpl.call, for a + function created by reflect.MakeFunc. */ + if (__builtin_strstr ((const char *) name, "makeFuncImpl") != NULL) + return 1; + return 0; } |