This patch to the Go frontend and libgo implements method values in the
reflect package.  Working with method values and reflect now works
correctly, at least on x86.  This changes the type signature for type
methods in the reflect package to match the gc compiler.  That in turn
required changing the reflect package to mark method values with a new
flag, as previously they were detected by the type signature.  The
MakeFunc support needed to create a function that takes a value and
passes a pointer to the method, since all methods take pointers.  It
also needed to create a function that holds a receiver value.  And the
recover code needed to handle these new cases.  Bootstrapped and ran Go
testsuite on x86_64-unknown-linux-gnu.  Committed to mainline and 4.8
branch.

Ian

diff -r e165d3ccf1e4 go/types.cc
--- a/go/types.cc	Wed Dec 11 15:38:00 2013 -0800
+++ b/go/types.cc	Wed Dec 11 16:57:53 2013 -0800
@@ -2261,26 +2261,9 @@
 
   ++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 @@
   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 -r e165d3ccf1e4 go/types.h
--- a/go/types.h	Wed Dec 11 15:38:00 2013 -0800
+++ b/go/types.h	Wed Dec 11 16:57:53 2013 -0800
@@ -1797,6 +1797,12 @@
   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 -r e165d3ccf1e4 libgo/go/reflect/all_test.go
--- a/libgo/go/reflect/all_test.go	Wed Dec 11 15:38:00 2013 -0800
+++ b/libgo/go/reflect/all_test.go	Wed Dec 11 16:57:53 2013 -0800
@@ -1631,9 +1631,13 @@
 	}
 }
 
-/* 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 @@
 	}
 }
 
-*/
-
 // Reflect version of $GOROOT/test/method5.go
 
 // Concrete types implementing M method.
@@ -1807,14 +1809,18 @@
 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 @@
 			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 -r e165d3ccf1e4 libgo/go/reflect/makefunc.go
--- a/libgo/go/reflect/makefunc.go	Wed Dec 11 15:38:00 2013 -0800
+++ b/libgo/go/reflect/makefunc.go	Wed Dec 11 16:57:53 2013 -0800
@@ -17,6 +17,11 @@
 	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 @@
 	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 @@
 		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)
 
-	panic("reflect makeMethodValue not implemented")
+	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")
+	}
+
+	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 -r e165d3ccf1e4 libgo/go/reflect/makefuncgo_386.go
--- a/libgo/go/reflect/makefuncgo_386.go	Wed Dec 11 15:38:00 2013 -0800
+++ b/libgo/go/reflect/makefuncgo_386.go	Wed Dec 11 16:57:53 2013 -0800
@@ -80,7 +80,7 @@
 
 	// 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 -r e165d3ccf1e4 libgo/go/reflect/makefuncgo_amd64.go
--- a/libgo/go/reflect/makefuncgo_amd64.go	Wed Dec 11 15:38:00 2013 -0800
+++ b/libgo/go/reflect/makefuncgo_amd64.go	Wed Dec 11 16:57:53 2013 -0800
@@ -319,7 +319,7 @@
 	// 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 -r e165d3ccf1e4 libgo/go/reflect/type.go
--- a/libgo/go/reflect/type.go	Wed Dec 11 15:38:00 2013 -0800
+++ b/libgo/go/reflect/type.go	Wed Dec 11 16:57:53 2013 -0800
@@ -517,7 +517,7 @@
 	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 -r e165d3ccf1e4 libgo/go/reflect/value.go
--- a/libgo/go/reflect/value.go	Wed Dec 11 15:38:00 2013 -0800
+++ b/libgo/go/reflect/value.go	Wed Dec 11 16:57:53 2013 -0800
@@ -98,6 +98,7 @@
 	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 @@
 	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 @@
 	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 @@
 		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 @@
 			// 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 -r e165d3ccf1e4 libgo/runtime/go-recover.c
--- a/libgo/runtime/go-recover.c	Wed Dec 11 15:38:00 2013 -0800
+++ b/libgo/runtime/go-recover.c	Wed Dec 11 16:57:53 2013 -0800
@@ -84,6 +84,11 @@
   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;
 }
 

Reply via email to