reflect/protoreflect: add Descriptor specific methods

Added methods:
	Enum.Descriptor
	Message.Descriptor
	EnumType.Descriptor
	MessageType.Descriptor
	ExtensionType.Descriptor
	Message.New

All functionality is switched over to use those methods instead of
implicitly relying on the fact that {Enum,Message}Type implicitly
implement the associated descriptor interface.

This CL does not yet remove {Enum,Message}.Type or prevent
{Enum,Message,Extension}Type from implementating a descriptor.
That is a subsequent CL.

The Message.New method is also added to replace functionality
that will be lost when the Type methods are removed.

Change-Id: I7fefde1673bbd40bfdac489aca05cec9a6c98eb1
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/174918
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/internal/impl/export.go b/internal/impl/export.go
index 5dae010..0b53d78 100644
--- a/internal/impl/export.go
+++ b/internal/impl/export.go
@@ -5,10 +5,10 @@
 package impl
 
 import (
+	"reflect"
 	"strconv"
 
 	"github.com/golang/protobuf/v2/encoding/textpb"
-	ptype "github.com/golang/protobuf/v2/internal/prototype"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 )
 
@@ -16,11 +16,12 @@
 // functions that we do not want to appear in godoc.
 type Export struct{}
 
+// enum is any enum type generated by protoc-gen-go
+// and must be a named int32 type.
+type enum = interface{}
+
 // EnumOf returns the protoreflect.Enum interface over e.
-// If e already implements proto.Enum, then it directly calls the
-// ProtoReflect method, otherwise it wraps the v1 enum to implement
-// the v2 reflective interface.
-func (Export) EnumOf(e interface{}) pref.Enum {
+func (Export) EnumOf(e enum) pref.Enum {
 	if ev, ok := e.(pref.Enum); ok {
 		return ev
 	}
@@ -28,16 +29,34 @@
 }
 
 // EnumTypeOf returns the protoreflect.EnumType for e.
-// If e already implements proto.Enum, then it obtains the type by directly
-// calling the ProtoReflect.Type method, otherwise it derives an enum type
-// from the v1 named int32 type.
-func (Export) EnumTypeOf(e interface{}) pref.EnumType {
+func (Export) EnumTypeOf(e enum) pref.EnumType {
 	if ev, ok := e.(pref.Enum); ok {
-		return ev.Type()
+		return &enumType{ev.Descriptor(), reflect.TypeOf(e)}
 	}
 	return legacyWrapper.EnumTypeOf(e)
 }
 
+// TODO: This needs to be centralized in a package.
+type enumType struct {
+	// TODO: Remove me as an embedded field.
+	pref.EnumDescriptor
+	typ reflect.Type // must implement protoreflect.Enum
+}
+
+func (t *enumType) Descriptor() pref.EnumDescriptor { return t.EnumDescriptor }
+func (t *enumType) GoType() reflect.Type            { return t.typ }
+func (t *enumType) New(n pref.EnumNumber) pref.Enum {
+	return reflect.ValueOf(n).Convert(t.typ).Interface().(pref.Enum)
+}
+
+// EnumDescriptorOf returns the protoreflect.EnumDescriptor for e.
+func (Export) EnumDescriptorOf(e enum) pref.EnumDescriptor {
+	if ev, ok := e.(pref.Enum); ok {
+		return ev.Descriptor()
+	}
+	return legacyWrapper.EnumDescriptorOf(e)
+}
+
 // EnumStringOf returns the enum value as a string, either as the name if
 // the number is resolvable, or the number formatted as a string.
 func (Export) EnumStringOf(ed pref.EnumDescriptor, n pref.EnumNumber) string {
@@ -48,11 +67,12 @@
 	return strconv.Itoa(int(n))
 }
 
+// message is any message type generated by protoc-gen-go
+// and must be a pointer to a named struct type.
+type message = interface{}
+
 // MessageOf returns the protoreflect.Message interface over m.
-// If m already implements proto.Message, then it directly calls the
-// ProtoReflect method, otherwise it wraps the v1 message to implement
-// the v2 reflective interface.
-func (Export) MessageOf(m interface{}) pref.Message {
+func (Export) MessageOf(m message) pref.Message {
 	if mv, ok := m.(pref.ProtoMessage); ok {
 		return mv.ProtoReflect()
 	}
@@ -60,32 +80,32 @@
 }
 
 // MessageTypeOf returns the protoreflect.MessageType for m.
-// If m already implements proto.Message, then it obtains the type by directly
-// calling the ProtoReflect.Type method, otherwise it derives a message type
-// from the v1 message struct.
-func (Export) MessageTypeOf(m interface{}) pref.MessageType {
+func (Export) MessageTypeOf(m message) pref.MessageType {
 	if mv, ok := m.(pref.ProtoMessage); ok {
-		return mv.ProtoReflect().Type()
+		return &messageType{mv.ProtoReflect().Descriptor(), reflect.TypeOf(m)}
 	}
 	return legacyWrapper.MessageTypeOf(m)
 }
 
-// ExtensionTypeOf returns a protoreflect.ExtensionType where the type of the
-// field is t. The type t must be provided if the field is an enum or message.
-// If t already implements proto.Enum or proto.Message, then this returns
-// an extension type by directly calling prototype.GoExtension.
-// Otherwise, it derives an extension type by wrapping the enum or message
-// using EnumOf or MessageOf.
-func (Export) ExtensionTypeOf(d pref.ExtensionDescriptor, t interface{}) pref.ExtensionType {
-	switch t := t.(type) {
-	case nil:
-		return ptype.GoExtension(d, nil, nil)
-	case pref.Enum:
-		return ptype.GoExtension(d, t.Type(), nil)
-	case pref.ProtoMessage:
-		return ptype.GoExtension(d, nil, t.ProtoReflect().Type())
+// TODO: This needs to be centralized in a package.
+type messageType struct {
+	// TODO: Remove me as an embedded field.
+	pref.MessageDescriptor
+	typ reflect.Type // must implement protoreflect.ProtoMessage
+}
+
+func (t *messageType) Descriptor() pref.MessageDescriptor { return t.MessageDescriptor }
+func (t *messageType) GoType() reflect.Type               { return t.typ }
+func (t *messageType) New() pref.Message {
+	return reflect.New(t.typ.Elem()).Interface().(pref.ProtoMessage).ProtoReflect()
+}
+
+// MessageDescriptorOf returns the protoreflect.MessageDescriptor for m.
+func (Export) MessageDescriptorOf(m message) pref.MessageDescriptor {
+	if mv, ok := m.(pref.ProtoMessage); ok {
+		return mv.ProtoReflect().Descriptor()
 	}
-	return legacyWrapper.ExtensionTypeOf(d, t)
+	return legacyWrapper.MessageDescriptorOf(m)
 }
 
 // MessageStringOf returns the message value as a string,