From 547a4168791b7ae9543215aba3ca6184c31bb37e Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 12 Dec 2013 01:08:52 +0000 Subject: [PATCH] compiler, reflect, runtime: Implement method values in reflect. From-SVN: r205913 --- gcc/go/gofrontend/types.cc | 49 ++++++++------- gcc/go/gofrontend/types.h | 6 ++ libgo/go/reflect/all_test.go | 20 ++++--- libgo/go/reflect/makefunc.go | 90 +++++++++++++++++++++++++++- libgo/go/reflect/makefuncgo_386.go | 2 +- libgo/go/reflect/makefuncgo_amd64.go | 2 +- libgo/go/reflect/type.go | 2 +- libgo/go/reflect/value.go | 43 ++++--------- 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 8c5d038a0b19..33c23e56092b 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 1bd8ce6cf8f3..9f9659161319 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 918adce887f4..f9700ce2fb5d 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 505c543a0825..935a3d3be02c 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<>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)< 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 6cef2660ea4d..ceb9b572582d 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; } -- 2.39.2