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/enum.go b/internal/legacy/enum.go
index 0ee397f..2a57b0b 100644
--- a/internal/legacy/enum.go
+++ b/internal/legacy/enum.go
@@ -55,12 +55,16 @@
 	goTyp reflect.Type
 }
 
-func (e *enumWrapper) Number() pref.EnumNumber {
-	return e.num
-}
+// TODO: Remove this.
 func (e *enumWrapper) Type() pref.EnumType {
 	return e.pbTyp
 }
+func (e *enumWrapper) Descriptor() pref.EnumDescriptor {
+	return e.pbTyp.Descriptor()
+}
+func (e *enumWrapper) Number() pref.EnumNumber {
+	return e.num
+}
 func (e *enumWrapper) ProtoReflect() pref.Enum {
 	return e
 }
diff --git a/internal/legacy/export.go b/internal/legacy/export.go
index 054108a..fa4375a 100644
--- a/internal/legacy/export.go
+++ b/internal/legacy/export.go
@@ -25,6 +25,10 @@
 	return loadEnumType(reflect.TypeOf(e))
 }
 
+func (Export) EnumDescriptorOf(e interface{}) pref.EnumDescriptor {
+	return LoadEnumDesc(reflect.TypeOf(e))
+}
+
 func (Export) MessageOf(m interface{}) pvalue.LegacyMessage {
 	return wrapMessage(reflect.ValueOf(m)).ProtoReflect().(pvalue.LegacyMessage)
 }
@@ -33,8 +37,8 @@
 	return loadMessageType(reflect.TypeOf(m)).PBType
 }
 
-func (Export) ExtensionTypeOf(d pref.ExtensionDescriptor, t interface{}) pref.ExtensionType {
-	return extensionTypeOf(d, reflect.TypeOf(t))
+func (Export) MessageDescriptorOf(m interface{}) pref.MessageDescriptor {
+	return LoadMessageDesc(reflect.TypeOf(m))
 }
 
 func (Export) ExtensionDescFromType(t pref.ExtensionType) *piface.ExtensionDescV1 {
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()) }
diff --git a/internal/legacy/extension_test.go b/internal/legacy/extension_test.go
index 630c158..cdba627 100644
--- a/internal/legacy/extension_test.go
+++ b/internal/legacy/extension_test.go
@@ -5,17 +5,15 @@
 package legacy_test
 
 import (
+	"reflect"
 	"testing"
 
 	pimpl "github.com/golang/protobuf/v2/internal/impl"
+	plegacy "github.com/golang/protobuf/v2/internal/legacy"
 	ptype "github.com/golang/protobuf/v2/internal/prototype"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 	piface "github.com/golang/protobuf/v2/runtime/protoiface"
 
-	// The legacy package must be imported prior to use of any legacy messages.
-	// TODO: Remove this when protoV1 registers these hooks for you.
-	plegacy "github.com/golang/protobuf/v2/internal/legacy"
-
 	proto2_20180125 "github.com/golang/protobuf/v2/internal/testprotos/legacy/proto2.v1.0.0-20180125-92554152"
 )
 
@@ -34,22 +32,22 @@
 func mustMakeExtensionType(x *ptype.StandaloneExtension, v interface{}) pref.ExtensionType {
 	xd, err := ptype.NewExtension(x)
 	if err != nil {
-		panic(xd)
+		panic(err)
 	}
-	return pimpl.Export{}.ExtensionTypeOf(xd, v)
+	return plegacy.ExtensionTypeOf(xd, reflect.TypeOf(v))
 }
 
 var (
-	parentType    = pimpl.Export{}.MessageTypeOf((*legacyTestMessage)(nil))
-	messageV1Type = pimpl.Export{}.MessageTypeOf((*proto2_20180125.Message_ChildMessage)(nil))
+	parentDesc    = pimpl.Export{}.MessageDescriptorOf((*legacyTestMessage)(nil))
+	messageV1Desc = pimpl.Export{}.MessageDescriptorOf((*proto2_20180125.Message_ChildMessage)(nil))
 
 	wantType = mustMakeExtensionType(&ptype.StandaloneExtension{
 		FullName:     "fizz.buzz.optional_message_v1",
 		Number:       10007,
 		Cardinality:  pref.Optional,
 		Kind:         pref.MessageKind,
-		MessageType:  messageV1Type,
-		ExtendedType: parentType,
+		MessageType:  messageV1Desc,
+		ExtendedType: parentDesc,
 	}, (*proto2_20180125.Message_ChildMessage)(nil))
 	wantDesc = &piface.ExtensionDescV1{
 		ExtendedType:  (*legacyTestMessage)(nil),
diff --git a/internal/legacy/legacy_test.go b/internal/legacy/legacy_test.go
index 3bac3b3..9bb484c 100644
--- a/internal/legacy/legacy_test.go
+++ b/internal/legacy/legacy_test.go
@@ -31,7 +31,7 @@
 	const numParallel = 5
 	var messageATypes [numParallel]protoreflect.MessageType
 	var messageBTypes [numParallel]protoreflect.MessageType
-	var enumTypes [numParallel]protoreflect.EnumType
+	var enumDescs [numParallel]protoreflect.EnumDescriptor
 
 	// Concurrently load message and enum types.
 	var wg sync.WaitGroup
@@ -48,31 +48,30 @@
 		}()
 		go func() {
 			defer wg.Done()
-			enumTypes[i] = Export{}.EnumTypeOf(Enum(0))
+			enumDescs[i] = Export{}.EnumDescriptorOf(Enum(0))
 		}()
 	}
 	wg.Wait()
 
 	var (
 		wantMTA = messageATypes[0]
-		wantMDA = messageATypes[0].Fields().ByNumber(1).Message()
+		wantMDA = messageATypes[0].Descriptor().Fields().ByNumber(1).Message()
 		wantMTB = messageBTypes[0]
-		wantMDB = messageBTypes[0].Fields().ByNumber(2).Message()
-		wantET  = enumTypes[0]
-		wantED  = messageATypes[0].Fields().ByNumber(3).Enum()
+		wantMDB = messageBTypes[0].Descriptor().Fields().ByNumber(2).Message()
+		wantED  = messageATypes[0].Descriptor().Fields().ByNumber(3).Enum()
 	)
 
 	for _, gotMT := range messageATypes[1:] {
 		if gotMT != wantMTA {
 			t.Error("MessageType(MessageA) mismatch")
 		}
-		if gotMDA := gotMT.Fields().ByNumber(1).Message(); gotMDA != wantMDA {
+		if gotMDA := gotMT.Descriptor().Fields().ByNumber(1).Message(); gotMDA != wantMDA {
 			t.Error("MessageDescriptor(MessageA) mismatch")
 		}
-		if gotMDB := gotMT.Fields().ByNumber(2).Message(); gotMDB != wantMDB {
+		if gotMDB := gotMT.Descriptor().Fields().ByNumber(2).Message(); gotMDB != wantMDB {
 			t.Error("MessageDescriptor(MessageB) mismatch")
 		}
-		if gotED := gotMT.Fields().ByNumber(3).Enum(); gotED != wantED {
+		if gotED := gotMT.Descriptor().Fields().ByNumber(3).Enum(); gotED != wantED {
 			t.Error("EnumDescriptor(Enum) mismatch")
 		}
 	}
@@ -80,18 +79,18 @@
 		if gotMT != wantMTB {
 			t.Error("MessageType(MessageB) mismatch")
 		}
-		if gotMDA := gotMT.Fields().ByNumber(1).Message(); gotMDA != wantMDA {
+		if gotMDA := gotMT.Descriptor().Fields().ByNumber(1).Message(); gotMDA != wantMDA {
 			t.Error("MessageDescriptor(MessageA) mismatch")
 		}
-		if gotMDB := gotMT.Fields().ByNumber(2).Message(); gotMDB != wantMDB {
+		if gotMDB := gotMT.Descriptor().Fields().ByNumber(2).Message(); gotMDB != wantMDB {
 			t.Error("MessageDescriptor(MessageB) mismatch")
 		}
-		if gotED := gotMT.Fields().ByNumber(3).Enum(); gotED != wantED {
+		if gotED := gotMT.Descriptor().Fields().ByNumber(3).Enum(); gotED != wantED {
 			t.Error("EnumDescriptor(Enum) mismatch")
 		}
 	}
-	for _, gotET := range enumTypes[1:] {
-		if gotET != wantET {
+	for _, gotED := range enumDescs[1:] {
+		if gotED != wantED {
 			t.Error("EnumType(Enum) mismatch")
 		}
 	}
diff --git a/internal/legacy/message.go b/internal/legacy/message.go
index 01bc053..ee242ca 100644
--- a/internal/legacy/message.go
+++ b/internal/legacy/message.go
@@ -232,14 +232,14 @@
 	// Populate EnumType and MessageType.
 	if f.EnumType == nil && f.Kind == pref.EnumKind {
 		if ev, ok := reflect.Zero(t).Interface().(pref.Enum); ok {
-			f.EnumType = ev.Type()
+			f.EnumType = ev.Descriptor()
 		} else {
 			f.EnumType = LoadEnumDesc(t)
 		}
 	}
 	if f.MessageType == nil && (f.Kind == pref.MessageKind || f.Kind == pref.GroupKind) {
 		if mv, ok := reflect.Zero(t).Interface().(pref.ProtoMessage); ok {
-			f.MessageType = mv.ProtoReflect().Type()
+			f.MessageType = mv.ProtoReflect().Descriptor()
 		} else if t.Kind() == reflect.Map {
 			m := &ptype.StandaloneMessage{
 				Syntax:     parent.Syntax,