reflect/prototype: initial commit

Add the prototype package which provides constructors for
protoreflect.{Enum,Message,Extension}Type.

Switch all usages of the internal/prototype equivalent to the new package.
Switch all custom implementions of {Enum,Message}Type to the new package.

Change-Id: Ia9dae6fed4f2b90e55c123627044a7faf098c4b1
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/178438
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/export.go b/internal/impl/export.go
index 9ea3900..8c7c121 100644
--- a/internal/impl/export.go
+++ b/internal/impl/export.go
@@ -10,6 +10,7 @@
 
 	"google.golang.org/protobuf/encoding/prototext"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/prototype"
 )
 
 // Export is a zero-length named type that exists only to export a set of
@@ -31,24 +32,16 @@
 // EnumTypeOf returns the protoreflect.EnumType for e.
 func (Export) EnumTypeOf(e enum) pref.EnumType {
 	if ev, ok := e.(pref.Enum); ok {
-		return &enumType{ev.Descriptor(), reflect.TypeOf(e)}
+		return &prototype.Enum{
+			EnumDescriptor: ev.Descriptor(),
+			NewEnum: func(n pref.EnumNumber) pref.Enum {
+				return reflect.ValueOf(n).Convert(reflect.TypeOf(e)).Interface().(pref.Enum)
+			},
+		}
 	}
 	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 {
@@ -82,24 +75,16 @@
 // MessageTypeOf returns the protoreflect.MessageType for m.
 func (Export) MessageTypeOf(m message) pref.MessageType {
 	if mv, ok := m.(pref.ProtoMessage); ok {
-		return &messageType{mv.ProtoReflect().Descriptor(), reflect.TypeOf(m)}
+		return &prototype.Message{
+			MessageDescriptor: mv.ProtoReflect().Descriptor(),
+			NewMessage: func() pref.Message {
+				return reflect.New(reflect.TypeOf(m).Elem()).Interface().(pref.ProtoMessage).ProtoReflect()
+			},
+		}
 	}
 	return legacyWrapper.MessageTypeOf(m)
 }
 
-// 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 {
diff --git a/internal/impl/legacy_hook.go b/internal/impl/legacy_hook.go
index f3ffe63..98eaf2f 100644
--- a/internal/impl/legacy_hook.go
+++ b/internal/impl/legacy_hook.go
@@ -4,14 +4,39 @@
 
 package impl
 
-import pvalue "google.golang.org/protobuf/internal/value"
+import (
+	"reflect"
+
+	pvalue "google.golang.org/protobuf/internal/value"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+	piface "google.golang.org/protobuf/runtime/protoiface"
+)
 
 // TODO: Add a default LegacyWrapper that panics with a more helpful message?
-var legacyWrapper pvalue.LegacyWrapper
+var legacyWrapper LegacyWrapper
 
 // RegisterLegacyWrapper registers a set of constructor functions that are
 // called when a legacy enum or message is encountered that does not natively
 // support the protobuf reflection APIs.
-func RegisterLegacyWrapper(w pvalue.LegacyWrapper) {
+func RegisterLegacyWrapper(w LegacyWrapper) {
 	legacyWrapper = w
 }
+
+// LegacyWrapper is a set of wrapper methods that wraps legacy v1 Go types
+// to implement the v2 reflection APIs.
+type LegacyWrapper interface {
+	NewConverter(reflect.Type, pref.Kind) pvalue.Converter
+
+	EnumOf(interface{}) pref.Enum
+	EnumTypeOf(interface{}) pref.EnumType
+	EnumDescriptorOf(interface{}) pref.EnumDescriptor
+
+	MessageOf(interface{}) pref.Message
+	MessageTypeOf(interface{}) pref.MessageType
+	MessageDescriptorOf(interface{}) pref.MessageDescriptor
+
+	// TODO: Remove these eventually.
+	// See the TODOs in internal/impl/legacy_extension.go.
+	ExtensionDescFromType(pref.ExtensionType) *piface.ExtensionDescV1
+	ExtensionTypeFromDesc(*piface.ExtensionDescV1) pref.ExtensionType
+}
diff --git a/internal/impl/message_field.go b/internal/impl/message_field.go
index 72f4523..a69d2c8 100644
--- a/internal/impl/message_field.go
+++ b/internal/impl/message_field.go
@@ -42,7 +42,7 @@
 	if !reflect.PtrTo(ot).Implements(ft) {
 		panic(fmt.Sprintf("invalid type: %v does not implement %v", ot, ft))
 	}
-	conv := pvalue.NewLegacyConverter(ot.Field(0).Type, fd.Kind(), legacyWrapper)
+	conv := newConverter(ot.Field(0).Type, fd.Kind())
 	fieldOffset := offsetOf(fs)
 	// TODO: Implement unsafe fast path?
 	return fieldInfo{
@@ -86,12 +86,9 @@
 			}
 			rv.Set(reflect.Zero(rv.Type()))
 		},
-		newMessage: func() pref.Message {
-			// This is only valid for messages and panics for other kinds.
-			return conv.MessageType.New()
-		},
-		offset:    fieldOffset,
-		isPointer: true,
+		newMessage: conv.NewMessage,
+		offset:     fieldOffset,
+		isPointer:  true,
 	}
 }
 
@@ -100,8 +97,8 @@
 	if ft.Kind() != reflect.Map {
 		panic(fmt.Sprintf("invalid type: got %v, want map kind", ft))
 	}
-	keyConv := pvalue.NewLegacyConverter(ft.Key(), fd.MapKey().Kind(), legacyWrapper)
-	valConv := pvalue.NewLegacyConverter(ft.Elem(), fd.MapValue().Kind(), legacyWrapper)
+	keyConv := newConverter(ft.Key(), fd.MapKey().Kind())
+	valConv := newConverter(ft.Elem(), fd.MapValue().Kind())
 	wiretag := wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
 	fieldOffset := offsetOf(fs)
 	// TODO: Implement unsafe fast path?
@@ -142,7 +139,7 @@
 	if ft.Kind() != reflect.Slice {
 		panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
 	}
-	conv := pvalue.NewLegacyConverter(ft.Elem(), fd.Kind(), legacyWrapper)
+	conv := newConverter(ft.Elem(), fd.Kind())
 	var wiretag uint64
 	if !fd.IsPacked() {
 		wiretag = wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
@@ -197,7 +194,7 @@
 			ft = ft.Elem()
 		}
 	}
-	conv := pvalue.NewLegacyConverter(ft, fd.Kind(), legacyWrapper)
+	conv := newConverter(ft, fd.Kind())
 	fieldOffset := offsetOf(fs)
 	wiretag := wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
 	// TODO: Implement unsafe fast path?
@@ -267,7 +264,7 @@
 
 func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
 	ft := fs.Type
-	conv := pvalue.NewLegacyConverter(ft, fd.Kind(), legacyWrapper)
+	conv := newConverter(ft, fd.Kind())
 	fieldOffset := offsetOf(fs)
 	// TODO: Implement unsafe fast path?
 	wiretag := wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
@@ -300,14 +297,12 @@
 			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			rv.Set(reflect.Zero(rv.Type()))
 		},
-		newMessage: func() pref.Message {
-			return conv.MessageType.New()
-		},
-		funcs:     fieldCoder(fd, ft),
-		offset:    fieldOffset,
-		isPointer: true,
-		wiretag:   wiretag,
-		tagsize:   wire.SizeVarint(wiretag),
+		newMessage: conv.NewMessage,
+		funcs:      fieldCoder(fd, ft),
+		offset:     fieldOffset,
+		isPointer:  true,
+		wiretag:    wiretag,
+		tagsize:    wire.SizeVarint(wiretag),
 	}
 }
 
@@ -342,3 +337,10 @@
 		},
 	}
 }
+
+func newConverter(t reflect.Type, k pref.Kind) pvalue.Converter {
+	if legacyWrapper != nil {
+		return legacyWrapper.NewConverter(t, k)
+	}
+	return pvalue.NewConverter(t, k)
+}
diff --git a/internal/impl/message_test.go b/internal/impl/message_test.go
index 26f8db6..e1fc1ce 100644
--- a/internal/impl/message_test.go
+++ b/internal/impl/message_test.go
@@ -19,6 +19,7 @@
 	scalar "google.golang.org/protobuf/internal/scalar"
 	pvalue "google.golang.org/protobuf/internal/value"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/prototype"
 
 	proto2_20180125 "google.golang.org/protobuf/internal/testprotos/legacy/proto2.v1.0.0-20180125-92554152"
 	"google.golang.org/protobuf/types/descriptorpb"
@@ -189,8 +190,8 @@
 	MapBytes   map[MyString]MyBytes
 )
 
-var scalarProto2Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto2)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var scalarProto2Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto2)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "ScalarProto2",
 		Fields: []ptype.Field{
@@ -219,10 +220,10 @@
 			{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("22"))},
 		},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(ScalarProto2)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *ScalarProto2) Type() pref.MessageType { return scalarProto2Type.PBType }
@@ -304,8 +305,8 @@
 	MyBytesA  MyString  `protobuf:"22"`
 }
 
-var scalarProto3Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto3)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var scalarProto3Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto3)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto3,
 		FullName: "ScalarProto3",
 		Fields: []ptype.Field{
@@ -334,10 +335,10 @@
 			{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind},
 		},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(ScalarProto3)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *ScalarProto3) Type() pref.MessageType { return scalarProto3Type.PBType }
@@ -437,8 +438,8 @@
 	MyBytes4   ListStrings `protobuf:"19"`
 }
 
-var listScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ListScalars)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var listScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ListScalars)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "ListScalars",
 		Fields: []ptype.Field{
@@ -465,10 +466,10 @@
 			{Name: "f19", Number: 19, Cardinality: pref.Repeated, Kind: pref.BytesKind},
 		},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(ListScalars)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *ListScalars) Type() pref.MessageType             { return listScalarsType.PBType }
@@ -628,8 +629,8 @@
 	}
 }
 
-var mapScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(MapScalars)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var mapScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(MapScalars)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "MapScalars",
 		Fields: []ptype.Field{
@@ -663,10 +664,10 @@
 			mustMakeMapEntry(25, pref.StringKind, pref.BytesKind),
 		},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(MapScalars)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *MapScalars) Type() pref.MessageType             { return mapScalarsType.PBType }
@@ -807,8 +808,8 @@
 	Union isOneofScalars_Union `protobuf_oneof:"union"`
 }
 
-var oneofScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(OneofScalars)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var oneofScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(OneofScalars)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "OneofScalars",
 		Fields: []ptype.Field{
@@ -828,10 +829,10 @@
 		},
 		Oneofs: []ptype.Oneof{{Name: "union"}},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(OneofScalars)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *OneofScalars) Type() pref.MessageType { return oneofScalarsType.PBType }
@@ -983,16 +984,16 @@
 
 type EnumProto2 int32
 
-var enumProto2Type = ptype.GoEnum(
-	mustMakeEnumDesc(ptype.StandaloneEnum{
+var enumProto2Type = &prototype.Enum{
+	EnumDescriptor: mustMakeEnumDesc(ptype.StandaloneEnum{
 		Syntax:   pref.Proto2,
 		FullName: "EnumProto2",
 		Values:   []ptype.EnumValue{{Name: "DEAD", Number: 0xdead}, {Name: "BEEF", Number: 0xbeef}},
 	}),
-	func(_ pref.EnumType, n pref.EnumNumber) pref.Enum {
+	NewEnum: func(n pref.EnumNumber) pref.Enum {
 		return EnumProto2(n)
 	},
-)
+}
 
 // TODO: Remove this.
 func (e EnumProto2) Type() pref.EnumType             { return enumProto2Type }
@@ -1002,16 +1003,16 @@
 
 type EnumProto3 int32
 
-var enumProto3Type = ptype.GoEnum(
-	mustMakeEnumDesc(ptype.StandaloneEnum{
+var enumProto3Type = &prototype.Enum{
+	EnumDescriptor: mustMakeEnumDesc(ptype.StandaloneEnum{
 		Syntax:   pref.Proto3,
 		FullName: "EnumProto3",
 		Values:   []ptype.EnumValue{{Name: "ALPHA", Number: 0}, {Name: "BRAVO", Number: 1}},
 	}),
-	func(_ pref.EnumType, n pref.EnumNumber) pref.Enum {
+	NewEnum: func(n pref.EnumNumber) pref.Enum {
 		return EnumProto3(n)
 	},
-)
+}
 
 // TODO: Remove this.
 func (e EnumProto3) Type() pref.EnumType             { return enumProto3Type }
@@ -1031,8 +1032,8 @@
 	Union         isEnumMessages_Union     `protobuf_oneof:"union"`
 }
 
-var enumMessagesType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(EnumMessages)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var enumMessagesType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(EnumMessages)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "EnumMessages",
 		Fields: []ptype.Field{
@@ -1051,10 +1052,10 @@
 		},
 		Oneofs: []ptype.Oneof{{Name: "union"}},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(EnumMessages)
 	},
-)}
+}}
 
 var enumMapDesc = mustMakeMessageDesc(ptype.StandaloneMessage{
 	Syntax:   pref.Proto2,