cmd/protoc-gen-go: add support for protobuf reflection

Implement support in protoc-gen-go for generating messages and enums
that satisfy the v2 protobuf reflection interfaces. Specifically, the following
are added:
* top-level variable representing the file descriptor
* ProtoReflect method on enums (to implement protoV2.Enum)
* ProtoReflect method on messages (to implement protoV2.Message)

The following are not supported yet:
* resolving transitive dependencies for file imports
* Extension descriptors
* Service descriptors

The implementation approach creates a single array for all the message and enum
declarations and references sections of that array to complete dependencies.
Since protobuf declarations can form a graph (a message may depend on itself),
it is difficult to construct a graph as a single literal. One way is to use
placeholder descriptors, but that is not efficient as it requires encoding
the full name of each dependent enum and message and then later resolving it;
thus, both expanding the binary size and also increasing initialization cost.
Instead, we add a prototype.{Enum,Message}.Reference method to obtain a
descriptor reference for the purposes for satisfying dependencies.
As such, nested declarations and dependencies are populated in an init function.

Other changes to support the implementation:
* Added a protoimpl package to expose the MessageType type and also the
MessageTypeOf and EnumTypeOf helper functions.
* Added a protogen.File.GoIdent field to provide a suggested variable name
for the file descriptor.
* Added prototype.{Enum,Message}.Reference that provides a descriptor reference
for the purposes for satisfying cyclic dependencies.
* Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source
identifier that represents the given constant.

Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d
Reviewed-on: https://go-review.googlesource.com/c/150074
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go
index 2a53d95..435eb5b 100644
--- a/cmd/protoc-gen-go/internal_gengo/main.go
+++ b/cmd/protoc-gen-go/internal_gengo/main.go
@@ -42,6 +42,8 @@
 	allEnums      []*protogen.Enum
 	allMessages   []*protogen.Message
 	allExtensions []*protogen.Extension
+
+	fileReflect fileReflect
 }
 
 // GenerateFile generates the contents of a .pb.go file.
@@ -53,7 +55,7 @@
 	// The different order for enums and extensions is to match the output
 	// of the previous implementation.
 	//
-	// TODO: Eventually make this consistent.
+	// TODO: Eventually make this consistent (and remove fileReflect).
 	f.allEnums = append(f.allEnums, f.File.Enums...)
 	walkMessages(f.Messages, func(message *protogen.Message) {
 		f.allMessages = append(f.allMessages, message)
@@ -62,6 +64,9 @@
 	})
 	f.allExtensions = append(f.allExtensions, f.File.Extensions...)
 
+	// Initialize data structures needed for reflection.
+	f.fileReflect.init(f)
+
 	// Determine the name of the var holding the file descriptor:
 	//
 	//     fileDescriptor_<hash of filename>
@@ -121,6 +126,8 @@
 
 	genInitFunction(gen, g, f)
 	genFileDescriptor(gen, g, f)
+	genReflectInitFunction(gen, g, f)
+	genReflectFileDescriptor(gen, g, f)
 }
 
 // walkMessages calls f on each message and all of its descendants.
@@ -264,6 +271,10 @@
 	}
 	g.P(")")
 	g.P()
+
+	// Generate support for protobuf reflection.
+	genReflectEnum(gen, g, f, enum)
+
 	nameMap := enum.GoIdent.GoName + "_name"
 	g.P("var ", nameMap, " = map[int32]string{")
 	generated := make(map[protoreflect.EnumNumber]bool)
@@ -406,6 +417,9 @@
 	g.P("}")
 	g.P()
 
+	// Generate support for protobuf reflection.
+	genReflectMessage(gen, g, f, message)
+
 	// Reset
 	g.P("func (m *", message.GoIdent, ") Reset() { *m = ", message.GoIdent, "{} }")
 	// String
@@ -736,7 +750,7 @@
 // genInitFunction generates an init function that registers the types in the
 // generated file with the proto package.
 func genInitFunction(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
-	if len(f.allMessages) == 0 && len(f.allEnums) == 0 && len(f.allExtensions) == 0 {
+	if len(f.allEnums)+len(f.allMessages)+len(f.allExtensions) == 0 {
 		return
 	}