reflect/prototype: add NewFileFromDescriptorProto constructor

Implement NewFileFromDescriptorProto, which constructs a
protoreflect.FileDescriptor from a provided descriptor.FileDescriptorProto.

Some other minor changes:
* Allow calling Find and Range methods on nil *protoregistry.Files to match
the behavior of maps where index operations are permitted, but store panics.
* Switch protoregistry test to be protoregistry_test to avoid cyclic dependency.

Change-Id: I5536901ef5096014a3421e63bc4a9dfcad335ea4
Reviewed-on: https://go-review.googlesource.com/132455
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/reflect/protoregistry/registry.go b/reflect/protoregistry/registry.go
index 5e21043..f01213a 100644
--- a/reflect/protoregistry/registry.go
+++ b/reflect/protoregistry/registry.go
@@ -188,6 +188,9 @@
 //
 // This return (nil, NotFound) if not found.
 func (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) {
+	if r == nil {
+		return nil, NotFound
+	}
 	pkg := name
 	root := &r.filesByPackage
 	for len(pkg) > 0 {
@@ -222,6 +225,9 @@
 // match before iterating over files with general prefix match.
 // The iteration order is undefined within exact matches or prefix matches.
 func (r *Files) RangeFilesByPackage(pkg protoreflect.FullName, f func(protoreflect.FileDescriptor) bool) {
+	if r == nil {
+		return
+	}
 	if strings.HasSuffix(string(pkg), ".") {
 		return // avoid edge case where splitPrefix allows trailing dot
 	}
@@ -255,6 +261,9 @@
 // RangeFilesByPath iterates over all registered files filtered by
 // the given proto path. The iteration order is undefined.
 func (r *Files) RangeFilesByPath(path string, f func(protoreflect.FileDescriptor) bool) {
+	if r == nil {
+		return
+	}
 	for _, fd := range r.filesByPath[path] { // TODO: iterate non-deterministically
 		if !f(fd) {
 			return
diff --git a/reflect/protoregistry/registry_test.go b/reflect/protoregistry/registry_test.go
index d05f8ed..fdabde0 100644
--- a/reflect/protoregistry/registry_test.go
+++ b/reflect/protoregistry/registry_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package protoregistry
+package protoregistry_test
 
 import (
 	"fmt"
@@ -13,6 +13,7 @@
 	"github.com/google/go-cmp/cmp/cmpopts"
 
 	pref "google.golang.org/proto/reflect/protoreflect"
+	preg "google.golang.org/proto/reflect/protoregistry"
 	ptype "google.golang.org/proto/reflect/prototype"
 )
 
@@ -252,7 +253,7 @@
 	})
 	for _, tt := range tests {
 		t.Run("", func(t *testing.T) {
-			var files Files
+			var files preg.Files
 			for i, tc := range tt.files {
 				fd, err := ptype.NewFile(tc.inFile)
 				if err != nil {
diff --git a/reflect/prototype/protofile.go b/reflect/prototype/protofile.go
index e2e4307..3d069b2 100644
--- a/reflect/prototype/protofile.go
+++ b/reflect/prototype/protofile.go
@@ -24,8 +24,6 @@
 // and initialized. This architectural approach keeps the literal representation
 // smaller, which then keeps the generated code size smaller.
 
-// TODO: Support initializing File from a google.protobuf.FileDescriptor?
-
 // TODO: Instead of a top-down construction approach where internal references
 // to message types use placeholder types, we could add a Reference method
 // on Message and Enum that creates a MessageDescriptor or EnumDescriptor
diff --git a/reflect/prototype/protofile_desc.go b/reflect/prototype/protofile_desc.go
new file mode 100644
index 0000000..39331db
--- /dev/null
+++ b/reflect/prototype/protofile_desc.go
@@ -0,0 +1,390 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package prototype
+
+import (
+	"fmt"
+	"math"
+	"strconv"
+	"strings"
+
+	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
+
+	"google.golang.org/proto/internal/encoding/text"
+	"google.golang.org/proto/internal/errors"
+	"google.golang.org/proto/reflect/protoreflect"
+	"google.golang.org/proto/reflect/protoregistry"
+)
+
+// TODO: Should we be responsible for validating other parts of the descriptor
+// that we don't directly use?
+//
+// For example:
+//	* That field numbers don't overlap with reserved numbers.
+//	* That field names don't overlap with reserved names.
+//	* That enum numbers don't overlap with reserved numbers.
+//	* That enum names don't overlap with reserved names.
+//	* That "extendee" is not set for a message field.
+//	* That "oneof_index" is not set for an extension field.
+//	* That "json_name" is not set for an extension field. Maybe, maybe not.
+//	* That "type_name" is not set on a field for non-enums and non-messages.
+//	* That "weak" is not set for an extension field (double check this).
+
+// TODO: Store the input file descriptor to implement:
+//	* protoreflect.Descriptor.DescriptorProto
+//	* protoreflect.Descriptor.DescriptorOptions
+
+// TODO: Should we return a File instead of protoreflect.FileDescriptor?
+// This would allow users to mutate the File before converting it.
+// However, this will complicate future work for validation since File may now
+// diverge from the stored descriptor proto (see above TODO).
+
+// NewFileFromDescriptorProto creates a new protoreflect.FileDescriptor from
+// the provided descriptor message. The file must represent a valid proto file
+// according to protobuf semantics.
+//
+// Any import files, enum types, or message types referenced in the file are
+// resolved using the provided registry. When looking up an import file path,
+// the path must be unique. The newly created file descriptor is not registered
+// back into the provided file registry.
+//
+// The caller must relinquish full ownership of the input fd and must not
+// access or mutate any fields.
+func NewFileFromDescriptorProto(fd *descriptorV1.FileDescriptorProto, r *protoregistry.Files) (protoreflect.FileDescriptor, error) {
+	var f File
+	switch fd.GetSyntax() {
+	case "proto2":
+		f.Syntax = protoreflect.Proto2
+	case "proto3":
+		f.Syntax = protoreflect.Proto3
+	default:
+		return nil, errors.New("invalid syntax: %v", fd.GetSyntax())
+	}
+	f.Path = fd.GetName()
+	f.Package = protoreflect.FullName(fd.GetPackage())
+
+	f.Imports = make([]protoreflect.FileImport, len(fd.GetDependency()))
+	for _, i := range fd.GetPublicDependency() {
+		if int(i) >= len(f.Imports) || f.Imports[i].IsPublic {
+			return nil, errors.New("invalid or duplicate public import index: %d", i)
+		}
+		f.Imports[i].IsPublic = true
+	}
+	for _, i := range fd.GetWeakDependency() {
+		if int(i) >= len(f.Imports) || f.Imports[i].IsWeak {
+			return nil, errors.New("invalid or duplicate weak import index: %d", i)
+		}
+		f.Imports[i].IsWeak = true
+	}
+	for i, path := range fd.GetDependency() {
+		var n int
+		imp := &f.Imports[i]
+		r.RangeFilesByPath(path, func(fd protoreflect.FileDescriptor) bool {
+			imp.FileDescriptor = fd
+			n++
+			return true
+		})
+		if n > 1 {
+			return nil, errors.New("duplicate files for import %q", path)
+		}
+		if imp.IsWeak || imp.FileDescriptor == nil {
+			imp.FileDescriptor = PlaceholderFile(path, "")
+		}
+	}
+
+	var err error
+	f.Messages, err = messagesFromDescriptorProto(fd.GetMessageType(), r)
+	if err != nil {
+		return nil, err
+	}
+	f.Enums, err = enumsFromDescriptorProto(fd.GetEnumType(), r)
+	if err != nil {
+		return nil, err
+	}
+	f.Extensions, err = extensionsFromDescriptorProto(fd.GetExtension(), r)
+	if err != nil {
+		return nil, err
+	}
+	f.Services, err = servicesFromDescriptorProto(fd.GetService(), r)
+	if err != nil {
+		return nil, err
+	}
+
+	return NewFile(&f)
+}
+
+func messagesFromDescriptorProto(mds []*descriptorV1.DescriptorProto, r *protoregistry.Files) (ms []Message, err error) {
+	for _, md := range mds {
+		var m Message
+		m.Name = protoreflect.Name(md.GetName())
+		m.IsMapEntry = md.GetOptions().GetMapEntry()
+		for _, fd := range md.GetField() {
+			var f Field
+			f.Name = protoreflect.Name(fd.GetName())
+			f.Number = protoreflect.FieldNumber(fd.GetNumber())
+			f.Cardinality = protoreflect.Cardinality(fd.GetLabel())
+			f.Kind = protoreflect.Kind(fd.GetType())
+			f.JSONName = fd.GetJsonName()
+			f.IsPacked = fd.GetOptions().GetPacked()
+			f.IsWeak = fd.GetOptions().GetWeak()
+			if fd.DefaultValue != nil {
+				f.Default, err = parseDefault(fd.GetDefaultValue(), f.Kind)
+				if err != nil {
+					return nil, err
+				}
+			}
+			if fd.OneofIndex != nil {
+				i := int(fd.GetOneofIndex())
+				if i >= len(md.GetOneofDecl()) {
+					return nil, errors.New("invalid oneof index: %d", i)
+				}
+				f.OneofName = protoreflect.Name(md.GetOneofDecl()[i].GetName())
+			}
+			switch f.Kind {
+			case protoreflect.EnumKind:
+				f.EnumType, err = findEnumDescriptor(fd.GetTypeName(), r)
+				if err != nil {
+					return nil, err
+				}
+				if f.IsWeak && !f.EnumType.IsPlaceholder() {
+					f.EnumType = PlaceholderEnum(f.EnumType.FullName())
+				}
+			case protoreflect.MessageKind, protoreflect.GroupKind:
+				f.MessageType, err = findMessageDescriptor(fd.GetTypeName(), r)
+				if err != nil {
+					return nil, err
+				}
+				if f.IsWeak && !f.MessageType.IsPlaceholder() {
+					f.MessageType = PlaceholderMessage(f.MessageType.FullName())
+				}
+			}
+			m.Fields = append(m.Fields, f)
+		}
+		for _, od := range md.GetOneofDecl() {
+			m.Oneofs = append(m.Oneofs, Oneof{Name: protoreflect.Name(od.GetName())})
+		}
+		for _, xr := range md.GetExtensionRange() {
+			m.ExtensionRanges = append(m.ExtensionRanges, [2]protoreflect.FieldNumber{
+				protoreflect.FieldNumber(xr.GetStart()),
+				protoreflect.FieldNumber(xr.GetEnd()),
+			})
+		}
+
+		m.Messages, err = messagesFromDescriptorProto(md.GetNestedType(), r)
+		if err != nil {
+			return nil, err
+		}
+		m.Enums, err = enumsFromDescriptorProto(md.GetEnumType(), r)
+		if err != nil {
+			return nil, err
+		}
+		m.Extensions, err = extensionsFromDescriptorProto(md.GetExtension(), r)
+		if err != nil {
+			return nil, err
+		}
+
+		ms = append(ms, m)
+	}
+	return ms, nil
+}
+
+func enumsFromDescriptorProto(eds []*descriptorV1.EnumDescriptorProto, r *protoregistry.Files) (es []Enum, err error) {
+	for _, ed := range eds {
+		var e Enum
+		e.Name = protoreflect.Name(ed.GetName())
+		for _, vd := range ed.GetValue() {
+			e.Values = append(e.Values, EnumValue{
+				Name:   protoreflect.Name(vd.GetName()),
+				Number: protoreflect.EnumNumber(vd.GetNumber()),
+			})
+		}
+		es = append(es, e)
+	}
+	return es, nil
+}
+
+func extensionsFromDescriptorProto(xds []*descriptorV1.FieldDescriptorProto, r *protoregistry.Files) (xs []Extension, err error) {
+	for _, xd := range xds {
+		var x Extension
+		x.Name = protoreflect.Name(xd.GetName())
+		x.Number = protoreflect.FieldNumber(xd.GetNumber())
+		x.Cardinality = protoreflect.Cardinality(xd.GetLabel())
+		x.Kind = protoreflect.Kind(xd.GetType())
+		x.IsPacked = xd.GetOptions().GetPacked()
+		if xd.DefaultValue != nil {
+			x.Default, err = parseDefault(xd.GetDefaultValue(), x.Kind)
+			if err != nil {
+				return nil, err
+			}
+		}
+		switch x.Kind {
+		case protoreflect.EnumKind:
+			x.EnumType, err = findEnumDescriptor(xd.GetTypeName(), r)
+			if err != nil {
+				return nil, err
+			}
+		case protoreflect.MessageKind, protoreflect.GroupKind:
+			x.MessageType, err = findMessageDescriptor(xd.GetTypeName(), r)
+			if err != nil {
+				return nil, err
+			}
+		}
+		x.ExtendedType, err = findMessageDescriptor(xd.GetExtendee(), r)
+		if err != nil {
+			return nil, err
+		}
+		xs = append(xs, x)
+	}
+	return xs, nil
+}
+
+func servicesFromDescriptorProto(sds []*descriptorV1.ServiceDescriptorProto, r *protoregistry.Files) (ss []Service, err error) {
+	for _, sd := range sds {
+		var s Service
+		s.Name = protoreflect.Name(sd.GetName())
+		for _, md := range sd.GetMethod() {
+			var m Method
+			m.Name = protoreflect.Name(md.GetName())
+			m.InputType, err = findMessageDescriptor(md.GetInputType(), r)
+			if err != nil {
+				return nil, err
+			}
+			m.OutputType, err = findMessageDescriptor(md.GetOutputType(), r)
+			if err != nil {
+				return nil, err
+			}
+			m.IsStreamingClient = md.GetClientStreaming()
+			m.IsStreamingServer = md.GetServerStreaming()
+			s.Methods = append(s.Methods, m)
+		}
+		ss = append(ss, s)
+	}
+	return ss, nil
+}
+
+// TODO: Should we allow relative names? The protoc compiler has emitted
+// absolute names for some time now. Requiring absolute names as an input
+// simplifies our implementation as we won't need to implement C++'s namespace
+// scoping rules.
+
+func findMessageDescriptor(s string, r *protoregistry.Files) (protoreflect.MessageDescriptor, error) {
+	if !strings.HasPrefix(s, ".") {
+		return nil, errors.New("identifier name must be fully qualified with a leading dot: %v", s)
+	}
+	name := protoreflect.FullName(strings.TrimPrefix(s, "."))
+	switch m, err := r.FindDescriptorByName(name); {
+	case err == nil:
+		m, ok := m.(protoreflect.MessageDescriptor)
+		if !ok {
+			return nil, errors.New("resolved wrong type: got %v, want message", typeName(m))
+		}
+		return m, nil
+	case err == protoregistry.NotFound:
+		return PlaceholderMessage(name), nil
+	default:
+		return nil, err
+	}
+}
+
+func findEnumDescriptor(s string, r *protoregistry.Files) (protoreflect.EnumDescriptor, error) {
+	if !strings.HasPrefix(s, ".") {
+		return nil, errors.New("identifier name must be fully qualified with a leading dot: %v", s)
+	}
+	name := protoreflect.FullName(strings.TrimPrefix(s, "."))
+	switch e, err := r.FindDescriptorByName(name); {
+	case err == nil:
+		e, ok := e.(protoreflect.EnumDescriptor)
+		if !ok {
+			return nil, errors.New("resolved wrong type: got %T, want enum", typeName(e))
+		}
+		return e, nil
+	case err == protoregistry.NotFound:
+		return PlaceholderEnum(name), nil
+	default:
+		return nil, err
+	}
+}
+
+func typeName(t protoreflect.Descriptor) string {
+	switch t.(type) {
+	case protoreflect.EnumType:
+		return "enum"
+	case protoreflect.MessageType:
+		return "message"
+	default:
+		return fmt.Sprintf("%T", t)
+	}
+}
+
+func parseDefault(s string, k protoreflect.Kind) (protoreflect.Value, error) {
+	switch k {
+	case protoreflect.BoolKind:
+		switch s {
+		case "true":
+			return protoreflect.ValueOf(true), nil
+		case "false":
+			return protoreflect.ValueOf(false), nil
+		}
+	case protoreflect.EnumKind:
+		// For enums, we are supposed to return a protoreflect.EnumNumber type.
+		// However, default values record the name instead of the number.
+		// We are unable to resolve the name into a number without additional
+		// type information. Thus, we temporarily return the name identifier
+		// for now and rely on logic in defaultValue.lazyInit to resolve it.
+		return protoreflect.ValueOf(s), nil
+	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
+		v, err := strconv.ParseInt(s, 0, 32)
+		if err == nil {
+			return protoreflect.ValueOf(int32(v)), nil
+		}
+	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
+		v, err := strconv.ParseInt(s, 0, 64)
+		if err == nil {
+			return protoreflect.ValueOf(int64(v)), nil
+		}
+	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
+		v, err := strconv.ParseUint(s, 0, 32)
+		if err == nil {
+			return protoreflect.ValueOf(uint64(v)), nil
+		}
+	case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
+		v, err := strconv.ParseUint(s, 0, 64)
+		if err == nil {
+			return protoreflect.ValueOf(uint64(v)), nil
+		}
+	case protoreflect.FloatKind, protoreflect.DoubleKind:
+		var v float64
+		var err error
+		switch s {
+		case "nan":
+			v = math.NaN()
+		case "inf":
+			v = math.Inf(+1)
+		case "-inf":
+			v = math.Inf(-1)
+		default:
+			v, err = strconv.ParseFloat(s, 64)
+		}
+		if err == nil {
+			if k == protoreflect.FloatKind {
+				return protoreflect.ValueOf(float32(v)), nil
+			}
+			return protoreflect.ValueOf(float64(v)), nil
+		}
+	case protoreflect.StringKind, protoreflect.BytesKind:
+		// String values use the same escaping as the text format,
+		// however they lack the surrounding double quotes.
+		// TODO: Export unmarshalString in the text package to avoid this hack.
+		v, err := text.Unmarshal([]byte(`["` + s + `"]:0`))
+		if err == nil && len(v.Message()) == 1 {
+			s := v.Message()[0][0].String()
+			if k == protoreflect.StringKind {
+				return protoreflect.ValueOf(s), nil
+			}
+			return protoreflect.ValueOf([]byte(s)), nil
+		}
+	}
+	return protoreflect.Null, errors.New("invalid default value for %v: %q", k, s)
+}
diff --git a/reflect/prototype/protofile_type.go b/reflect/prototype/protofile_type.go
index e82de98..6650126 100644
--- a/reflect/prototype/protofile_type.go
+++ b/reflect/prototype/protofile_type.go
@@ -427,7 +427,21 @@
 	p.once.Do(func() {
 		p.val = v
 		if !v.IsNull() {
-			if t.Kind() == pref.BytesKind {
+			switch t.Kind() {
+			case pref.EnumKind:
+				// Treat a string value as an identifier referencing some enum
+				// value by name and extract the enum number.
+				// If this fails, validateMessage will later detect that the
+				// default value for an enum value is the wrong type.
+				if s, ok := v.Interface().(string); ok {
+					v := t.EnumType().Values().ByName(pref.Name(s))
+					if v != nil {
+						p.val = pref.ValueOf(v.Number())
+					}
+				}
+			case pref.BytesKind:
+				// Store a copy of the default bytes, so that we can detect
+				// accidental mutations of the original value.
 				if b, ok := v.Interface().([]byte); ok && len(b) > 0 {
 					p.buf = append([]byte(nil), b...)
 				}
diff --git a/reflect/prototype/type_test.go b/reflect/prototype/type_test.go
index a5ef61d..ab7ad72 100644
--- a/reflect/prototype/type_test.go
+++ b/reflect/prototype/type_test.go
@@ -8,9 +8,11 @@
 	"reflect"
 	"strconv"
 	"strings"
-	"sync"
 	"testing"
 
+	protoV1 "github.com/golang/protobuf/proto"
+	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
+
 	pref "google.golang.org/proto/reflect/protoreflect"
 )
 
@@ -61,8 +63,10 @@
 	}
 }
 
+// TODO: Test NewFileFromDescriptorProto with imported files.
+
 func TestFile(t *testing.T) {
-	f := &File{
+	f1 := &File{
 		Syntax:  pref.Proto2,
 		Path:    "path/to/file.proto",
 		Package: "test",
@@ -159,7 +163,7 @@
 			Number:       1000,
 			Cardinality:  pref.Repeated,
 			Kind:         pref.MessageKind,
-			IsPacked:     false,
+			IsPacked:     true,
 			MessageType:  PlaceholderMessage("test.C"),
 			ExtendedType: PlaceholderMessage("test.B"),
 		}},
@@ -174,12 +178,159 @@
 			}},
 		}},
 	}
-
-	fd, err := NewFile(f)
+	fd1, err := NewFile(f1)
 	if err != nil {
 		t.Fatalf("NewFile() error: %v", err)
 	}
 
+	f2 := &descriptorV1.FileDescriptorProto{
+		Syntax:  protoV1.String("proto2"),
+		Name:    protoV1.String("path/to/file.proto"),
+		Package: protoV1.String("test"),
+		MessageType: []*descriptorV1.DescriptorProto{{
+			Name:    protoV1.String("A"),
+			Options: &descriptorV1.MessageOptions{MapEntry: protoV1.Bool(true)},
+			Field: []*descriptorV1.FieldDescriptorProto{{
+				Name:   protoV1.String("key"),
+				Number: protoV1.Int32(1),
+				Label:  descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:   descriptorV1.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+			}, {
+				Name:     protoV1.String("value"),
+				Number:   protoV1.Int32(2),
+				Label:    descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:     descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName: protoV1.String(".test.B"),
+			}},
+		}, {
+			Name: protoV1.String("B"),
+			Field: []*descriptorV1.FieldDescriptorProto{{
+				Name:         protoV1.String("field_one"),
+				Number:       protoV1.Int32(1),
+				Label:        descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:         descriptorV1.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+				DefaultValue: protoV1.String("hello"),
+				OneofIndex:   protoV1.Int32(0),
+			}, {
+				Name:         protoV1.String("field_two"),
+				JsonName:     protoV1.String("Field2"),
+				Number:       protoV1.Int32(2),
+				Label:        descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:         descriptorV1.FieldDescriptorProto_Type(pref.EnumKind).Enum(),
+				DefaultValue: protoV1.String("BAR"),
+				TypeName:     protoV1.String(".test.E1"),
+				OneofIndex:   protoV1.Int32(1),
+			}, {
+				Name:       protoV1.String("field_three"),
+				Number:     protoV1.Int32(3),
+				Label:      descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:       descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName:   protoV1.String(".test.C"),
+				OneofIndex: protoV1.Int32(1),
+			}, {
+				Name:     protoV1.String("field_four"),
+				JsonName: protoV1.String("Field4"),
+				Number:   protoV1.Int32(4),
+				Label:    descriptorV1.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:     descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName: protoV1.String(".test.A"),
+			}, {
+				Name:    protoV1.String("field_five"),
+				Number:  protoV1.Int32(5),
+				Label:   descriptorV1.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:    descriptorV1.FieldDescriptorProto_Type(pref.Int32Kind).Enum(),
+				Options: &descriptorV1.FieldOptions{Packed: protoV1.Bool(true)},
+			}, {
+				Name:   protoV1.String("field_six"),
+				Number: protoV1.Int32(6),
+				Label:  descriptorV1.FieldDescriptorProto_Label(pref.Required).Enum(),
+				Type:   descriptorV1.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+			}},
+			OneofDecl: []*descriptorV1.OneofDescriptorProto{
+				{Name: protoV1.String("O1")},
+				{Name: protoV1.String("O2")},
+			},
+			ExtensionRange: []*descriptorV1.DescriptorProto_ExtensionRange{
+				{Start: protoV1.Int32(1000), End: protoV1.Int32(2000)},
+			},
+		}, {
+			Name: protoV1.String("C"),
+			NestedType: []*descriptorV1.DescriptorProto{{
+				Name: protoV1.String("A"),
+				Field: []*descriptorV1.FieldDescriptorProto{{
+					Name:   protoV1.String("F"),
+					Number: protoV1.Int32(1),
+					Label:  descriptorV1.FieldDescriptorProto_Label(pref.Required).Enum(),
+					Type:   descriptorV1.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
+				}},
+			}},
+			EnumType: []*descriptorV1.EnumDescriptorProto{{
+				Name: protoV1.String("E1"),
+				Value: []*descriptorV1.EnumValueDescriptorProto{
+					{Name: protoV1.String("FOO"), Number: protoV1.Int32(0)},
+					{Name: protoV1.String("BAR"), Number: protoV1.Int32(1)},
+				},
+			}},
+			Extension: []*descriptorV1.FieldDescriptorProto{{
+				Name:     protoV1.String("X"),
+				Number:   protoV1.Int32(1000),
+				Label:    descriptorV1.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:     descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName: protoV1.String(".test.C"),
+				Extendee: protoV1.String(".test.B"),
+			}},
+		}},
+		EnumType: []*descriptorV1.EnumDescriptorProto{{
+			Name: protoV1.String("E1"),
+			Value: []*descriptorV1.EnumValueDescriptorProto{
+				{Name: protoV1.String("FOO"), Number: protoV1.Int32(0)},
+				{Name: protoV1.String("BAR"), Number: protoV1.Int32(1)},
+			},
+		}},
+		Extension: []*descriptorV1.FieldDescriptorProto{{
+			Name:     protoV1.String("X"),
+			Number:   protoV1.Int32(1000),
+			Label:    descriptorV1.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+			Type:     descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+			Options:  &descriptorV1.FieldOptions{Packed: protoV1.Bool(true)},
+			TypeName: protoV1.String(".test.C"),
+			Extendee: protoV1.String(".test.B"),
+		}},
+		Service: []*descriptorV1.ServiceDescriptorProto{{
+			Name: protoV1.String("S"),
+			Method: []*descriptorV1.MethodDescriptorProto{{
+				Name:            protoV1.String("M"),
+				InputType:       protoV1.String(".test.A"),
+				OutputType:      protoV1.String(".test.C.A"),
+				ClientStreaming: protoV1.Bool(true),
+				ServerStreaming: protoV1.Bool(true),
+			}},
+		}},
+	}
+	fd2, err := NewFileFromDescriptorProto(f2, nil)
+	if err != nil {
+		t.Fatalf("NewFileFromDescriptorProto() error: %v", err)
+	}
+
+	tests := []struct {
+		name string
+		desc pref.FileDescriptor
+	}{
+		{"NewFile", fd1},
+		{"NewFileFromDescriptorProto", fd2},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Run("Accessors", func(t *testing.T) {
+				// Run sub-tests in parallel to induce potential races.
+				t.Run("", func(t *testing.T) { t.Parallel(); testFileAccessors(t, tt.desc) })
+				t.Run("", func(t *testing.T) { t.Parallel(); testFileAccessors(t, tt.desc) })
+			})
+		})
+	}
+}
+
+func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
 	// Represent the descriptor as a map where each key is an accessor method
 	// and the value is either the wanted tail value or another accessor map.
 	type M = map[string]interface{}
@@ -359,7 +510,7 @@
 				"Number":       pref.FieldNumber(1000),
 				"Cardinality":  pref.Repeated,
 				"Kind":         pref.MessageKind,
-				"IsPacked":     false,
+				"IsPacked":     true,
 				"MessageType":  M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
 				"ExtendedType": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
 			},
@@ -413,18 +564,7 @@
 		"DescriptorByName:test.S.M":         M{"FullName": pref.FullName("test.S.M")},
 		"DescriptorByName:test.M":           nil,
 	}
-
-	// Concurrently explore the file tree to induce races.
-	const numGoRoutines = 2
-	var wg sync.WaitGroup
-	defer wg.Wait()
-	for i := 0; i < numGoRoutines; i++ {
-		wg.Add(1)
-		go func() {
-			defer wg.Done()
-			checkAccessors(t, "", reflect.ValueOf(fd), want)
-		}()
-	}
+	checkAccessors(t, "", reflect.ValueOf(fd), want)
 }
 
 func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]interface{}) {
diff --git a/reflect/prototype/validate.go b/reflect/prototype/validate.go
index adbb35c..a0bb1db 100644
--- a/reflect/prototype/validate.go
+++ b/reflect/prototype/validate.go
@@ -10,6 +10,22 @@
 
 // TODO: This is important to prevent users from creating invalid types,
 // but is not functionality needed now.
+//
+// Things to verify:
+//	* Weak fields are only used if flags.Proto1Legacy is set
+//	* Weak fields can only reference singular messages
+//	(check if this the case for oneof fields)
+//	* FieldDescriptor.MessageType cannot reference a remote type when the
+//	remote name is a type within the local file.
+//	* Default enum identifiers resolve to a declared number.
+//	* Default values are only allowed in proto2.
+//	* Default strings are valid UTF-8? Note that protoc does not check this.
+//	* Field extensions are only valid in proto2, except when extending the
+//	descriptor options.
+//	* Remote enum and message types are actually found in imported files.
+//	* Placeholder messages and types may only be for weak fields.
+//	* Placeholder full names must be valid.
+//	* The name of each descriptor must be valid.
 
 func validateFile(t pref.FileDescriptor) error {
 	return nil