]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
compiler, reflect, runtime: Implement method values in reflect.
authorIan Lance Taylor <ian@gcc.gnu.org>
Thu, 12 Dec 2013 01:08:52 +0000 (01:08 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Thu, 12 Dec 2013 01:08:52 +0000 (01:08 +0000)
From-SVN: r205913

gcc/go/gofrontend/types.cc
gcc/go/gofrontend/types.h
libgo/go/reflect/all_test.go
libgo/go/reflect/makefunc.go
libgo/go/reflect/makefuncgo_386.go
libgo/go/reflect/makefuncgo_amd64.go
libgo/go/reflect/type.go
libgo/go/reflect/value.go
libgo/runtime/go-recover.c

index 8c5d038a0b19080da570933fef517dc1f221fc39..33c23e56092b53aef7326ae5d984a3518f33a360 100644 (file)
@@ -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.
 
index 1bd8ce6cf8f3c8f71abf18e93e2d55b6a2fb2b86..9f9659161319f2a7083b53369570504c642af052 100644 (file)
@@ -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.
index 918adce887f4c903119779d0e5f11295a2b181c0..f9700ce2fb5d9bb8b3f8877f2777b812c5699635 100644 (file)
@@ -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()
index 505c543a082552b03293ef3883c2e98fb1ad85cf..935a3d3be02c62608bf1a21a98017f600ae40d00 100644 (file)
@@ -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)
+               }
+       }
 }
index 7559af6f6acc6f6a9ed5906c10d72950d09e6f45..71957b61b3dbc9cdbf3be58180fc5b47c2fc143f 100644 (file)
@@ -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")
index ecc50a42520aeaf977ec2ff326974cb9b74a0424..42fe03a9310894d2fbd0bebf745894d964b7aba1 100644 (file)
@@ -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")
index aaac2c3487ffca7e7a8a182633ba9b5d609bc701..a930d64607a6942c93868788cfc130b4bc033e51 100644 (file)
@@ -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
 }
index 216ee3f1ca202a2c3da09fbb2696c8cc5f0e3b5c..fc7dfae23d2ce9043b49cb88bcf0cc03c48f8718 100644 (file)
@@ -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
index 6cef2660ea4d870fb7b5c4ebc9046f5ad8feebc8..ceb9b572582d9680d660f7906fdc72e406130eb8 100644 (file)
@@ -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;
 }