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/legacy/extension.go b/internal/legacy/extension.go
index 94ad7b7..e9c8bb7 100644
--- a/internal/legacy/extension.go
+++ b/internal/legacy/extension.go
@@ -15,6 +15,7 @@
 	pfmt "github.com/golang/protobuf/v2/internal/typefmt"
 	pvalue "github.com/golang/protobuf/v2/internal/value"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
+	preg "github.com/golang/protobuf/v2/reflect/protoregistry"
 	piface "github.com/golang/protobuf/v2/runtime/protoiface"
 )
 
@@ -46,25 +47,25 @@
 
 // extensionDescFromType converts a v2 protoreflect.ExtensionType to a
 // protoiface.ExtensionDescV1. The returned ExtensionDesc must not be mutated.
-func extensionDescFromType(t pref.ExtensionType) *piface.ExtensionDescV1 {
+func extensionDescFromType(xt pref.ExtensionType) *piface.ExtensionDescV1 {
 	// Fast-path: check whether an extension desc is already nested within.
-	if t, ok := t.(interface {
+	if xt, ok := xt.(interface {
 		ProtoLegacyExtensionDesc() *piface.ExtensionDescV1
 	}); ok {
-		if d := t.ProtoLegacyExtensionDesc(); d != nil {
+		if d := xt.ProtoLegacyExtensionDesc(); d != nil {
 			return d
 		}
 	}
 
 	// Fast-path: check the cache for whether this ExtensionType has already
 	// been converted to a legacy descriptor.
-	if d, ok := extensionDescCache.Load(t); ok {
+	if d, ok := extensionDescCache.Load(xt); ok {
 		return d.(*piface.ExtensionDescV1)
 	}
 
 	// Determine the parent type if possible.
 	var parent piface.MessageV1
-	if mt, ok := t.Extendee().(pref.MessageType); ok {
+	if mt, _ := preg.GlobalTypes.FindMessageByName(xt.Descriptor().Extendee().FullName()); mt != nil {
 		// Create a new parent message and unwrap it if possible.
 		mv := mt.New().Interface()
 		t := reflect.TypeOf(mv)
@@ -81,7 +82,7 @@
 
 	// Determine the v1 extension type, which is unfortunately not the same as
 	// the v2 ExtensionType.GoType.
-	extType := t.GoType()
+	extType := xt.GoType()
 	switch extType.Kind() {
 	case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
 		extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
@@ -94,23 +95,21 @@
 	// Reconstruct the legacy enum full name, which is an odd mixture of the
 	// proto package name with the Go type name.
 	var enumName string
-	if t.Kind() == pref.EnumKind {
+	if xt.Descriptor().Kind() == pref.EnumKind {
 		// Derive Go type name.
-		// For legacy enums, unwrap the wrapper to get the underlying Go type.
-		et := t.Enum().(pref.EnumType)
-		var ev interface{} = et.New(0)
-		if u, ok := ev.(pvalue.Unwrapper); ok {
-			ev = u.ProtoUnwrap()
+		t := extType
+		if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
+			t = t.Elem()
 		}
-		enumName = reflect.TypeOf(ev).Name()
+		enumName = t.Name()
 
 		// Derive the proto package name.
 		// For legacy enums, obtain the proto package from the raw descriptor.
 		var protoPkg string
-		if fd := parentFileDescriptor(et); fd != nil {
+		if fd := parentFileDescriptor(xt.Descriptor().Enum()); fd != nil {
 			protoPkg = string(fd.Package())
 		}
-		if ed, ok := ev.(enumV1); ok && protoPkg == "" {
+		if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
 			b, _ := ed.EnumDescriptor()
 			protoPkg = loadFileDesc(b).GetPackage()
 		}
@@ -122,21 +121,21 @@
 
 	// Derive the proto file that the extension was declared within.
 	var filename string
-	if fd := parentFileDescriptor(t); fd != nil {
+	if fd := parentFileDescriptor(xt.Descriptor()); fd != nil {
 		filename = fd.Path()
 	}
 
 	// Construct and return a ExtensionDescV1.
 	d := &piface.ExtensionDescV1{
-		Type:          t,
+		Type:          xt,
 		ExtendedType:  parent,
 		ExtensionType: reflect.Zero(extType).Interface(),
-		Field:         int32(t.Number()),
-		Name:          string(t.FullName()),
-		Tag:           ptag.Marshal(t, enumName),
+		Field:         int32(xt.Descriptor().Number()),
+		Name:          string(xt.Descriptor().FullName()),
+		Tag:           ptag.Marshal(xt.Descriptor(), enumName),
 		Filename:      filename,
 	}
-	if d, ok := extensionDescCache.LoadOrStore(t, d); ok {
+	if d, ok := extensionDescCache.LoadOrStore(xt, d); ok {
 		return d.(*piface.ExtensionDescV1)
 	}
 	return d
@@ -169,7 +168,15 @@
 	f := ptag.Unmarshal(d.Tag, t)
 
 	// Construct a v2 ExtensionType.
+	var ed pref.EnumDescriptor
+	var md pref.MessageDescriptor
 	conv := pvalue.NewLegacyConverter(t, f.Kind, Export{})
+	if conv.EnumType != nil {
+		ed = conv.EnumType.Descriptor()
+	}
+	if conv.MessageType != nil {
+		md = conv.MessageType.Descriptor()
+	}
 	xd, err := ptype.NewExtension(&ptype.StandaloneExtension{
 		FullName:     pref.FullName(d.Name),
 		Number:       pref.FieldNumber(d.Field),
@@ -177,19 +184,14 @@
 		Kind:         f.Kind,
 		Default:      f.Default,
 		Options:      f.Options,
-		EnumType:     conv.EnumType,
-		MessageType:  conv.MessageType,
-		ExtendedType: pimpl.Export{}.MessageTypeOf(d.ExtendedType),
+		EnumType:     ed,
+		MessageType:  md,
+		ExtendedType: pimpl.Export{}.MessageDescriptorOf(d.ExtendedType),
 	})
 	if err != nil {
 		panic(err)
 	}
-	var zv interface{}
-	switch xd.Kind() {
-	case pref.EnumKind, pref.MessageKind, pref.GroupKind:
-		zv = reflect.Zero(t).Interface()
-	}
-	xt := pimpl.Export{}.ExtensionTypeOf(xd, zv)
+	xt := ExtensionTypeOf(xd, t)
 
 	// Cache the conversion for both directions.
 	extensionDescCache.LoadOrStore(xt, d)
@@ -199,17 +201,26 @@
 	return xt
 }
 
-// extensionTypeOf returns a protoreflect.ExtensionType where the GoType
-// is the underlying v1 Go type instead of the wrapper types used to present
-// v1 Go types as if they satisfied the v2 API.
+// 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.
 //
-// This function is only valid if xd.Kind is an enum or message.
-func extensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
-	// Step 1: Create an ExtensionType where GoType is the wrapper type.
+// This is exported for testing purposes.
+func ExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
+	// Extension types for non-enums and non-messages are simple.
+	switch xd.Kind() {
+	case pref.EnumKind, pref.MessageKind, pref.GroupKind:
+	default:
+		return ptype.GoExtension(xd, nil, nil)
+	}
+
+	// Create an ExtensionType where GoType is the wrapper type.
 	conv := pvalue.NewLegacyConverter(t, xd.Kind(), Export{})
 	xt := ptype.GoExtension(xd, conv.EnumType, conv.MessageType)
+	if !conv.IsLegacy {
+		return xt
+	}
 
-	// Step 2: Wrap ExtensionType such that GoType presents the legacy Go type.
+	// Wrap ExtensionType such that GoType presents the legacy Go type.
 	xt2 := &extensionType{ExtensionType: xt}
 	if xd.Cardinality() != pref.Repeated {
 		xt2.typ = t
@@ -264,4 +275,4 @@
 func (x *extensionType) New() pref.Value                      { return x.new() }
 func (x *extensionType) ValueOf(v interface{}) pref.Value     { return x.valueOf(v) }
 func (x *extensionType) InterfaceOf(v pref.Value) interface{} { return x.interfaceOf(v) }
-func (x *extensionType) Format(s fmt.State, r rune)           { pfmt.FormatDesc(s, r, x) }
+func (x *extensionType) Format(s fmt.State, r rune)           { pfmt.FormatDesc(s, r, x.Descriptor()) }