internal/filedesc, internal/filetype: initial commit

The internal/fileinit package is split apart into two packages:
* internal/filedesc constructs descriptors from the raw proto.
It is very similar to the previous internal/fileinit package.
* internal/filetype wraps descriptors with Go type information

Overview:
* The internal/fileinit package will be deleted in a future CL.
It is kept around since the v1 repo currently depends on it.
* The internal/prototype package is deleted. All former usages of it
are now using internal/filedesc instead. Most significantly,
the reflect/protodesc package was almost entirely re-written.
* The internal/impl package drops support for messages that do not
have a Descriptor method (pre-2016). This removes a significant amount
of technical debt.
filedesc.Builder to parse raw descriptors.
* The internal/encoding/defval package now handles enum values by name.

Change-Id: I3957bcc8588a70470fd6c7de1122216b80615ab7
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/182360
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/filedesc/build.go b/internal/filedesc/build.go
new file mode 100644
index 0000000..28bd370
--- /dev/null
+++ b/internal/filedesc/build.go
@@ -0,0 +1,150 @@
+// Copyright 2019 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 filedesc provides functionality for constructing descriptors.
+package filedesc
+
+import (
+	"google.golang.org/protobuf/internal/encoding/wire"
+	"google.golang.org/protobuf/internal/fieldnum"
+	"google.golang.org/protobuf/reflect/protoreflect"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+	preg "google.golang.org/protobuf/reflect/protoregistry"
+)
+
+// DescBuilder construct a protoreflect.FileDescriptor from the raw descriptor.
+type DescBuilder struct {
+	// RawDescriptor is the wire-encoded bytes of FileDescriptorProto
+	// and must be populated.
+	RawDescriptor []byte
+
+	// NumEnums is the total number of enums declared in the file.
+	NumEnums int32
+	// NumMessages is the total number of messages declared in the file.
+	// It includes the implicit message declarations for map entries.
+	NumMessages int32
+	// NumExtensions is the total number of extensions declared in the file.
+	NumExtensions int32
+	// NumServices is the total number of services declared in the file.
+	NumServices int32
+
+	// TypeResolver resolves extension field types for descriptor options.
+	// If nil, it uses protoregistry.GlobalTypes.
+	TypeResolver interface {
+		preg.ExtensionTypeResolver
+	}
+
+	// FileRegistry is use to lookup file, enum, and message dependencies.
+	// Once constructed, the file descriptor is registered here.
+	// If nil, it uses protoregistry.GlobalFiles.
+	FileRegistry interface {
+		FindFileByPath(string) (protoreflect.FileDescriptor, error)
+		FindEnumByName(pref.FullName) (pref.EnumDescriptor, error)
+		FindMessageByName(pref.FullName) (pref.MessageDescriptor, error)
+		Register(...pref.FileDescriptor) error
+	}
+}
+
+// resolverByIndex is an interface DescBuilder.FileRegistry may implement.
+// If so, it permits looking up an enum or message dependency based on the
+// sub-list and element index into filetype.TypeBuilder.DependencyIndexes.
+type resolverByIndex interface {
+	FindEnumByIndex(int32, int32, []Enum, []Message) pref.EnumDescriptor
+	FindMessageByIndex(int32, int32, []Enum, []Message) pref.MessageDescriptor
+}
+
+// Indexes of each sub-list in filetype.TypeBuilder.DependencyIndexes.
+const (
+	listFieldDeps int32 = iota
+	listExtTargets
+	listExtDeps
+	listMethInDeps
+	listMethOutDeps
+)
+
+// Build constructs a FileDescriptor given the parameters set in DescBuilder.
+// It assumes that the inputs are well-formed and panics if any inconsistencies
+// are encountered.
+//
+// If NumEnums+NumMessages+NumExtensions+NumServices is zero,
+// then Build automatically derives them from the raw descriptor.
+func (db DescBuilder) Build() (out struct {
+	File pref.FileDescriptor
+
+	// Enums is all enum descriptors in "flattened ordering".
+	Enums []Enum
+	// Messages is all message descriptors in "flattened ordering".
+	// It includes the implicit message declarations for map entries.
+	Messages []Message
+	// Extensions is all extension descriptors in "flattened ordering".
+	Extensions []Extension
+	// Service is all service descriptors in "flattened ordering".
+	Services []Service
+}) {
+	// Populate the counts if uninitialized.
+	if db.NumEnums+db.NumMessages+db.NumExtensions+db.NumServices == 0 {
+		db.unmarshalCounts(db.RawDescriptor, true)
+	}
+
+	// Initialize resolvers and registries if unpopulated.
+	if db.TypeResolver == nil {
+		db.TypeResolver = preg.GlobalTypes
+	}
+	if db.FileRegistry == nil {
+		db.FileRegistry = preg.GlobalFiles
+	}
+
+	fd := newRawFile(db)
+	out.File = fd
+	out.Enums = fd.allEnums
+	out.Messages = fd.allMessages
+	out.Extensions = fd.allExtensions
+	out.Services = fd.allServices
+
+	if err := db.FileRegistry.Register(fd); err != nil {
+		panic(err)
+	}
+	return out
+}
+
+// unmarshalCounts counts the number of enum, message, extension, and service
+// declarations in the raw message, which is either a FileDescriptorProto
+// or a MessageDescriptorProto depending on whether isFile is set.
+func (db *DescBuilder) unmarshalCounts(b []byte, isFile bool) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			if isFile {
+				switch num {
+				case fieldnum.FileDescriptorProto_EnumType:
+					db.NumEnums++
+				case fieldnum.FileDescriptorProto_MessageType:
+					db.unmarshalCounts(v, false)
+					db.NumMessages++
+				case fieldnum.FileDescriptorProto_Extension:
+					db.NumExtensions++
+				case fieldnum.FileDescriptorProto_Service:
+					db.NumServices++
+				}
+			} else {
+				switch num {
+				case fieldnum.DescriptorProto_EnumType:
+					db.NumEnums++
+				case fieldnum.DescriptorProto_NestedType:
+					db.unmarshalCounts(v, false)
+					db.NumMessages++
+				case fieldnum.DescriptorProto_Extension:
+					db.NumExtensions++
+				}
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
diff --git a/internal/filedesc/build_test.go b/internal/filedesc/build_test.go
new file mode 100644
index 0000000..edeb56e
--- /dev/null
+++ b/internal/filedesc/build_test.go
@@ -0,0 +1,117 @@
+// 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 filedesc_test
+
+import (
+	"bytes"
+	"compress/gzip"
+	"io/ioutil"
+	"testing"
+
+	proto "github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protodesc"
+	"google.golang.org/protobuf/reflect/protoreflect"
+
+	testpb "google.golang.org/protobuf/internal/testprotos/test"
+	"google.golang.org/protobuf/types/descriptorpb"
+)
+
+func TestInit(t *testing.T) {
+	// Compare the FileDescriptorProto for the same test file from two different sources:
+	//
+	// 1. The result of passing the filedesc-produced FileDescriptor through protodesc.
+	// 2. The protoc-generated wire-encoded message.
+	//
+	// This serves as a test of both filedesc and protodesc.
+	got := protodesc.ToFileDescriptorProto(testpb.File_test_test_proto)
+
+	want := &descriptorpb.FileDescriptorProto{}
+	zb, _ := (&testpb.TestAllTypes{}).Descriptor()
+	r, _ := gzip.NewReader(bytes.NewBuffer(zb))
+	b, _ := ioutil.ReadAll(r)
+	if err := proto.Unmarshal(b, want); err != nil {
+		t.Fatal(err)
+	}
+
+	if !proto.Equal(got, want) {
+		t.Errorf("protodesc.ToFileDescriptorProto(testpb.Test_protoFile) is not equal to the protoc-generated FileDescriptorProto for internal/testprotos/test/test.proto")
+	}
+
+	// Verify that the test proto file provides exhaustive coverage of all descriptor fields.
+	seen := make(map[protoreflect.FullName]bool)
+	visitFields(want.ProtoReflect(), func(field protoreflect.FieldDescriptor) {
+		seen[field.FullName()] = true
+	})
+	ignore := map[protoreflect.FullName]bool{
+		// The protoreflect descriptors don't include source info.
+		"google.protobuf.FileDescriptorProto.source_code_info": true,
+		"google.protobuf.FileDescriptorProto.syntax":           true,
+
+		// TODO: Test oneof and extension options. Testing these requires extending the
+		// options messages (because they contain no user-settable fields), but importing
+		// decriptor.proto from test.proto currently causes an import cycle. Add test
+		// cases when that import cycle has been fixed.
+		"google.protobuf.OneofDescriptorProto.options": true,
+	}
+	for _, messageName := range []protoreflect.Name{
+		"FileDescriptorProto",
+		"DescriptorProto",
+		"FieldDescriptorProto",
+		"OneofDescriptorProto",
+		"EnumDescriptorProto",
+		"EnumValueDescriptorProto",
+		"ServiceDescriptorProto",
+		"MethodDescriptorProto",
+	} {
+		message := descriptorpb.File_google_protobuf_descriptor_proto.Messages().ByName(messageName)
+		for i, fields := 0, message.Fields(); i < fields.Len(); i++ {
+			if name := fields.Get(i).FullName(); !seen[name] && !ignore[name] {
+				t.Errorf("No test for descriptor field: %v", name)
+			}
+		}
+	}
+
+	// Verify that message descriptors for map entries have no Go type info.
+	mapEntryName := protoreflect.FullName("goproto.proto.test.TestAllTypes.MapInt32Int32Entry")
+	d := testpb.File_test_test_proto.Messages().ByName("TestAllTypes").Fields().ByName("map_int32_int32").Message()
+	if gotName, wantName := d.FullName(), mapEntryName; gotName != wantName {
+		t.Fatalf("looked up wrong descriptor: got %v, want %v", gotName, wantName)
+	}
+	if _, ok := d.(protoreflect.MessageType); ok {
+		t.Errorf("message descriptor for %v must not implement protoreflect.MessageType", mapEntryName)
+	}
+}
+
+// visitFields calls f for every field set in m and its children.
+func visitFields(m protoreflect.Message, f func(protoreflect.FieldDescriptor)) {
+	m.Range(func(fd protoreflect.FieldDescriptor, value protoreflect.Value) bool {
+		f(fd)
+		switch fd.Kind() {
+		case protoreflect.MessageKind, protoreflect.GroupKind:
+			if fd.IsList() {
+				for i, list := 0, value.List(); i < list.Len(); i++ {
+					visitFields(list.Get(i).Message(), f)
+				}
+			} else {
+				visitFields(value.Message(), f)
+			}
+		}
+		return true
+	})
+}
+
+func TestWeakInit(t *testing.T) {
+	file := testpb.File_test_test_proto
+	fd := file.Messages().ByName("TestWeak").Fields().ByName("weak_message")
+	if want, got := fd.IsWeak(), true; got != want {
+		t.Errorf("field %v: IsWeak() = %v, want %v", fd.FullName(), want, got)
+	}
+	if want, got := fd.Message().IsPlaceholder(), false; got != want {
+		t.Errorf("field %v: Message.IsPlaceholder() = %v, want %v", fd.FullName(), want, got)
+	}
+	if fd.Message().Fields().Len() == 0 {
+		t.Errorf("field %v: Message().Fields().Len() == 0, want >0", fd.FullName())
+	}
+}
diff --git a/internal/filedesc/desc.go b/internal/filedesc/desc.go
new file mode 100644
index 0000000..40c47dc
--- /dev/null
+++ b/internal/filedesc/desc.go
@@ -0,0 +1,555 @@
+// Copyright 2019 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 filedesc
+
+import (
+	"bytes"
+	"fmt"
+	"sync"
+	"sync/atomic"
+
+	"google.golang.org/protobuf/internal/descfmt"
+	"google.golang.org/protobuf/internal/descopts"
+	"google.golang.org/protobuf/internal/encoding/defval"
+	"google.golang.org/protobuf/internal/pragma"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// The types in this file may have a suffix:
+//	• L0: Contains fields common to all descriptors (except File) and
+//	must be initialized up front.
+//	• L1: Contains fields specific to a descriptor and
+//	must be initialized up front.
+//	• L2: Contains fields that are lazily initialized when constructing
+//	from the raw file descriptor. When constructing as a literal, the L2
+//	fields must be initialized up front.
+//
+// The types are exported so that packages like reflect/protodesc can
+// directly construct descriptors.
+
+type (
+	File struct {
+		fileRaw
+		L1 FileL1
+
+		once uint32     // atomically set if L2 is valid
+		mu   sync.Mutex // protects L2
+		L2   *FileL2
+	}
+	FileL1 struct {
+		Syntax  pref.Syntax
+		Path    string
+		Package pref.FullName
+
+		Enums      Enums
+		Messages   Messages
+		Extensions Extensions
+		Services   Services
+	}
+	FileL2 struct {
+		Options func() pref.ProtoMessage
+		Imports FileImports
+	}
+)
+
+func (fd *File) ParentFile() pref.FileDescriptor { return fd }
+func (fd *File) Parent() pref.Descriptor         { return nil }
+func (fd *File) Index() int                      { return 0 }
+func (fd *File) Syntax() pref.Syntax             { return fd.L1.Syntax }
+func (fd *File) Name() pref.Name                 { return fd.L1.Package.Name() }
+func (fd *File) FullName() pref.FullName         { return fd.L1.Package }
+func (fd *File) IsPlaceholder() bool             { return false }
+func (fd *File) Options() pref.ProtoMessage {
+	if f := fd.lazyInit().Options; f != nil {
+		return f()
+	}
+	return descopts.File
+}
+func (fd *File) Path() string                          { return fd.L1.Path }
+func (fd *File) Package() pref.FullName                { return fd.L1.Package }
+func (fd *File) Imports() pref.FileImports             { return &fd.lazyInit().Imports }
+func (fd *File) Enums() pref.EnumDescriptors           { return &fd.L1.Enums }
+func (fd *File) Messages() pref.MessageDescriptors     { return &fd.L1.Messages }
+func (fd *File) Extensions() pref.ExtensionDescriptors { return &fd.L1.Extensions }
+func (fd *File) Services() pref.ServiceDescriptors     { return &fd.L1.Services }
+func (fd *File) Format(s fmt.State, r rune)            { descfmt.FormatDesc(s, r, fd) }
+func (fd *File) ProtoType(pref.FileDescriptor)         {}
+func (fd *File) ProtoInternal(pragma.DoNotImplement)   {}
+
+func (fd *File) lazyInit() *FileL2 {
+	if atomic.LoadUint32(&fd.once) == 0 {
+		fd.lazyInitOnce()
+	}
+	return fd.L2
+}
+
+func (fd *File) lazyInitOnce() {
+	fd.mu.Lock()
+	if fd.L2 == nil {
+		fd.lazyRawInit() // recursively initializes all L2 structures
+	}
+	atomic.StoreUint32(&fd.once, 1)
+	fd.mu.Unlock()
+}
+
+// ProtoLegacyRawDesc is a pseudo-internal API for allowing the v1 code
+// to be able to retrieve the raw descriptor.
+//
+// WARNING: This method is exempt from the compatibility promise and may be
+// removed in the future without warning.
+func (fd *File) ProtoLegacyRawDesc() []byte {
+	return fd.builder.RawDescriptor
+}
+
+type (
+	Enum struct {
+		Base
+		L1 EnumL1
+		L2 *EnumL2 // protected by fileDesc.once
+	}
+	EnumL1 struct{}
+	EnumL2 struct {
+		Options        func() pref.ProtoMessage
+		Values         EnumValues
+		ReservedNames  Names
+		ReservedRanges EnumRanges
+	}
+
+	EnumValue struct {
+		Base
+		L1 EnumValueL1
+	}
+	EnumValueL1 struct {
+		Options func() pref.ProtoMessage
+		Number  pref.EnumNumber
+	}
+)
+
+func (ed *Enum) Options() pref.ProtoMessage {
+	if f := ed.lazyInit().Options; f != nil {
+		return f()
+	}
+	return descopts.Enum
+}
+func (ed *Enum) Values() pref.EnumValueDescriptors { return &ed.lazyInit().Values }
+func (ed *Enum) ReservedNames() pref.Names         { return &ed.lazyInit().ReservedNames }
+func (ed *Enum) ReservedRanges() pref.EnumRanges   { return &ed.lazyInit().ReservedRanges }
+func (ed *Enum) Format(s fmt.State, r rune)        { descfmt.FormatDesc(s, r, ed) }
+func (ed *Enum) ProtoType(pref.EnumDescriptor)     {}
+func (ed *Enum) lazyInit() *EnumL2 {
+	ed.L0.ParentFile.lazyInit() // implicitly initializes L2
+	return ed.L2
+}
+
+func (ed *EnumValue) Options() pref.ProtoMessage {
+	if f := ed.L1.Options; f != nil {
+		return f()
+	}
+	return descopts.EnumValue
+}
+func (ed *EnumValue) Number() pref.EnumNumber            { return ed.L1.Number }
+func (ed *EnumValue) Format(s fmt.State, r rune)         { descfmt.FormatDesc(s, r, ed) }
+func (ed *EnumValue) ProtoType(pref.EnumValueDescriptor) {}
+
+type (
+	Message struct {
+		Base
+		L1 MessageL1
+		L2 *MessageL2 // protected by fileDesc.once
+	}
+	MessageL1 struct {
+		Enums      Enums
+		Messages   Messages
+		Extensions Extensions
+	}
+	MessageL2 struct {
+		Options               func() pref.ProtoMessage
+		IsMapEntry            bool // promoted from google.protobuf.MessageOptions
+		IsMessageSet          bool // promoted from google.protobuf.MessageOptions
+		Fields                Fields
+		Oneofs                Oneofs
+		ReservedNames         Names
+		ReservedRanges        FieldRanges
+		RequiredNumbers       FieldNumbers // must be consistent with Fields.Cardinality
+		ExtensionRanges       FieldRanges
+		ExtensionRangeOptions []func() pref.ProtoMessage // must be same length as ExtensionRanges
+	}
+
+	Field struct {
+		Base
+		L1 FieldL1
+	}
+	FieldL1 struct {
+		Options         func() pref.ProtoMessage
+		Number          pref.FieldNumber
+		Cardinality     pref.Cardinality // must be consistent with Message.RequiredNumbers
+		Kind            pref.Kind
+		JSONName        jsonName
+		IsWeak          bool // promoted from google.protobuf.FieldOptions
+		HasPacked       bool // promoted from google.protobuf.FieldOptions
+		IsPacked        bool // promoted from google.protobuf.FieldOptions
+		Default         defaultValue
+		ContainingOneof pref.OneofDescriptor // must be consistent with Message.Oneofs.Fields
+		Enum            pref.EnumDescriptor
+		Message         pref.MessageDescriptor
+	}
+
+	Oneof struct {
+		Base
+		L1 OneofL1
+	}
+	OneofL1 struct {
+		Options func() pref.ProtoMessage
+		Fields  OneofFields // must be consistent with Message.Fields.ContainingOneof
+	}
+)
+
+func (md *Message) Options() pref.ProtoMessage {
+	if f := md.lazyInit().Options; f != nil {
+		return f()
+	}
+	return descopts.Message
+}
+func (md *Message) IsMapEntry() bool                   { return md.lazyInit().IsMapEntry }
+func (md *Message) Fields() pref.FieldDescriptors      { return &md.lazyInit().Fields }
+func (md *Message) Oneofs() pref.OneofDescriptors      { return &md.lazyInit().Oneofs }
+func (md *Message) ReservedNames() pref.Names          { return &md.lazyInit().ReservedNames }
+func (md *Message) ReservedRanges() pref.FieldRanges   { return &md.lazyInit().ReservedRanges }
+func (md *Message) RequiredNumbers() pref.FieldNumbers { return &md.lazyInit().RequiredNumbers }
+func (md *Message) ExtensionRanges() pref.FieldRanges  { return &md.lazyInit().ExtensionRanges }
+func (md *Message) ExtensionRangeOptions(i int) pref.ProtoMessage {
+	if f := md.lazyInit().ExtensionRangeOptions[i]; f != nil {
+		return f()
+	}
+	return descopts.ExtensionRange
+}
+func (md *Message) Enums() pref.EnumDescriptors           { return &md.L1.Enums }
+func (md *Message) Messages() pref.MessageDescriptors     { return &md.L1.Messages }
+func (md *Message) Extensions() pref.ExtensionDescriptors { return &md.L1.Extensions }
+func (md *Message) ProtoType(pref.MessageDescriptor)      {}
+func (md *Message) Format(s fmt.State, r rune)            { descfmt.FormatDesc(s, r, md) }
+func (md *Message) lazyInit() *MessageL2 {
+	md.L0.ParentFile.lazyInit() // implicitly initializes L2
+	return md.L2
+}
+
+// IsMessageSet is a pseudo-internal API for checking whether a message
+// should serialize in the proto1 message format.
+//
+// WARNING: This method is exempt from the compatibility promise and may be
+// removed in the future without warning.
+func (md *Message) IsMessageSet() bool {
+	return md.lazyInit().IsMessageSet
+}
+
+func (fd *Field) Options() pref.ProtoMessage {
+	if f := fd.L1.Options; f != nil {
+		return f()
+	}
+	return descopts.Field
+}
+func (fd *Field) Number() pref.FieldNumber      { return fd.L1.Number }
+func (fd *Field) Cardinality() pref.Cardinality { return fd.L1.Cardinality }
+func (fd *Field) Kind() pref.Kind               { return fd.L1.Kind }
+func (fd *Field) HasJSONName() bool             { return fd.L1.JSONName.has }
+func (fd *Field) JSONName() string              { return fd.L1.JSONName.get(fd) }
+func (fd *Field) IsPacked() bool {
+	if !fd.L1.HasPacked && fd.L0.ParentFile.L1.Syntax != pref.Proto2 && fd.L1.Cardinality == pref.Repeated {
+		switch fd.L1.Kind {
+		case pref.StringKind, pref.BytesKind, pref.MessageKind, pref.GroupKind:
+		default:
+			return true
+		}
+	}
+	return fd.L1.IsPacked
+}
+func (fd *Field) IsExtension() bool { return false }
+func (fd *Field) IsWeak() bool      { return fd.L1.IsWeak }
+func (fd *Field) IsList() bool      { return fd.Cardinality() == pref.Repeated && !fd.IsMap() }
+func (fd *Field) IsMap() bool       { return fd.Message() != nil && fd.Message().IsMapEntry() }
+func (fd *Field) MapKey() pref.FieldDescriptor {
+	if !fd.IsMap() {
+		return nil
+	}
+	return fd.Message().Fields().ByNumber(1)
+}
+func (fd *Field) MapValue() pref.FieldDescriptor {
+	if !fd.IsMap() {
+		return nil
+	}
+	return fd.Message().Fields().ByNumber(2)
+}
+func (fd *Field) HasDefault() bool                           { return fd.L1.Default.has }
+func (fd *Field) Default() pref.Value                        { return fd.L1.Default.get(fd) }
+func (fd *Field) DefaultEnumValue() pref.EnumValueDescriptor { return fd.L1.Default.enum }
+func (fd *Field) ContainingOneof() pref.OneofDescriptor      { return fd.L1.ContainingOneof }
+func (fd *Field) ContainingMessage() pref.MessageDescriptor {
+	return fd.L0.Parent.(pref.MessageDescriptor)
+}
+func (fd *Field) Enum() pref.EnumDescriptor       { return fd.L1.Enum }
+func (fd *Field) Message() pref.MessageDescriptor { return fd.L1.Message }
+func (fd *Field) Format(s fmt.State, r rune)      { descfmt.FormatDesc(s, r, fd) }
+func (fd *Field) ProtoType(pref.FieldDescriptor)  {}
+
+func (od *Oneof) Options() pref.ProtoMessage {
+	if f := od.L1.Options; f != nil {
+		return f()
+	}
+	return descopts.Oneof
+}
+func (od *Oneof) Fields() pref.FieldDescriptors  { return &od.L1.Fields }
+func (od *Oneof) Format(s fmt.State, r rune)     { descfmt.FormatDesc(s, r, od) }
+func (od *Oneof) ProtoType(pref.OneofDescriptor) {}
+
+type (
+	Extension struct {
+		Base
+		L1 ExtensionL1
+		L2 *ExtensionL2 // protected by fileDesc.once
+	}
+	ExtensionL1 struct {
+		Number   pref.FieldNumber
+		Extendee pref.MessageDescriptor
+		Kind     pref.Kind
+	}
+	ExtensionL2 struct {
+		Options     func() pref.ProtoMessage
+		Cardinality pref.Cardinality
+		JSONName    jsonName
+		IsPacked    bool // promoted from google.protobuf.FieldOptions
+		Default     defaultValue
+		Enum        pref.EnumDescriptor
+		Message     pref.MessageDescriptor
+	}
+)
+
+func (xd *Extension) Options() pref.ProtoMessage {
+	if f := xd.lazyInit().Options; f != nil {
+		return f()
+	}
+	return descopts.Field
+}
+func (xd *Extension) Number() pref.FieldNumber                   { return xd.L1.Number }
+func (xd *Extension) Cardinality() pref.Cardinality              { return xd.lazyInit().Cardinality }
+func (xd *Extension) Kind() pref.Kind                            { return xd.L1.Kind }
+func (xd *Extension) HasJSONName() bool                          { return xd.lazyInit().JSONName.has }
+func (xd *Extension) JSONName() string                           { return xd.lazyInit().JSONName.get(xd) }
+func (xd *Extension) IsPacked() bool                             { return xd.lazyInit().IsPacked }
+func (xd *Extension) IsExtension() bool                          { return true }
+func (xd *Extension) IsWeak() bool                               { return false }
+func (xd *Extension) IsList() bool                               { return xd.Cardinality() == pref.Repeated }
+func (xd *Extension) IsMap() bool                                { return false }
+func (xd *Extension) MapKey() pref.FieldDescriptor               { return nil }
+func (xd *Extension) MapValue() pref.FieldDescriptor             { return nil }
+func (xd *Extension) HasDefault() bool                           { return xd.lazyInit().Default.has }
+func (xd *Extension) Default() pref.Value                        { return xd.lazyInit().Default.get(xd) }
+func (xd *Extension) DefaultEnumValue() pref.EnumValueDescriptor { return xd.lazyInit().Default.enum }
+func (xd *Extension) ContainingOneof() pref.OneofDescriptor      { return nil }
+func (xd *Extension) ContainingMessage() pref.MessageDescriptor  { return xd.L1.Extendee }
+func (xd *Extension) Enum() pref.EnumDescriptor                  { return xd.lazyInit().Enum }
+func (xd *Extension) Message() pref.MessageDescriptor            { return xd.lazyInit().Message }
+func (xd *Extension) Format(s fmt.State, r rune)                 { descfmt.FormatDesc(s, r, xd) }
+func (xd *Extension) ProtoType(pref.FieldDescriptor)             {}
+func (xd *Extension) ProtoInternal(pragma.DoNotImplement)        {}
+func (xd *Extension) lazyInit() *ExtensionL2 {
+	xd.L0.ParentFile.lazyInit() // implicitly initializes L2
+	return xd.L2
+}
+
+type (
+	Service struct {
+		Base
+		L1 ServiceL1
+		L2 *ServiceL2 // protected by fileDesc.once
+	}
+	ServiceL1 struct{}
+	ServiceL2 struct {
+		Options func() pref.ProtoMessage
+		Methods Methods
+	}
+
+	Method struct {
+		Base
+		L1 MethodL1
+	}
+	MethodL1 struct {
+		Options           func() pref.ProtoMessage
+		Input             pref.MessageDescriptor
+		Output            pref.MessageDescriptor
+		IsStreamingClient bool
+		IsStreamingServer bool
+	}
+)
+
+func (sd *Service) Options() pref.ProtoMessage {
+	if f := sd.lazyInit().Options; f != nil {
+		return f()
+	}
+	return descopts.Service
+}
+func (sd *Service) Methods() pref.MethodDescriptors     { return &sd.lazyInit().Methods }
+func (sd *Service) Format(s fmt.State, r rune)          { descfmt.FormatDesc(s, r, sd) }
+func (sd *Service) ProtoType(pref.ServiceDescriptor)    {}
+func (sd *Service) ProtoInternal(pragma.DoNotImplement) {}
+func (sd *Service) lazyInit() *ServiceL2 {
+	sd.L0.ParentFile.lazyInit() // implicitly initializes L2
+	return sd.L2
+}
+
+func (md *Method) Options() pref.ProtoMessage {
+	if f := md.L1.Options; f != nil {
+		return f()
+	}
+	return descopts.Method
+}
+func (md *Method) Input() pref.MessageDescriptor       { return md.L1.Input }
+func (md *Method) Output() pref.MessageDescriptor      { return md.L1.Output }
+func (md *Method) IsStreamingClient() bool             { return md.L1.IsStreamingClient }
+func (md *Method) IsStreamingServer() bool             { return md.L1.IsStreamingServer }
+func (md *Method) Format(s fmt.State, r rune)          { descfmt.FormatDesc(s, r, md) }
+func (md *Method) ProtoType(pref.MethodDescriptor)     {}
+func (md *Method) ProtoInternal(pragma.DoNotImplement) {}
+
+// Surrogate files are can be used to create standalone descriptors
+// where the syntax is only information derived from the parent file.
+var (
+	SurrogateProto2 = &File{L1: FileL1{Syntax: pref.Proto2}, L2: &FileL2{}}
+	SurrogateProto3 = &File{L1: FileL1{Syntax: pref.Proto3}, L2: &FileL2{}}
+)
+
+type (
+	Base struct {
+		L0 BaseL0
+	}
+	BaseL0 struct {
+		FullName   pref.FullName // must be populated
+		ParentFile *File         // must be populated
+		Parent     pref.Descriptor
+		Index      int
+	}
+)
+
+func (d *Base) Name() pref.Name         { return d.L0.FullName.Name() }
+func (d *Base) FullName() pref.FullName { return d.L0.FullName }
+func (d *Base) ParentFile() pref.FileDescriptor {
+	if d.L0.ParentFile == SurrogateProto2 || d.L0.ParentFile == SurrogateProto3 {
+		return nil // surrogate files are not real parents
+	}
+	return d.L0.ParentFile
+}
+func (d *Base) Parent() pref.Descriptor             { return d.L0.Parent }
+func (d *Base) Index() int                          { return d.L0.Index }
+func (d *Base) Syntax() pref.Syntax                 { return d.L0.ParentFile.Syntax() }
+func (d *Base) IsPlaceholder() bool                 { return false }
+func (d *Base) ProtoInternal(pragma.DoNotImplement) {}
+
+func JSONName(s string) jsonName {
+	return jsonName{has: true, name: s}
+}
+
+type jsonName struct {
+	has  bool
+	once sync.Once
+	name string
+}
+
+func (js *jsonName) get(fd pref.FieldDescriptor) string {
+	if !js.has {
+		js.once.Do(func() {
+			js.name = makeJSONName(fd.Name())
+		})
+	}
+	return js.name
+}
+
+// makeJSONName creates a JSON name from the protobuf short name.
+func makeJSONName(s pref.Name) string {
+	var b []byte
+	var wasUnderscore bool
+	for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
+		c := s[i]
+		if c != '_' {
+			isLower := 'a' <= c && c <= 'z'
+			if wasUnderscore && isLower {
+				c -= 'a' - 'A'
+			}
+			b = append(b, c)
+		}
+		wasUnderscore = c == '_'
+	}
+	return string(b)
+}
+
+func DefaultValue(v pref.Value, ev pref.EnumValueDescriptor) defaultValue {
+	dv := defaultValue{has: v.IsValid(), val: v, enum: ev}
+	if b, ok := v.Interface().([]byte); ok {
+		// Store a copy of the default bytes, so that we can detect
+		// accidental mutations of the original value.
+		dv.bytes = append([]byte(nil), b...)
+	}
+	return dv
+}
+
+func unmarshalDefault(b []byte, k pref.Kind, pf *File, ed pref.EnumDescriptor) defaultValue {
+	var evs pref.EnumValueDescriptors
+	if k == pref.EnumKind {
+		// If the enum is declared within the same file, be careful not to
+		// blindly call the Values method, lest we bind ourselves in a deadlock.
+		if ed, ok := ed.(*Enum); ok && ed.L0.ParentFile == pf {
+			evs = &ed.L2.Values
+		} else {
+			evs = ed.Values()
+		}
+	}
+
+	v, ev, err := defval.Unmarshal(string(b), k, evs, defval.Descriptor)
+	if err != nil {
+		panic(err)
+	}
+	return DefaultValue(v, ev)
+}
+
+type defaultValue struct {
+	has   bool
+	val   pref.Value
+	enum  pref.EnumValueDescriptor
+	bytes []byte
+}
+
+func (dv *defaultValue) get(fd pref.FieldDescriptor) pref.Value {
+	// Return the zero value as the default if unpopulated.
+	if !dv.has {
+		switch fd.Kind() {
+		case pref.BoolKind:
+			return pref.ValueOf(false)
+		case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
+			return pref.ValueOf(int32(0))
+		case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
+			return pref.ValueOf(int64(0))
+		case pref.Uint32Kind, pref.Fixed32Kind:
+			return pref.ValueOf(uint32(0))
+		case pref.Uint64Kind, pref.Fixed64Kind:
+			return pref.ValueOf(uint64(0))
+		case pref.FloatKind:
+			return pref.ValueOf(float32(0))
+		case pref.DoubleKind:
+			return pref.ValueOf(float64(0))
+		case pref.StringKind:
+			return pref.ValueOf(string(""))
+		case pref.BytesKind:
+			return pref.ValueOf([]byte(nil))
+		case pref.EnumKind:
+			return pref.ValueOf(fd.Enum().Values().Get(0).Number())
+		}
+	}
+
+	if len(dv.bytes) > 0 && !bytes.Equal(dv.bytes, dv.val.Bytes()) {
+		// TODO: Avoid panic if we're running with the race detector
+		// and instead spawn a goroutine that periodically resets
+		// this value back to the original to induce a race.
+		panic("detected mutation on the default bytes")
+	}
+	return dv.val
+}
diff --git a/internal/filedesc/desc_init.go b/internal/filedesc/desc_init.go
new file mode 100644
index 0000000..03abf8f
--- /dev/null
+++ b/internal/filedesc/desc_init.go
@@ -0,0 +1,390 @@
+// Copyright 2019 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 filedesc
+
+import (
+	"google.golang.org/protobuf/internal/encoding/wire"
+	"google.golang.org/protobuf/internal/fieldnum"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// fileRaw is a data struct used when initializing a file descriptor from
+// a raw FileDescriptorProto.
+type fileRaw struct {
+	builder       DescBuilder
+	allEnums      []Enum
+	allMessages   []Message
+	allExtensions []Extension
+	allServices   []Service
+}
+
+func newRawFile(db DescBuilder) *File {
+	fd := &File{fileRaw: fileRaw{builder: db}}
+	fd.initDecls(db.NumEnums, db.NumMessages, db.NumExtensions, db.NumServices)
+	fd.unmarshalSeed(db.RawDescriptor)
+
+	// Extended message targets are eagerly resolved since registration
+	// needs this information at program init time.
+	for i := range fd.allExtensions {
+		xd := &fd.allExtensions[i]
+		xd.L1.Extendee = fd.resolveMessageDependency(xd.L1.Extendee, listExtTargets, int32(i))
+	}
+
+	fd.checkDecls()
+	return fd
+}
+
+// initDecls pre-allocates slices for the exact number of enums, messages
+// (including map entries), extensions, and services declared in the proto file.
+// This is done to avoid regrowing the slice, which would change the address
+// for any previously seen declaration.
+//
+// The alloc methods "allocates" slices by pulling from the capacity.
+func (fd *File) initDecls(numEnums, numMessages, numExtensions, numServices int32) {
+	fd.allEnums = make([]Enum, 0, numEnums)
+	fd.allMessages = make([]Message, 0, numMessages)
+	fd.allExtensions = make([]Extension, 0, numExtensions)
+	fd.allServices = make([]Service, 0, numServices)
+}
+
+func (fd *File) allocEnums(n int) []Enum {
+	total := len(fd.allEnums)
+	es := fd.allEnums[total : total+n]
+	fd.allEnums = fd.allEnums[:total+n]
+	return es
+}
+func (fd *File) allocMessages(n int) []Message {
+	total := len(fd.allMessages)
+	ms := fd.allMessages[total : total+n]
+	fd.allMessages = fd.allMessages[:total+n]
+	return ms
+}
+func (fd *File) allocExtensions(n int) []Extension {
+	total := len(fd.allExtensions)
+	xs := fd.allExtensions[total : total+n]
+	fd.allExtensions = fd.allExtensions[:total+n]
+	return xs
+}
+func (fd *File) allocServices(n int) []Service {
+	total := len(fd.allServices)
+	xs := fd.allServices[total : total+n]
+	fd.allServices = fd.allServices[:total+n]
+	return xs
+}
+
+// checkDecls performs a sanity check that the expected number of expected
+// declarations matches the number that were found in the descriptor proto.
+func (fd *File) checkDecls() {
+	switch {
+	case len(fd.allEnums) != cap(fd.allEnums):
+	case len(fd.allMessages) != cap(fd.allMessages):
+	case len(fd.allExtensions) != cap(fd.allExtensions):
+	case len(fd.allServices) != cap(fd.allServices):
+	default:
+		return
+	}
+	panic("mismatching cardinality")
+}
+
+func (fd *File) unmarshalSeed(b []byte) {
+	nb := getNameBuilder()
+	defer putNameBuilder(nb)
+
+	var prevField pref.FieldNumber
+	var numEnums, numMessages, numExtensions, numServices int
+	var posEnums, posMessages, posExtensions, posServices int
+	b0 := b
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FileDescriptorProto_Syntax:
+				switch string(v) {
+				case "proto2":
+					fd.L1.Syntax = pref.Proto2
+				case "proto3":
+					fd.L1.Syntax = pref.Proto3
+				default:
+					panic("invalid syntax")
+				}
+			case fieldnum.FileDescriptorProto_Name:
+				fd.L1.Path = nb.MakeString(v)
+			case fieldnum.FileDescriptorProto_Package:
+				fd.L1.Package = pref.FullName(nb.MakeString(v))
+			case fieldnum.FileDescriptorProto_EnumType:
+				if prevField != fieldnum.FileDescriptorProto_EnumType {
+					if numEnums > 0 {
+						panic("non-contiguous repeated field")
+					}
+					posEnums = len(b0) - len(b) - n - m
+				}
+				numEnums++
+			case fieldnum.FileDescriptorProto_MessageType:
+				if prevField != fieldnum.FileDescriptorProto_MessageType {
+					if numMessages > 0 {
+						panic("non-contiguous repeated field")
+					}
+					posMessages = len(b0) - len(b) - n - m
+				}
+				numMessages++
+			case fieldnum.FileDescriptorProto_Extension:
+				if prevField != fieldnum.FileDescriptorProto_Extension {
+					if numExtensions > 0 {
+						panic("non-contiguous repeated field")
+					}
+					posExtensions = len(b0) - len(b) - n - m
+				}
+				numExtensions++
+			case fieldnum.FileDescriptorProto_Service:
+				if prevField != fieldnum.FileDescriptorProto_Service {
+					if numServices > 0 {
+						panic("non-contiguous repeated field")
+					}
+					posServices = len(b0) - len(b) - n - m
+				}
+				numServices++
+			}
+			prevField = num
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+			prevField = -1 // ignore known field numbers of unknown wire type
+		}
+	}
+
+	// If syntax is missing, it is assumed to be proto2.
+	if fd.L1.Syntax == 0 {
+		fd.L1.Syntax = pref.Proto2
+	}
+
+	// Must allocate all declarations before parsing each descriptor type
+	// to ensure we handled all descriptors in "flattened ordering".
+	if numEnums > 0 {
+		fd.L1.Enums.List = fd.allocEnums(numEnums)
+	}
+	if numMessages > 0 {
+		fd.L1.Messages.List = fd.allocMessages(numMessages)
+	}
+	if numExtensions > 0 {
+		fd.L1.Extensions.List = fd.allocExtensions(numExtensions)
+	}
+	if numServices > 0 {
+		fd.L1.Services.List = fd.allocServices(numServices)
+	}
+
+	if numEnums > 0 {
+		b := b0[posEnums:]
+		for i := range fd.L1.Enums.List {
+			_, n := wire.ConsumeVarint(b)
+			v, m := wire.ConsumeBytes(b[n:])
+			fd.L1.Enums.List[i].unmarshalSeed(v, nb, fd, fd, i)
+			b = b[n+m:]
+		}
+	}
+	if numMessages > 0 {
+		b := b0[posMessages:]
+		for i := range fd.L1.Messages.List {
+			_, n := wire.ConsumeVarint(b)
+			v, m := wire.ConsumeBytes(b[n:])
+			fd.L1.Messages.List[i].unmarshalSeed(v, nb, fd, fd, i)
+			b = b[n+m:]
+		}
+	}
+	if numExtensions > 0 {
+		b := b0[posExtensions:]
+		for i := range fd.L1.Extensions.List {
+			_, n := wire.ConsumeVarint(b)
+			v, m := wire.ConsumeBytes(b[n:])
+			fd.L1.Extensions.List[i].unmarshalSeed(v, nb, fd, fd, i)
+			b = b[n+m:]
+		}
+	}
+	if numServices > 0 {
+		b := b0[posServices:]
+		for i := range fd.L1.Services.List {
+			_, n := wire.ConsumeVarint(b)
+			v, m := wire.ConsumeBytes(b[n:])
+			fd.L1.Services.List[i].unmarshalSeed(v, nb, fd, fd, i)
+			b = b[n+m:]
+		}
+	}
+}
+
+func (ed *Enum) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+	ed.L0.ParentFile = pf
+	ed.L0.Parent = pd
+	ed.L0.Index = i
+
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.EnumDescriptorProto_Name:
+				ed.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
+
+func (md *Message) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+	md.L0.ParentFile = pf
+	md.L0.Parent = pd
+	md.L0.Index = i
+
+	var prevField pref.FieldNumber
+	var numEnums, numMessages, numExtensions int
+	var posEnums, posMessages, posExtensions int
+	b0 := b
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.DescriptorProto_Name:
+				md.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+			case fieldnum.DescriptorProto_EnumType:
+				if prevField != fieldnum.DescriptorProto_EnumType {
+					if numEnums > 0 {
+						panic("non-contiguous repeated field")
+					}
+					posEnums = len(b0) - len(b) - n - m
+				}
+				numEnums++
+			case fieldnum.DescriptorProto_NestedType:
+				if prevField != fieldnum.DescriptorProto_NestedType {
+					if numMessages > 0 {
+						panic("non-contiguous repeated field")
+					}
+					posMessages = len(b0) - len(b) - n - m
+				}
+				numMessages++
+			case fieldnum.DescriptorProto_Extension:
+				if prevField != fieldnum.DescriptorProto_Extension {
+					if numExtensions > 0 {
+						panic("non-contiguous repeated field")
+					}
+					posExtensions = len(b0) - len(b) - n - m
+				}
+				numExtensions++
+			}
+			prevField = num
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+			prevField = -1 // ignore known field numbers of unknown wire type
+		}
+	}
+
+	// Must allocate all declarations before parsing each descriptor type
+	// to ensure we handled all descriptors in "flattened ordering".
+	if numEnums > 0 {
+		md.L1.Enums.List = pf.allocEnums(numEnums)
+	}
+	if numMessages > 0 {
+		md.L1.Messages.List = pf.allocMessages(numMessages)
+	}
+	if numExtensions > 0 {
+		md.L1.Extensions.List = pf.allocExtensions(numExtensions)
+	}
+
+	if numEnums > 0 {
+		b := b0[posEnums:]
+		for i := range md.L1.Enums.List {
+			_, n := wire.ConsumeVarint(b)
+			v, m := wire.ConsumeBytes(b[n:])
+			md.L1.Enums.List[i].unmarshalSeed(v, nb, pf, md, i)
+			b = b[n+m:]
+		}
+	}
+	if numMessages > 0 {
+		b := b0[posMessages:]
+		for i := range md.L1.Messages.List {
+			_, n := wire.ConsumeVarint(b)
+			v, m := wire.ConsumeBytes(b[n:])
+			md.L1.Messages.List[i].unmarshalSeed(v, nb, pf, md, i)
+			b = b[n+m:]
+		}
+	}
+	if numExtensions > 0 {
+		b := b0[posExtensions:]
+		for i := range md.L1.Extensions.List {
+			_, n := wire.ConsumeVarint(b)
+			v, m := wire.ConsumeBytes(b[n:])
+			md.L1.Extensions.List[i].unmarshalSeed(v, nb, pf, md, i)
+			b = b[n+m:]
+		}
+	}
+}
+
+func (xd *Extension) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+	xd.L0.ParentFile = pf
+	xd.L0.Parent = pd
+	xd.L0.Index = i
+
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FieldDescriptorProto_Number:
+				xd.L1.Number = pref.FieldNumber(v)
+			case fieldnum.FieldDescriptorProto_Type:
+				xd.L1.Kind = pref.Kind(v)
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FieldDescriptorProto_Name:
+				xd.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+			case fieldnum.FieldDescriptorProto_Extendee:
+				xd.L1.Extendee = PlaceholderMessage(nb.MakeFullName(v))
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
+
+func (sd *Service) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+	sd.L0.ParentFile = pf
+	sd.L0.Parent = pd
+	sd.L0.Index = i
+
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.ServiceDescriptorProto_Name:
+				sd.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
diff --git a/internal/filedesc/desc_lazy.go b/internal/filedesc/desc_lazy.go
new file mode 100644
index 0000000..8b8ab5a
--- /dev/null
+++ b/internal/filedesc/desc_lazy.go
@@ -0,0 +1,686 @@
+// Copyright 2019 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 filedesc
+
+import (
+	"reflect"
+
+	"google.golang.org/protobuf/internal/descopts"
+	"google.golang.org/protobuf/internal/encoding/wire"
+	"google.golang.org/protobuf/internal/fieldnum"
+	"google.golang.org/protobuf/proto"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+func (fd *File) lazyRawInit() {
+	fd.unmarshalFull(fd.builder.RawDescriptor)
+	fd.resolveMessages()
+	fd.resolveExtensions()
+	fd.resolveServices()
+}
+
+func (file *File) resolveMessages() {
+	var depIdx int32
+	for i := range file.allMessages {
+		md := &file.allMessages[i]
+
+		// Resolve message field dependencies.
+		for j := range md.L2.Fields.List {
+			fd := &md.L2.Fields.List[j]
+
+			// Weak fields are only resolved by name.
+			if fd.L1.IsWeak {
+				r := file.builder.FileRegistry
+				if md, _ := r.FindMessageByName(fd.L1.Message.FullName()); md != nil {
+					fd.L1.Message = md
+				}
+				continue
+			}
+
+			// Resolve message field dependency.
+			switch fd.L1.Kind {
+			case pref.EnumKind:
+				fd.L1.Enum = file.resolveEnumDependency(fd.L1.Enum, listFieldDeps, depIdx)
+				depIdx++
+			case pref.MessageKind, pref.GroupKind:
+				fd.L1.Message = file.resolveMessageDependency(fd.L1.Message, listFieldDeps, depIdx)
+				depIdx++
+			}
+
+			// Default is resolved here since it depends on Enum being resolved.
+			if v := fd.L1.Default.val; v.IsValid() {
+				fd.L1.Default = unmarshalDefault(v.Bytes(), fd.L1.Kind, file, fd.L1.Enum)
+			}
+		}
+	}
+}
+
+func (file *File) resolveExtensions() {
+	var depIdx int32
+	for i := range file.allExtensions {
+		xd := &file.allExtensions[i]
+
+		// Resolve extension field dependency.
+		switch xd.L1.Kind {
+		case pref.EnumKind:
+			xd.L2.Enum = file.resolveEnumDependency(xd.L2.Enum, listExtDeps, depIdx)
+			depIdx++
+		case pref.MessageKind, pref.GroupKind:
+			xd.L2.Message = file.resolveMessageDependency(xd.L2.Message, listExtDeps, depIdx)
+			depIdx++
+		}
+
+		// Default is resolved here since it depends on Enum being resolved.
+		if v := xd.L2.Default.val; v.IsValid() {
+			xd.L2.Default = unmarshalDefault(v.Bytes(), xd.L1.Kind, file, xd.L2.Enum)
+		}
+	}
+}
+
+func (file *File) resolveServices() {
+	var depIdx int32
+	for i := range file.allServices {
+		sd := &file.allServices[i]
+
+		// Resolve method dependencies.
+		for j := range sd.L2.Methods.List {
+			md := &sd.L2.Methods.List[j]
+			md.L1.Input = file.resolveMessageDependency(md.L1.Input, listMethInDeps, depIdx)
+			md.L1.Output = file.resolveMessageDependency(md.L1.Output, listMethOutDeps, depIdx)
+			depIdx++
+		}
+	}
+}
+
+func (file *File) resolveEnumDependency(ed pref.EnumDescriptor, i, j int32) pref.EnumDescriptor {
+	r := file.builder.FileRegistry
+	if r, ok := r.(resolverByIndex); ok {
+		if ed2 := r.FindEnumByIndex(i, j, file.allEnums, file.allMessages); ed2 != nil {
+			return ed2
+		}
+	}
+	for i := range file.allEnums {
+		if ed2 := &file.allEnums[i]; ed2.L0.FullName == ed.FullName() {
+			return ed2
+		}
+	}
+	if ed2, _ := r.FindEnumByName(ed.FullName()); ed2 != nil {
+		return ed2
+	}
+	return ed
+}
+
+func (file *File) resolveMessageDependency(md pref.MessageDescriptor, i, j int32) pref.MessageDescriptor {
+	r := file.builder.FileRegistry
+	if r, ok := r.(resolverByIndex); ok {
+		if md2 := r.FindMessageByIndex(i, j, file.allEnums, file.allMessages); md2 != nil {
+			return md2
+		}
+	}
+	for i := range file.allMessages {
+		if md2 := &file.allMessages[i]; md2.L0.FullName == md.FullName() {
+			return md2
+		}
+	}
+	if md2, _ := r.FindMessageByName(md.FullName()); md2 != nil {
+		return md2
+	}
+	return md
+}
+
+func (fd *File) unmarshalFull(b []byte) {
+	nb := getNameBuilder()
+	defer putNameBuilder(nb)
+
+	var enumIdx, messageIdx, extensionIdx, serviceIdx int
+	var rawOptions []byte
+	fd.L2 = new(FileL2)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FileDescriptorProto_PublicDependency:
+				fd.L2.Imports[v].IsPublic = true
+			case fieldnum.FileDescriptorProto_WeakDependency:
+				fd.L2.Imports[v].IsWeak = true
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FileDescriptorProto_Dependency:
+				path := nb.MakeString(v)
+				imp, _ := fd.builder.FileRegistry.FindFileByPath(path)
+				if imp == nil {
+					imp = PlaceholderFile(path)
+				}
+				fd.L2.Imports = append(fd.L2.Imports, pref.FileImport{FileDescriptor: imp})
+			case fieldnum.FileDescriptorProto_EnumType:
+				fd.L1.Enums.List[enumIdx].unmarshalFull(v, nb)
+				enumIdx++
+			case fieldnum.FileDescriptorProto_MessageType:
+				fd.L1.Messages.List[messageIdx].unmarshalFull(v, nb)
+				messageIdx++
+			case fieldnum.FileDescriptorProto_Extension:
+				fd.L1.Extensions.List[extensionIdx].unmarshalFull(v, nb)
+				extensionIdx++
+			case fieldnum.FileDescriptorProto_Service:
+				fd.L1.Services.List[serviceIdx].unmarshalFull(v, nb)
+				serviceIdx++
+			case fieldnum.FileDescriptorProto_Options:
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	fd.L2.Options = fd.builder.optionsUnmarshaler(descopts.File, rawOptions)
+}
+
+func (ed *Enum) unmarshalFull(b []byte, nb *nameBuilder) {
+	var rawValues [][]byte
+	var rawOptions []byte
+	ed.L2 = new(EnumL2)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.EnumDescriptorProto_Value:
+				rawValues = append(rawValues, v)
+			case fieldnum.EnumDescriptorProto_ReservedName:
+				ed.L2.ReservedNames.List = append(ed.L2.ReservedNames.List, pref.Name(nb.MakeString(v)))
+			case fieldnum.EnumDescriptorProto_ReservedRange:
+				ed.L2.ReservedRanges.List = append(ed.L2.ReservedRanges.List, unmarshalEnumReservedRange(v))
+			case fieldnum.EnumDescriptorProto_Options:
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	if len(rawValues) > 0 {
+		ed.L2.Values.List = make([]EnumValue, len(rawValues))
+		for i, b := range rawValues {
+			ed.L2.Values.List[i].unmarshalFull(b, nb, ed.L0.ParentFile, ed, i)
+		}
+	}
+	ed.L2.Options = ed.L0.ParentFile.builder.optionsUnmarshaler(descopts.Enum, rawOptions)
+}
+
+func unmarshalEnumReservedRange(b []byte) (r [2]pref.EnumNumber) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.EnumDescriptorProto_EnumReservedRange_Start:
+				r[0] = pref.EnumNumber(v)
+			case fieldnum.EnumDescriptorProto_EnumReservedRange_End:
+				r[1] = pref.EnumNumber(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	return r
+}
+
+func (vd *EnumValue) unmarshalFull(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+	vd.L0.ParentFile = pf
+	vd.L0.Parent = pd
+	vd.L0.Index = i
+
+	var rawOptions []byte
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.EnumValueDescriptorProto_Number:
+				vd.L1.Number = pref.EnumNumber(v)
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.EnumValueDescriptorProto_Name:
+				// NOTE: Enum values are in the same scope as the enum parent.
+				vd.L0.FullName = nb.AppendFullName(pd.Parent().FullName(), v)
+			case fieldnum.EnumValueDescriptorProto_Options:
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	vd.L1.Options = pf.builder.optionsUnmarshaler(descopts.EnumValue, rawOptions)
+}
+
+func (md *Message) unmarshalFull(b []byte, nb *nameBuilder) {
+	var rawFields, rawOneofs [][]byte
+	var enumIdx, messageIdx, extensionIdx int
+	var rawOptions []byte
+	md.L2 = new(MessageL2)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.DescriptorProto_Field:
+				rawFields = append(rawFields, v)
+			case fieldnum.DescriptorProto_OneofDecl:
+				rawOneofs = append(rawOneofs, v)
+			case fieldnum.DescriptorProto_ReservedName:
+				md.L2.ReservedNames.List = append(md.L2.ReservedNames.List, pref.Name(nb.MakeString(v)))
+			case fieldnum.DescriptorProto_ReservedRange:
+				md.L2.ReservedRanges.List = append(md.L2.ReservedRanges.List, unmarshalMessageReservedRange(v))
+			case fieldnum.DescriptorProto_ExtensionRange:
+				r, rawOptions := unmarshalMessageExtensionRange(v)
+				opts := md.L0.ParentFile.builder.optionsUnmarshaler(descopts.ExtensionRange, rawOptions)
+				md.L2.ExtensionRanges.List = append(md.L2.ExtensionRanges.List, r)
+				md.L2.ExtensionRangeOptions = append(md.L2.ExtensionRangeOptions, opts)
+			case fieldnum.DescriptorProto_EnumType:
+				md.L1.Enums.List[enumIdx].unmarshalFull(v, nb)
+				enumIdx++
+			case fieldnum.DescriptorProto_NestedType:
+				md.L1.Messages.List[messageIdx].unmarshalFull(v, nb)
+				messageIdx++
+			case fieldnum.DescriptorProto_Extension:
+				md.L1.Extensions.List[extensionIdx].unmarshalFull(v, nb)
+				extensionIdx++
+			case fieldnum.DescriptorProto_Options:
+				md.unmarshalOptions(v)
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	if len(rawFields) > 0 || len(rawOneofs) > 0 {
+		md.L2.Fields.List = make([]Field, len(rawFields))
+		md.L2.Oneofs.List = make([]Oneof, len(rawOneofs))
+		for i, b := range rawFields {
+			fd := &md.L2.Fields.List[i]
+			fd.unmarshalFull(b, nb, md.L0.ParentFile, md, i)
+			if fd.L1.Cardinality == pref.Required {
+				md.L2.RequiredNumbers.List = append(md.L2.RequiredNumbers.List, fd.L1.Number)
+			}
+		}
+		for i, b := range rawOneofs {
+			od := &md.L2.Oneofs.List[i]
+			od.unmarshalFull(b, nb, md.L0.ParentFile, md, i)
+		}
+	}
+	md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(descopts.Message, rawOptions)
+}
+
+func (md *Message) unmarshalOptions(b []byte) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.MessageOptions_MapEntry:
+				md.L2.IsMapEntry = wire.DecodeBool(v)
+			case fieldnum.MessageOptions_MessageSetWireFormat:
+				md.L2.IsMessageSet = wire.DecodeBool(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
+
+func unmarshalMessageReservedRange(b []byte) (r [2]pref.FieldNumber) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.DescriptorProto_ReservedRange_Start:
+				r[0] = pref.FieldNumber(v)
+			case fieldnum.DescriptorProto_ReservedRange_End:
+				r[1] = pref.FieldNumber(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	return r
+}
+
+func unmarshalMessageExtensionRange(b []byte) (r [2]pref.FieldNumber, rawOptions []byte) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.DescriptorProto_ExtensionRange_Start:
+				r[0] = pref.FieldNumber(v)
+			case fieldnum.DescriptorProto_ExtensionRange_End:
+				r[1] = pref.FieldNumber(v)
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.DescriptorProto_ExtensionRange_Options:
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	return r, rawOptions
+}
+
+func (fd *Field) unmarshalFull(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+	fd.L0.ParentFile = pf
+	fd.L0.Parent = pd
+	fd.L0.Index = i
+
+	var rawTypeName []byte
+	var rawOptions []byte
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FieldDescriptorProto_Number:
+				fd.L1.Number = pref.FieldNumber(v)
+			case fieldnum.FieldDescriptorProto_Label:
+				fd.L1.Cardinality = pref.Cardinality(v)
+			case fieldnum.FieldDescriptorProto_Type:
+				fd.L1.Kind = pref.Kind(v)
+			case fieldnum.FieldDescriptorProto_OneofIndex:
+				// In Message.unmarshalFull, we allocate slices for both
+				// the field and oneof descriptors before unmarshaling either
+				// of them. This ensures pointers to slice elements are stable.
+				od := &pd.(*Message).L2.Oneofs.List[v]
+				od.L1.Fields.List = append(od.L1.Fields.List, fd)
+				if fd.L1.ContainingOneof != nil {
+					panic("oneof type already set")
+				}
+				fd.L1.ContainingOneof = od
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FieldDescriptorProto_Name:
+				fd.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+			case fieldnum.FieldDescriptorProto_JsonName:
+				fd.L1.JSONName = JSONName(nb.MakeString(v))
+			case fieldnum.FieldDescriptorProto_DefaultValue:
+				fd.L1.Default.val = pref.ValueOf(v) // temporarily store as bytes; later resolved in resolveMessages
+			case fieldnum.FieldDescriptorProto_TypeName:
+				rawTypeName = v
+			case fieldnum.FieldDescriptorProto_Options:
+				fd.unmarshalOptions(v)
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	if rawTypeName != nil {
+		name := nb.MakeFullName(rawTypeName)
+		switch fd.L1.Kind {
+		case pref.EnumKind:
+			fd.L1.Enum = PlaceholderEnum(name)
+		case pref.MessageKind, pref.GroupKind:
+			fd.L1.Message = PlaceholderMessage(name)
+		}
+	}
+	fd.L1.Options = pf.builder.optionsUnmarshaler(descopts.Field, rawOptions)
+}
+
+func (fd *Field) unmarshalOptions(b []byte) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FieldOptions_Packed:
+				fd.L1.HasPacked = true
+				fd.L1.IsPacked = wire.DecodeBool(v)
+			case fieldnum.FieldOptions_Weak:
+				fd.L1.IsWeak = wire.DecodeBool(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
+
+func (od *Oneof) unmarshalFull(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+	od.L0.ParentFile = pf
+	od.L0.Parent = pd
+	od.L0.Index = i
+
+	var rawOptions []byte
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.OneofDescriptorProto_Name:
+				od.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+			case fieldnum.OneofDescriptorProto_Options:
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	od.L1.Options = pf.builder.optionsUnmarshaler(descopts.Oneof, rawOptions)
+}
+
+func (xd *Extension) unmarshalFull(b []byte, nb *nameBuilder) {
+	var rawTypeName []byte
+	var rawOptions []byte
+	xd.L2 = new(ExtensionL2)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FieldDescriptorProto_Label:
+				xd.L2.Cardinality = pref.Cardinality(v)
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FieldDescriptorProto_JsonName:
+				xd.L2.JSONName = JSONName(nb.MakeString(v))
+			case fieldnum.FieldDescriptorProto_DefaultValue:
+				xd.L2.Default.val = pref.ValueOf(v) // temporarily store as bytes; later resolved in resolveExtensions
+			case fieldnum.FieldDescriptorProto_TypeName:
+				rawTypeName = v
+			case fieldnum.FieldDescriptorProto_Options:
+				xd.unmarshalOptions(v)
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	if rawTypeName != nil {
+		name := nb.MakeFullName(rawTypeName)
+		switch xd.L1.Kind {
+		case pref.EnumKind:
+			xd.L2.Enum = PlaceholderEnum(name)
+		case pref.MessageKind, pref.GroupKind:
+			xd.L2.Message = PlaceholderMessage(name)
+		}
+	}
+	xd.L2.Options = xd.L0.ParentFile.builder.optionsUnmarshaler(descopts.Field, rawOptions)
+}
+
+func (xd *Extension) unmarshalOptions(b []byte) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.FieldOptions_Packed:
+				xd.L2.IsPacked = wire.DecodeBool(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
+
+func (sd *Service) unmarshalFull(b []byte, nb *nameBuilder) {
+	var rawMethods [][]byte
+	var rawOptions []byte
+	sd.L2 = new(ServiceL2)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.ServiceDescriptorProto_Method:
+				rawMethods = append(rawMethods, v)
+			case fieldnum.ServiceDescriptorProto_Options:
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	if len(rawMethods) > 0 {
+		sd.L2.Methods.List = make([]Method, len(rawMethods))
+		for i, b := range rawMethods {
+			sd.L2.Methods.List[i].unmarshalFull(b, nb, sd.L0.ParentFile, sd, i)
+		}
+	}
+	sd.L2.Options = sd.L0.ParentFile.builder.optionsUnmarshaler(descopts.Service, rawOptions)
+}
+
+func (md *Method) unmarshalFull(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+	md.L0.ParentFile = pf
+	md.L0.Parent = pd
+	md.L0.Index = i
+
+	var rawOptions []byte
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.MethodDescriptorProto_ClientStreaming:
+				md.L1.IsStreamingClient = wire.DecodeBool(v)
+			case fieldnum.MethodDescriptorProto_ServerStreaming:
+				md.L1.IsStreamingServer = wire.DecodeBool(v)
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldnum.MethodDescriptorProto_Name:
+				md.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+			case fieldnum.MethodDescriptorProto_InputType:
+				md.L1.Input = PlaceholderMessage(nb.MakeFullName(v))
+			case fieldnum.MethodDescriptorProto_OutputType:
+				md.L1.Output = PlaceholderMessage(nb.MakeFullName(v))
+			case fieldnum.MethodDescriptorProto_Options:
+				rawOptions = appendOptions(rawOptions, v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	md.L1.Options = pf.builder.optionsUnmarshaler(descopts.Method, rawOptions)
+}
+
+// appendOptions appends src to dst, where the returned slice is never nil.
+// This is necessary to distinguish between empty and unpopulated options.
+func appendOptions(dst, src []byte) []byte {
+	if dst == nil {
+		dst = []byte{}
+	}
+	return append(dst, src...)
+}
+
+func (db *DescBuilder) optionsUnmarshaler(p pref.ProtoMessage, b []byte) func() pref.ProtoMessage {
+	if b == nil {
+		return nil
+	}
+	return func() pref.ProtoMessage {
+		p := reflect.New(reflect.TypeOf(p).Elem()).Interface().(pref.ProtoMessage)
+		if err := (proto.UnmarshalOptions{
+			AllowPartial: true,
+			Resolver:     db.TypeResolver,
+		}).Unmarshal(b, p); err != nil {
+			panic(err)
+		}
+		return p
+	}
+}
diff --git a/internal/filedesc/desc_list.go b/internal/filedesc/desc_list.go
new file mode 100644
index 0000000..bbded60
--- /dev/null
+++ b/internal/filedesc/desc_list.go
@@ -0,0 +1,189 @@
+// Copyright 2019 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 filedesc
+
+import (
+	"fmt"
+	"sort"
+	"sync"
+
+	"google.golang.org/protobuf/internal/descfmt"
+	"google.golang.org/protobuf/internal/pragma"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+type FileImports []pref.FileImport
+
+func (p *FileImports) Len() int                            { return len(*p) }
+func (p *FileImports) Get(i int) pref.FileImport           { return (*p)[i] }
+func (p *FileImports) Format(s fmt.State, r rune)          { descfmt.FormatList(s, r, p) }
+func (p *FileImports) ProtoInternal(pragma.DoNotImplement) {}
+
+type Names struct {
+	List []pref.Name
+	once sync.Once
+	has  map[pref.Name]struct{} // protected by once
+}
+
+func (p *Names) Len() int            { return len(p.List) }
+func (p *Names) Get(i int) pref.Name { return p.List[i] }
+func (p *Names) Has(s pref.Name) bool {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.has = make(map[pref.Name]struct{}, len(p.List))
+			for _, s := range p.List {
+				p.has[s] = struct{}{}
+			}
+		}
+	})
+	_, ok := p.has[s]
+	return ok
+}
+func (p *Names) Format(s fmt.State, r rune)          { descfmt.FormatList(s, r, p) }
+func (p *Names) ProtoInternal(pragma.DoNotImplement) {}
+
+type EnumRanges struct {
+	List   [][2]pref.EnumNumber // start inclusive; end inclusive
+	once   sync.Once
+	sorted [][2]pref.EnumNumber         // protected by once
+	has    map[pref.EnumNumber]struct{} // protected by once
+}
+
+func (p *EnumRanges) Len() int                     { return len(p.List) }
+func (p *EnumRanges) Get(i int) [2]pref.EnumNumber { return p.List[i] }
+func (p *EnumRanges) Has(n pref.EnumNumber) bool {
+	p.once.Do(func() {
+		for _, r := range p.List {
+			if r[0] == r[1]-0 {
+				if p.has == nil {
+					p.has = make(map[pref.EnumNumber]struct{}, len(p.List))
+				}
+				p.has[r[0]] = struct{}{}
+			} else {
+				p.sorted = append(p.sorted, r)
+			}
+		}
+		sort.Slice(p.sorted, func(i, j int) bool {
+			return p.sorted[i][0] < p.sorted[j][0]
+		})
+	})
+	if _, ok := p.has[n]; ok {
+		return true
+	}
+	for ls := p.sorted; len(ls) > 0; {
+		i := len(ls) / 2
+		switch r := ls[i]; {
+		case n < r[0]:
+			ls = ls[:i] // search lower
+		case n > r[1]:
+			ls = ls[i+1:] // search upper
+		default:
+			return true
+		}
+	}
+	return false
+}
+func (p *EnumRanges) Format(s fmt.State, r rune)          { descfmt.FormatList(s, r, p) }
+func (p *EnumRanges) ProtoInternal(pragma.DoNotImplement) {}
+
+type FieldRanges struct {
+	List   [][2]pref.FieldNumber // start inclusive; end exclusive
+	once   sync.Once
+	sorted [][2]pref.FieldNumber         // protected by once
+	has    map[pref.FieldNumber]struct{} // protected by once
+}
+
+func (p *FieldRanges) Len() int                      { return len(p.List) }
+func (p *FieldRanges) Get(i int) [2]pref.FieldNumber { return p.List[i] }
+func (p *FieldRanges) Has(n pref.FieldNumber) bool {
+	p.once.Do(func() {
+		for _, r := range p.List {
+			if r[0] == r[1]-1 {
+				if p.has == nil {
+					p.has = make(map[pref.FieldNumber]struct{}, len(p.List))
+				}
+				p.has[r[0]] = struct{}{}
+			} else {
+				p.sorted = append(p.sorted, r)
+			}
+		}
+		sort.Slice(p.sorted, func(i, j int) bool {
+			return p.sorted[i][0] < p.sorted[j][0]
+		})
+	})
+	if _, ok := p.has[n]; ok {
+		return true
+	}
+	for ls := p.sorted; len(ls) > 0; {
+		i := len(ls) / 2
+		switch r := ls[i]; {
+		case n < r[0]:
+			ls = ls[:i] // search lower
+		case n >= r[1]:
+			ls = ls[i+1:] // search higher
+		default:
+			return true
+		}
+	}
+	return false
+}
+func (p *FieldRanges) Format(s fmt.State, r rune)          { descfmt.FormatList(s, r, p) }
+func (p *FieldRanges) ProtoInternal(pragma.DoNotImplement) {}
+
+type FieldNumbers struct {
+	List []pref.FieldNumber
+	once sync.Once
+	has  map[pref.FieldNumber]struct{} // protected by once
+}
+
+func (p *FieldNumbers) Len() int                   { return len(p.List) }
+func (p *FieldNumbers) Get(i int) pref.FieldNumber { return p.List[i] }
+func (p *FieldNumbers) Has(n pref.FieldNumber) bool {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.has = make(map[pref.FieldNumber]struct{}, len(p.List))
+			for _, n := range p.List {
+				p.has[n] = struct{}{}
+			}
+		}
+	})
+	_, ok := p.has[n]
+	return ok
+}
+func (p *FieldNumbers) Format(s fmt.State, r rune)          { descfmt.FormatList(s, r, p) }
+func (p *FieldNumbers) ProtoInternal(pragma.DoNotImplement) {}
+
+type OneofFields struct {
+	List   []pref.FieldDescriptor
+	once   sync.Once
+	byName map[pref.Name]pref.FieldDescriptor        // protected by once
+	byJSON map[string]pref.FieldDescriptor           // protected by once
+	byNum  map[pref.FieldNumber]pref.FieldDescriptor // protected by once
+}
+
+func (p *OneofFields) Len() int                                         { return len(p.List) }
+func (p *OneofFields) Get(i int) pref.FieldDescriptor                   { return p.List[i] }
+func (p *OneofFields) ByName(s pref.Name) pref.FieldDescriptor          { return p.lazyInit().byName[s] }
+func (p *OneofFields) ByJSONName(s string) pref.FieldDescriptor         { return p.lazyInit().byJSON[s] }
+func (p *OneofFields) ByNumber(n pref.FieldNumber) pref.FieldDescriptor { return p.lazyInit().byNum[n] }
+func (p *OneofFields) Format(s fmt.State, r rune)                       { descfmt.FormatList(s, r, p) }
+func (p *OneofFields) ProtoInternal(pragma.DoNotImplement)              {}
+
+func (p *OneofFields) lazyInit() *OneofFields {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.byName = make(map[pref.Name]pref.FieldDescriptor, len(p.List))
+			p.byJSON = make(map[string]pref.FieldDescriptor, len(p.List))
+			p.byNum = make(map[pref.FieldNumber]pref.FieldDescriptor, len(p.List))
+			for _, f := range p.List {
+				// Field names and numbers are guaranteed to be unique.
+				p.byName[f.Name()] = f
+				p.byJSON[f.JSONName()] = f
+				p.byNum[f.Number()] = f
+			}
+		}
+	})
+	return p
+}
diff --git a/internal/filedesc/desc_list_gen.go b/internal/filedesc/desc_list_gen.go
new file mode 100644
index 0000000..d875920
--- /dev/null
+++ b/internal/filedesc/desc_list_gen.go
@@ -0,0 +1,345 @@
+// 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.
+
+// Code generated by generate-types. DO NOT EDIT.
+
+package filedesc
+
+import (
+	"fmt"
+	"sync"
+
+	"google.golang.org/protobuf/internal/descfmt"
+	"google.golang.org/protobuf/internal/pragma"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+type Enums struct {
+	List   []Enum
+	once   sync.Once
+	byName map[protoreflect.Name]*Enum // protected by once
+}
+
+func (p *Enums) Len() int {
+	return len(p.List)
+}
+func (p *Enums) Get(i int) protoreflect.EnumDescriptor {
+	return &p.List[i]
+}
+func (p *Enums) ByName(s protoreflect.Name) protoreflect.EnumDescriptor {
+	if d := p.lazyInit().byName[s]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *Enums) Format(s fmt.State, r rune) {
+	descfmt.FormatList(s, r, p)
+}
+func (p *Enums) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Enums) lazyInit() *Enums {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.byName = make(map[protoreflect.Name]*Enum, len(p.List))
+			for i := range p.List {
+				d := &p.List[i]
+				if _, ok := p.byName[d.Name()]; !ok {
+					p.byName[d.Name()] = d
+				}
+			}
+		}
+	})
+	return p
+}
+
+type EnumValues struct {
+	List   []EnumValue
+	once   sync.Once
+	byName map[protoreflect.Name]*EnumValue       // protected by once
+	byNum  map[protoreflect.EnumNumber]*EnumValue // protected by once
+}
+
+func (p *EnumValues) Len() int {
+	return len(p.List)
+}
+func (p *EnumValues) Get(i int) protoreflect.EnumValueDescriptor {
+	return &p.List[i]
+}
+func (p *EnumValues) ByName(s protoreflect.Name) protoreflect.EnumValueDescriptor {
+	if d := p.lazyInit().byName[s]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *EnumValues) ByNumber(n protoreflect.EnumNumber) protoreflect.EnumValueDescriptor {
+	if d := p.lazyInit().byNum[n]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *EnumValues) Format(s fmt.State, r rune) {
+	descfmt.FormatList(s, r, p)
+}
+func (p *EnumValues) ProtoInternal(pragma.DoNotImplement) {}
+func (p *EnumValues) lazyInit() *EnumValues {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.byName = make(map[protoreflect.Name]*EnumValue, len(p.List))
+			p.byNum = make(map[protoreflect.EnumNumber]*EnumValue, len(p.List))
+			for i := range p.List {
+				d := &p.List[i]
+				if _, ok := p.byName[d.Name()]; !ok {
+					p.byName[d.Name()] = d
+				}
+				if _, ok := p.byNum[d.Number()]; !ok {
+					p.byNum[d.Number()] = d
+				}
+			}
+		}
+	})
+	return p
+}
+
+type Messages struct {
+	List   []Message
+	once   sync.Once
+	byName map[protoreflect.Name]*Message // protected by once
+}
+
+func (p *Messages) Len() int {
+	return len(p.List)
+}
+func (p *Messages) Get(i int) protoreflect.MessageDescriptor {
+	return &p.List[i]
+}
+func (p *Messages) ByName(s protoreflect.Name) protoreflect.MessageDescriptor {
+	if d := p.lazyInit().byName[s]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *Messages) Format(s fmt.State, r rune) {
+	descfmt.FormatList(s, r, p)
+}
+func (p *Messages) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Messages) lazyInit() *Messages {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.byName = make(map[protoreflect.Name]*Message, len(p.List))
+			for i := range p.List {
+				d := &p.List[i]
+				if _, ok := p.byName[d.Name()]; !ok {
+					p.byName[d.Name()] = d
+				}
+			}
+		}
+	})
+	return p
+}
+
+type Fields struct {
+	List   []Field
+	once   sync.Once
+	byName map[protoreflect.Name]*Field        // protected by once
+	byJSON map[string]*Field                   // protected by once
+	byNum  map[protoreflect.FieldNumber]*Field // protected by once
+}
+
+func (p *Fields) Len() int {
+	return len(p.List)
+}
+func (p *Fields) Get(i int) protoreflect.FieldDescriptor {
+	return &p.List[i]
+}
+func (p *Fields) ByName(s protoreflect.Name) protoreflect.FieldDescriptor {
+	if d := p.lazyInit().byName[s]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *Fields) ByJSONName(s string) protoreflect.FieldDescriptor {
+	if d := p.lazyInit().byJSON[s]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *Fields) ByNumber(n protoreflect.FieldNumber) protoreflect.FieldDescriptor {
+	if d := p.lazyInit().byNum[n]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *Fields) Format(s fmt.State, r rune) {
+	descfmt.FormatList(s, r, p)
+}
+func (p *Fields) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Fields) lazyInit() *Fields {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.byName = make(map[protoreflect.Name]*Field, len(p.List))
+			p.byJSON = make(map[string]*Field, len(p.List))
+			p.byNum = make(map[protoreflect.FieldNumber]*Field, len(p.List))
+			for i := range p.List {
+				d := &p.List[i]
+				if _, ok := p.byName[d.Name()]; !ok {
+					p.byName[d.Name()] = d
+				}
+				if _, ok := p.byJSON[d.JSONName()]; !ok {
+					p.byJSON[d.JSONName()] = d
+				}
+				if _, ok := p.byNum[d.Number()]; !ok {
+					p.byNum[d.Number()] = d
+				}
+			}
+		}
+	})
+	return p
+}
+
+type Oneofs struct {
+	List   []Oneof
+	once   sync.Once
+	byName map[protoreflect.Name]*Oneof // protected by once
+}
+
+func (p *Oneofs) Len() int {
+	return len(p.List)
+}
+func (p *Oneofs) Get(i int) protoreflect.OneofDescriptor {
+	return &p.List[i]
+}
+func (p *Oneofs) ByName(s protoreflect.Name) protoreflect.OneofDescriptor {
+	if d := p.lazyInit().byName[s]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *Oneofs) Format(s fmt.State, r rune) {
+	descfmt.FormatList(s, r, p)
+}
+func (p *Oneofs) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Oneofs) lazyInit() *Oneofs {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.byName = make(map[protoreflect.Name]*Oneof, len(p.List))
+			for i := range p.List {
+				d := &p.List[i]
+				if _, ok := p.byName[d.Name()]; !ok {
+					p.byName[d.Name()] = d
+				}
+			}
+		}
+	})
+	return p
+}
+
+type Extensions struct {
+	List   []Extension
+	once   sync.Once
+	byName map[protoreflect.Name]*Extension // protected by once
+}
+
+func (p *Extensions) Len() int {
+	return len(p.List)
+}
+func (p *Extensions) Get(i int) protoreflect.ExtensionDescriptor {
+	return &p.List[i]
+}
+func (p *Extensions) ByName(s protoreflect.Name) protoreflect.ExtensionDescriptor {
+	if d := p.lazyInit().byName[s]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *Extensions) Format(s fmt.State, r rune) {
+	descfmt.FormatList(s, r, p)
+}
+func (p *Extensions) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Extensions) lazyInit() *Extensions {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.byName = make(map[protoreflect.Name]*Extension, len(p.List))
+			for i := range p.List {
+				d := &p.List[i]
+				if _, ok := p.byName[d.Name()]; !ok {
+					p.byName[d.Name()] = d
+				}
+			}
+		}
+	})
+	return p
+}
+
+type Services struct {
+	List   []Service
+	once   sync.Once
+	byName map[protoreflect.Name]*Service // protected by once
+}
+
+func (p *Services) Len() int {
+	return len(p.List)
+}
+func (p *Services) Get(i int) protoreflect.ServiceDescriptor {
+	return &p.List[i]
+}
+func (p *Services) ByName(s protoreflect.Name) protoreflect.ServiceDescriptor {
+	if d := p.lazyInit().byName[s]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *Services) Format(s fmt.State, r rune) {
+	descfmt.FormatList(s, r, p)
+}
+func (p *Services) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Services) lazyInit() *Services {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.byName = make(map[protoreflect.Name]*Service, len(p.List))
+			for i := range p.List {
+				d := &p.List[i]
+				if _, ok := p.byName[d.Name()]; !ok {
+					p.byName[d.Name()] = d
+				}
+			}
+		}
+	})
+	return p
+}
+
+type Methods struct {
+	List   []Method
+	once   sync.Once
+	byName map[protoreflect.Name]*Method // protected by once
+}
+
+func (p *Methods) Len() int {
+	return len(p.List)
+}
+func (p *Methods) Get(i int) protoreflect.MethodDescriptor {
+	return &p.List[i]
+}
+func (p *Methods) ByName(s protoreflect.Name) protoreflect.MethodDescriptor {
+	if d := p.lazyInit().byName[s]; d != nil {
+		return d
+	}
+	return nil
+}
+func (p *Methods) Format(s fmt.State, r rune) {
+	descfmt.FormatList(s, r, p)
+}
+func (p *Methods) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Methods) lazyInit() *Methods {
+	p.once.Do(func() {
+		if len(p.List) > 0 {
+			p.byName = make(map[protoreflect.Name]*Method, len(p.List))
+			for i := range p.List {
+				d := &p.List[i]
+				if _, ok := p.byName[d.Name()]; !ok {
+					p.byName[d.Name()] = d
+				}
+			}
+		}
+	})
+	return p
+}
diff --git a/internal/filedesc/desc_test.go b/internal/filedesc/desc_test.go
new file mode 100644
index 0000000..b6e0f7d
--- /dev/null
+++ b/internal/filedesc/desc_test.go
@@ -0,0 +1,772 @@
+// 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 filedesc_test
+
+import (
+	"fmt"
+	"reflect"
+	"regexp"
+	"strconv"
+	"strings"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	detrand "google.golang.org/protobuf/internal/detrand"
+	"google.golang.org/protobuf/internal/filedesc"
+	scalar "google.golang.org/protobuf/internal/scalar"
+	"google.golang.org/protobuf/proto"
+	pdesc "google.golang.org/protobuf/reflect/protodesc"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+
+	"google.golang.org/protobuf/types/descriptorpb"
+)
+
+func init() {
+	// Disable detrand to enable direct comparisons on outputs.
+	detrand.Disable()
+}
+
+// TODO: Test protodesc.NewFile with imported files.
+
+func TestFile(t *testing.T) {
+	f1 := &descriptorpb.FileDescriptorProto{
+		Syntax:  scalar.String("proto2"),
+		Name:    scalar.String("path/to/file.proto"),
+		Package: scalar.String("test"),
+		Options: &descriptorpb.FileOptions{Deprecated: scalar.Bool(true)},
+		MessageType: []*descriptorpb.DescriptorProto{{
+			Name: scalar.String("A"),
+			Options: &descriptorpb.MessageOptions{
+				MapEntry:   scalar.Bool(true),
+				Deprecated: scalar.Bool(true),
+			},
+			Field: []*descriptorpb.FieldDescriptorProto{{
+				Name:    scalar.String("key"),
+				Number:  scalar.Int32(1),
+				Options: &descriptorpb.FieldOptions{Deprecated: scalar.Bool(true)},
+				Label:   descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:    descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+			}, {
+				Name:     scalar.String("value"),
+				Number:   scalar.Int32(2),
+				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName: scalar.String(".test.B"),
+			}},
+		}, {
+			Name: scalar.String("B"),
+			Field: []*descriptorpb.FieldDescriptorProto{{
+				Name:         scalar.String("field_one"),
+				Number:       scalar.Int32(1),
+				Label:        descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:         descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+				DefaultValue: scalar.String("hello, \"world!\"\n"),
+				OneofIndex:   scalar.Int32(0),
+			}, {
+				Name:         scalar.String("field_two"),
+				JsonName:     scalar.String("Field2"),
+				Number:       scalar.Int32(2),
+				Label:        descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:         descriptorpb.FieldDescriptorProto_Type(pref.EnumKind).Enum(),
+				DefaultValue: scalar.String("BAR"),
+				TypeName:     scalar.String(".test.E1"),
+				OneofIndex:   scalar.Int32(1),
+			}, {
+				Name:       scalar.String("field_three"),
+				Number:     scalar.Int32(3),
+				Label:      descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:       descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName:   scalar.String(".test.C"),
+				OneofIndex: scalar.Int32(1),
+			}, {
+				Name:     scalar.String("field_four"),
+				JsonName: scalar.String("Field4"),
+				Number:   scalar.Int32(4),
+				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName: scalar.String(".test.A"),
+			}, {
+				Name:    scalar.String("field_five"),
+				Number:  scalar.Int32(5),
+				Label:   descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:    descriptorpb.FieldDescriptorProto_Type(pref.Int32Kind).Enum(),
+				Options: &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+			}, {
+				Name:   scalar.String("field_six"),
+				Number: scalar.Int32(6),
+				Label:  descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(),
+				Type:   descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
+			}},
+			OneofDecl: []*descriptorpb.OneofDescriptorProto{
+				{
+					Name: scalar.String("O1"),
+					Options: &descriptorpb.OneofOptions{
+						UninterpretedOption: []*descriptorpb.UninterpretedOption{
+							{StringValue: []byte("option")},
+						},
+					},
+				},
+				{Name: scalar.String("O2")},
+			},
+			ReservedName: []string{"fizz", "buzz"},
+			ReservedRange: []*descriptorpb.DescriptorProto_ReservedRange{
+				{Start: scalar.Int32(100), End: scalar.Int32(200)},
+				{Start: scalar.Int32(300), End: scalar.Int32(301)},
+			},
+			ExtensionRange: []*descriptorpb.DescriptorProto_ExtensionRange{
+				{Start: scalar.Int32(1000), End: scalar.Int32(2000)},
+				{Start: scalar.Int32(3000), End: scalar.Int32(3001), Options: new(descriptorpb.ExtensionRangeOptions)},
+			},
+		}, {
+			Name: scalar.String("C"),
+			NestedType: []*descriptorpb.DescriptorProto{{
+				Name: scalar.String("A"),
+				Field: []*descriptorpb.FieldDescriptorProto{{
+					Name:         scalar.String("F"),
+					Number:       scalar.Int32(1),
+					Label:        descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(),
+					Type:         descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
+					DefaultValue: scalar.String(`dead\276\357`),
+				}},
+			}},
+			EnumType: []*descriptorpb.EnumDescriptorProto{{
+				Name: scalar.String("E1"),
+				Value: []*descriptorpb.EnumValueDescriptorProto{
+					{Name: scalar.String("FOO"), Number: scalar.Int32(0)},
+					{Name: scalar.String("BAR"), Number: scalar.Int32(1)},
+				},
+			}},
+			Extension: []*descriptorpb.FieldDescriptorProto{{
+				Name:     scalar.String("X"),
+				Number:   scalar.Int32(1000),
+				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName: scalar.String(".test.C"),
+				Extendee: scalar.String(".test.B"),
+			}},
+		}},
+		EnumType: []*descriptorpb.EnumDescriptorProto{{
+			Name:    scalar.String("E1"),
+			Options: &descriptorpb.EnumOptions{Deprecated: scalar.Bool(true)},
+			Value: []*descriptorpb.EnumValueDescriptorProto{
+				{
+					Name:    scalar.String("FOO"),
+					Number:  scalar.Int32(0),
+					Options: &descriptorpb.EnumValueOptions{Deprecated: scalar.Bool(true)},
+				},
+				{Name: scalar.String("BAR"), Number: scalar.Int32(1)},
+			},
+			ReservedName: []string{"FIZZ", "BUZZ"},
+			ReservedRange: []*descriptorpb.EnumDescriptorProto_EnumReservedRange{
+				{Start: scalar.Int32(10), End: scalar.Int32(19)},
+				{Start: scalar.Int32(30), End: scalar.Int32(30)},
+			},
+		}},
+		Extension: []*descriptorpb.FieldDescriptorProto{{
+			Name:     scalar.String("X"),
+			Number:   scalar.Int32(1000),
+			Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+			Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+			Options:  &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+			TypeName: scalar.String(".test.C"),
+			Extendee: scalar.String(".test.B"),
+		}},
+		Service: []*descriptorpb.ServiceDescriptorProto{{
+			Name:    scalar.String("S"),
+			Options: &descriptorpb.ServiceOptions{Deprecated: scalar.Bool(true)},
+			Method: []*descriptorpb.MethodDescriptorProto{{
+				Name:            scalar.String("M"),
+				InputType:       scalar.String(".test.A"),
+				OutputType:      scalar.String(".test.C.A"),
+				ClientStreaming: scalar.Bool(true),
+				ServerStreaming: scalar.Bool(true),
+				Options:         &descriptorpb.MethodOptions{Deprecated: scalar.Bool(true)},
+			}},
+		}},
+	}
+	fd1, err := pdesc.NewFile(f1, nil)
+	if err != nil {
+		t.Fatalf("protodesc.NewFile() error: %v", err)
+	}
+
+	b, err := proto.Marshal(f1)
+	if err != nil {
+		t.Fatalf("proto.Marshal() error: %v", err)
+	}
+	fd2 := filedesc.DescBuilder{RawDescriptor: b}.Build().File
+
+	tests := []struct {
+		name string
+		desc pref.FileDescriptor
+	}{
+		{"protodesc.NewFile", fd1},
+		{"filedesc.DescBuilder.Build", fd2},
+	}
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			// Run sub-tests in parallel to induce potential races.
+			for i := 0; i < 2; i++ {
+				t.Run("Accessors", func(t *testing.T) { t.Parallel(); testFileAccessors(t, tt.desc) })
+				t.Run("Format", func(t *testing.T) { t.Parallel(); testFileFormat(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{}
+	want := M{
+		"Parent":        nil,
+		"Index":         0,
+		"Syntax":        pref.Proto2,
+		"Name":          pref.Name("test"),
+		"FullName":      pref.FullName("test"),
+		"Path":          "path/to/file.proto",
+		"Package":       pref.FullName("test"),
+		"IsPlaceholder": false,
+		"Options":       &descriptorpb.FileOptions{Deprecated: scalar.Bool(true)},
+		"Messages": M{
+			"Len": 3,
+			"Get:0": M{
+				"Parent":        M{"FullName": pref.FullName("test")},
+				"Index":         0,
+				"Syntax":        pref.Proto2,
+				"Name":          pref.Name("A"),
+				"FullName":      pref.FullName("test.A"),
+				"IsPlaceholder": false,
+				"IsMapEntry":    true,
+				"Options": &descriptorpb.MessageOptions{
+					MapEntry:   scalar.Bool(true),
+					Deprecated: scalar.Bool(true),
+				},
+				"Fields": M{
+					"Len": 2,
+					"ByNumber:1": M{
+						"Parent":            M{"FullName": pref.FullName("test.A")},
+						"Index":             0,
+						"Name":              pref.Name("key"),
+						"FullName":          pref.FullName("test.A.key"),
+						"Number":            pref.FieldNumber(1),
+						"Cardinality":       pref.Optional,
+						"Kind":              pref.StringKind,
+						"Options":           &descriptorpb.FieldOptions{Deprecated: scalar.Bool(true)},
+						"HasJSONName":       false,
+						"JSONName":          "key",
+						"IsPacked":          false,
+						"IsList":            false,
+						"IsMap":             false,
+						"IsExtension":       false,
+						"IsWeak":            false,
+						"Default":           "",
+						"ContainingOneof":   nil,
+						"ContainingMessage": M{"FullName": pref.FullName("test.A")},
+						"Message":           nil,
+						"Enum":              nil,
+					},
+					"ByNumber:2": M{
+						"Parent":            M{"FullName": pref.FullName("test.A")},
+						"Index":             1,
+						"Name":              pref.Name("value"),
+						"FullName":          pref.FullName("test.A.value"),
+						"Number":            pref.FieldNumber(2),
+						"Cardinality":       pref.Optional,
+						"Kind":              pref.MessageKind,
+						"JSONName":          "value",
+						"IsPacked":          false,
+						"IsList":            false,
+						"IsMap":             false,
+						"IsExtension":       false,
+						"IsWeak":            false,
+						"Default":           nil,
+						"ContainingOneof":   nil,
+						"ContainingMessage": M{"FullName": pref.FullName("test.A")},
+						"Message":           M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
+						"Enum":              nil,
+					},
+					"ByNumber:3": nil,
+				},
+				"Oneofs":          M{"Len": 0},
+				"RequiredNumbers": M{"Len": 0},
+				"ExtensionRanges": M{"Len": 0},
+				"Messages":        M{"Len": 0},
+				"Enums":           M{"Len": 0},
+				"Extensions":      M{"Len": 0},
+			},
+			"ByName:B": M{
+				"Name":  pref.Name("B"),
+				"Index": 1,
+				"Fields": M{
+					"Len":                  6,
+					"ByJSONName:field_one": nil,
+					"ByJSONName:fieldOne": M{
+						"Name":              pref.Name("field_one"),
+						"Index":             0,
+						"JSONName":          "fieldOne",
+						"Default":           "hello, \"world!\"\n",
+						"ContainingOneof":   M{"Name": pref.Name("O1"), "IsPlaceholder": false},
+						"ContainingMessage": M{"FullName": pref.FullName("test.B")},
+					},
+					"ByJSONName:fieldTwo": nil,
+					"ByJSONName:Field2": M{
+						"Name":            pref.Name("field_two"),
+						"Index":           1,
+						"HasJSONName":     true,
+						"JSONName":        "Field2",
+						"Default":         pref.EnumNumber(1),
+						"ContainingOneof": M{"Name": pref.Name("O2"), "IsPlaceholder": false},
+					},
+					"ByName:fieldThree": nil,
+					"ByName:field_three": M{
+						"IsExtension":       false,
+						"IsMap":             false,
+						"MapKey":            nil,
+						"MapValue":          nil,
+						"Message":           M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
+						"ContainingOneof":   M{"Name": pref.Name("O2"), "IsPlaceholder": false},
+						"ContainingMessage": M{"FullName": pref.FullName("test.B")},
+					},
+					"ByNumber:12": nil,
+					"ByNumber:4": M{
+						"Cardinality": pref.Repeated,
+						"IsExtension": false,
+						"IsList":      false,
+						"IsMap":       true,
+						"MapKey":      M{"Kind": pref.StringKind},
+						"MapValue":    M{"Kind": pref.MessageKind, "Message": M{"FullName": pref.FullName("test.B")}},
+						"Default":     nil,
+						"Message":     M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
+					},
+					"ByNumber:5": M{
+						"Cardinality": pref.Repeated,
+						"Kind":        pref.Int32Kind,
+						"IsPacked":    true,
+						"IsList":      true,
+						"IsMap":       false,
+						"Default":     int32(0),
+					},
+					"ByNumber:6": M{
+						"Cardinality":     pref.Required,
+						"Default":         []byte(nil),
+						"ContainingOneof": nil,
+					},
+				},
+				"Oneofs": M{
+					"Len":       2,
+					"ByName:O0": nil,
+					"ByName:O1": M{
+						"FullName": pref.FullName("test.B.O1"),
+						"Index":    0,
+						"Options": &descriptorpb.OneofOptions{
+							UninterpretedOption: []*descriptorpb.UninterpretedOption{
+								{StringValue: []byte("option")},
+							},
+						},
+						"Fields": M{
+							"Len":   1,
+							"Get:0": M{"FullName": pref.FullName("test.B.field_one")},
+						},
+					},
+					"Get:1": M{
+						"FullName": pref.FullName("test.B.O2"),
+						"Index":    1,
+						"Fields": M{
+							"Len":              2,
+							"ByName:field_two": M{"Name": pref.Name("field_two")},
+							"Get:1":            M{"Name": pref.Name("field_three")},
+						},
+					},
+				},
+				"ReservedNames": M{
+					"Len":         2,
+					"Get:0":       pref.Name("fizz"),
+					"Has:buzz":    true,
+					"Has:noexist": false,
+				},
+				"ReservedRanges": M{
+					"Len":     2,
+					"Get:0":   [2]pref.FieldNumber{100, 200},
+					"Has:99":  false,
+					"Has:100": true,
+					"Has:150": true,
+					"Has:199": true,
+					"Has:200": false,
+					"Has:300": true,
+					"Has:301": false,
+				},
+				"RequiredNumbers": M{
+					"Len":   1,
+					"Get:0": pref.FieldNumber(6),
+					"Has:1": false,
+					"Has:6": true,
+				},
+				"ExtensionRanges": M{
+					"Len":      2,
+					"Get:0":    [2]pref.FieldNumber{1000, 2000},
+					"Has:999":  false,
+					"Has:1000": true,
+					"Has:1500": true,
+					"Has:1999": true,
+					"Has:2000": false,
+					"Has:3000": true,
+					"Has:3001": false,
+				},
+				"ExtensionRangeOptions:0": (*descriptorpb.ExtensionRangeOptions)(nil),
+				"ExtensionRangeOptions:1": new(descriptorpb.ExtensionRangeOptions),
+			},
+			"Get:2": M{
+				"Name":  pref.Name("C"),
+				"Index": 2,
+				"Messages": M{
+					"Len":   1,
+					"Get:0": M{"FullName": pref.FullName("test.C.A")},
+				},
+				"Enums": M{
+					"Len":   1,
+					"Get:0": M{"FullName": pref.FullName("test.C.E1")},
+				},
+				"Extensions": M{
+					"Len":   1,
+					"Get:0": M{"FullName": pref.FullName("test.C.X")},
+				},
+			},
+		},
+		"Enums": M{
+			"Len": 1,
+			"Get:0": M{
+				"Name":    pref.Name("E1"),
+				"Options": &descriptorpb.EnumOptions{Deprecated: scalar.Bool(true)},
+				"Values": M{
+					"Len":        2,
+					"ByName:Foo": nil,
+					"ByName:FOO": M{
+						"FullName": pref.FullName("test.FOO"),
+						"Options":  &descriptorpb.EnumValueOptions{Deprecated: scalar.Bool(true)},
+					},
+					"ByNumber:2": nil,
+					"ByNumber:1": M{"FullName": pref.FullName("test.BAR")},
+				},
+				"ReservedNames": M{
+					"Len":         2,
+					"Get:0":       pref.Name("FIZZ"),
+					"Has:BUZZ":    true,
+					"Has:NOEXIST": false,
+				},
+				"ReservedRanges": M{
+					"Len":    2,
+					"Get:0":  [2]pref.EnumNumber{10, 19},
+					"Has:9":  false,
+					"Has:10": true,
+					"Has:15": true,
+					"Has:19": true,
+					"Has:20": false,
+					"Has:30": true,
+					"Has:31": false,
+				},
+			},
+		},
+		"Extensions": M{
+			"Len": 1,
+			"ByName:X": M{
+				"Name":              pref.Name("X"),
+				"Number":            pref.FieldNumber(1000),
+				"Cardinality":       pref.Repeated,
+				"Kind":              pref.MessageKind,
+				"IsExtension":       true,
+				"IsPacked":          true,
+				"IsList":            true,
+				"IsMap":             false,
+				"MapKey":            nil,
+				"MapValue":          nil,
+				"ContainingOneof":   nil,
+				"ContainingMessage": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
+				"Message":           M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
+				"Options":           &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+			},
+		},
+		"Services": M{
+			"Len":      1,
+			"ByName:s": nil,
+			"ByName:S": M{
+				"Parent":   M{"FullName": pref.FullName("test")},
+				"Name":     pref.Name("S"),
+				"FullName": pref.FullName("test.S"),
+				"Options":  &descriptorpb.ServiceOptions{Deprecated: scalar.Bool(true)},
+				"Methods": M{
+					"Len": 1,
+					"Get:0": M{
+						"Parent":            M{"FullName": pref.FullName("test.S")},
+						"Name":              pref.Name("M"),
+						"FullName":          pref.FullName("test.S.M"),
+						"Input":             M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
+						"Output":            M{"FullName": pref.FullName("test.C.A"), "IsPlaceholder": false},
+						"IsStreamingClient": true,
+						"IsStreamingServer": true,
+						"Options":           &descriptorpb.MethodOptions{Deprecated: scalar.Bool(true)},
+					},
+				},
+			},
+		},
+	}
+	checkAccessors(t, "", reflect.ValueOf(fd), want)
+}
+func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]interface{}) {
+	p0 := p
+	defer func() {
+		if ex := recover(); ex != nil {
+			t.Errorf("panic at %v: %v", p, ex)
+		}
+	}()
+
+	if rv.Interface() == nil {
+		t.Errorf("%v is nil, want non-nil", p)
+		return
+	}
+	for s, v := range want {
+		// Call the accessor method.
+		p = p0 + "." + s
+		var rets []reflect.Value
+		if i := strings.IndexByte(s, ':'); i >= 0 {
+			// Accessor method takes in a single argument, which is encoded
+			// after the accessor name, separated by a ':' delimiter.
+			fnc := rv.MethodByName(s[:i])
+			arg := reflect.New(fnc.Type().In(0)).Elem()
+			s = s[i+len(":"):]
+			switch arg.Kind() {
+			case reflect.String:
+				arg.SetString(s)
+			case reflect.Int32, reflect.Int:
+				n, _ := strconv.ParseInt(s, 0, 64)
+				arg.SetInt(n)
+			}
+			rets = fnc.Call([]reflect.Value{arg})
+		} else {
+			rets = rv.MethodByName(s).Call(nil)
+		}
+
+		// Check that (val, ok) pattern is internally consistent.
+		if len(rets) == 2 {
+			if rets[0].IsNil() && rets[1].Bool() {
+				t.Errorf("%v = (nil, true), want (nil, false)", p)
+			}
+			if !rets[0].IsNil() && !rets[1].Bool() {
+				t.Errorf("%v = (non-nil, false), want (non-nil, true)", p)
+			}
+		}
+
+		// Check that the accessor output matches.
+		if want, ok := v.(map[string]interface{}); ok {
+			checkAccessors(t, p, rets[0], want)
+			continue
+		}
+
+		got := rets[0].Interface()
+		if pv, ok := got.(pref.Value); ok {
+			got = pv.Interface()
+		}
+
+		// Compare with proto.Equal if possible.
+		gotMsg, gotMsgOK := got.(proto.Message)
+		wantMsg, wantMsgOK := v.(proto.Message)
+		if gotMsgOK && wantMsgOK {
+			gotNil := reflect.ValueOf(gotMsg).IsNil()
+			wantNil := reflect.ValueOf(wantMsg).IsNil()
+			switch {
+			case !gotNil && wantNil:
+				t.Errorf("%v = non-nil, want nil", p)
+			case gotNil && !wantNil:
+				t.Errorf("%v = nil, want non-nil", p)
+			case !proto.Equal(gotMsg, wantMsg):
+				t.Errorf("%v = %v, want %v", p, gotMsg, wantMsg)
+			}
+			continue
+		}
+
+		if want := v; !reflect.DeepEqual(got, want) {
+			t.Errorf("%v = %T(%v), want %T(%v)", p, got, got, want, want)
+		}
+	}
+}
+
+func testFileFormat(t *testing.T, fd pref.FileDescriptor) {
+	const want = `FileDescriptor{
+	Syntax:  proto2
+	Path:    "path/to/file.proto"
+	Package: test
+	Messages: [{
+		Name:       A
+		IsMapEntry: true
+		Fields: [{
+			Name:        key
+			Number:      1
+			Cardinality: optional
+			Kind:        string
+			JSONName:    "key"
+		}, {
+			Name:        value
+			Number:      2
+			Cardinality: optional
+			Kind:        message
+			JSONName:    "value"
+			Message:     test.B
+		}]
+	}, {
+		Name: B
+		Fields: [{
+			Name:        field_one
+			Number:      1
+			Cardinality: optional
+			Kind:        string
+			JSONName:    "fieldOne"
+			HasDefault:  true
+			Default:     "hello, \"world!\"\n"
+			Oneof:       O1
+		}, {
+			Name:        field_two
+			Number:      2
+			Cardinality: optional
+			Kind:        enum
+			HasJSONName: true
+			JSONName:    "Field2"
+			HasDefault:  true
+			Default:     1
+			Oneof:       O2
+			Enum:        test.E1
+		}, {
+			Name:        field_three
+			Number:      3
+			Cardinality: optional
+			Kind:        message
+			JSONName:    "fieldThree"
+			Oneof:       O2
+			Message:     test.C
+		}, {
+			Name:        field_four
+			Number:      4
+			Cardinality: repeated
+			Kind:        message
+			HasJSONName: true
+			JSONName:    "Field4"
+			IsMap:       true
+			MapKey:      string
+			MapValue:    test.B
+		}, {
+			Name:        field_five
+			Number:      5
+			Cardinality: repeated
+			Kind:        int32
+			JSONName:    "fieldFive"
+			IsPacked:    true
+			IsList:      true
+		}, {
+			Name:        field_six
+			Number:      6
+			Cardinality: required
+			Kind:        bytes
+			JSONName:    "fieldSix"
+		}]
+		Oneofs: [{
+			Name:   O1
+			Fields: [field_one]
+		}, {
+			Name:   O2
+			Fields: [field_two, field_three]
+		}]
+		ReservedNames:   [fizz, buzz]
+		ReservedRanges:  [100:200, 300]
+		RequiredNumbers: [6]
+		ExtensionRanges: [1000:2000, 3000]
+	}, {
+		Name: C
+		Messages: [{
+			Name: A
+			Fields: [{
+				Name:        F
+				Number:      1
+				Cardinality: required
+				Kind:        bytes
+				JSONName:    "F"
+				HasDefault:  true
+				Default:     "dead\xbe\xef"
+			}]
+			RequiredNumbers: [1]
+		}]
+		Enums: [{
+			Name: E1
+			Values: [
+				{Name: FOO}
+				{Name: BAR, Number: 1}
+			]
+		}]
+		Extensions: [{
+			Name:        X
+			Number:      1000
+			Cardinality: repeated
+			Kind:        message
+			JSONName:    "X"
+			IsExtension: true
+			IsList:      true
+			Extendee:    test.B
+			Message:     test.C
+		}]
+	}]
+	Enums: [{
+		Name: E1
+		Values: [
+			{Name: FOO}
+			{Name: BAR, Number: 1}
+		]
+		ReservedNames:  [FIZZ, BUZZ]
+		ReservedRanges: [10:20, 30]
+	}]
+	Extensions: [{
+		Name:        X
+		Number:      1000
+		Cardinality: repeated
+		Kind:        message
+		JSONName:    "X"
+		IsPacked:    true
+		IsExtension: true
+		IsList:      true
+		Extendee:    test.B
+		Message:     test.C
+	}]
+	Services: [{
+		Name: S
+		Methods: [{
+			Name:              M
+			Input:             test.A
+			Output:            test.C.A
+			IsStreamingClient: true
+			IsStreamingServer: true
+		}]
+	}]
+}`
+	tests := []struct{ fmt, want string }{{"%v", compactMultiFormat(want)}, {"%+v", want}}
+	for _, tt := range tests {
+		got := fmt.Sprintf(tt.fmt, fd)
+		if diff := cmp.Diff(got, tt.want); diff != "" {
+			t.Errorf("fmt.Sprintf(%q, fd) mismatch (-got +want):\n%s", tt.fmt, diff)
+		}
+	}
+}
+
+// compactMultiFormat returns the single line form of a multi line output.
+func compactMultiFormat(s string) string {
+	var b []byte
+	for _, s := range strings.Split(s, "\n") {
+		s = strings.TrimSpace(s)
+		s = regexp.MustCompile(": +").ReplaceAllString(s, ": ")
+		prevWord := len(b) > 0 && b[len(b)-1] != '[' && b[len(b)-1] != '{'
+		nextWord := len(s) > 0 && s[0] != ']' && s[0] != '}'
+		if prevWord && nextWord {
+			b = append(b, ", "...)
+		}
+		b = append(b, s...)
+	}
+	return string(b)
+}
diff --git a/internal/filedesc/name_pure.go b/internal/filedesc/name_pure.go
new file mode 100644
index 0000000..5626829
--- /dev/null
+++ b/internal/filedesc/name_pure.go
@@ -0,0 +1,34 @@
+// 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.
+
+// +build purego appengine
+
+package filedesc
+
+import pref "google.golang.org/protobuf/reflect/protoreflect"
+
+func getNameBuilder() *nameBuilder { return nil }
+func putNameBuilder(*nameBuilder)  {}
+
+type nameBuilder struct{}
+
+// MakeFullName converts b to a protoreflect.FullName,
+// where b must start with a leading dot.
+func (*nameBuilder) MakeFullName(b []byte) pref.FullName {
+	if len(b) == 0 || b[0] != '.' {
+		panic("name reference must be fully qualified")
+	}
+	return pref.FullName(b[1:])
+}
+
+// AppendFullName is equivalent to protoreflect.FullName.Append.
+func (*nameBuilder) AppendFullName(prefix pref.FullName, name []byte) pref.FullName {
+	return prefix.Append(pref.Name(name))
+}
+
+// MakeString is equivalent to string(b), but optimized for large batches
+// with a shared lifetime.
+func (*nameBuilder) MakeString(b []byte) string {
+	return string(b)
+}
diff --git a/internal/filedesc/name_unsafe.go b/internal/filedesc/name_unsafe.go
new file mode 100644
index 0000000..317114f
--- /dev/null
+++ b/internal/filedesc/name_unsafe.go
@@ -0,0 +1,124 @@
+// 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.
+
+// +build !purego,!appengine
+
+package filedesc
+
+import (
+	"sync"
+	"unsafe"
+
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+var nameBuilderPool = sync.Pool{
+	New: func() interface{} { return new(nameBuilder) },
+}
+
+func getNameBuilder() *nameBuilder {
+	return nameBuilderPool.Get().(*nameBuilder)
+}
+func putNameBuilder(b *nameBuilder) {
+	nameBuilderPool.Put(b)
+}
+
+type nameBuilder struct {
+	sb stringBuilder
+}
+
+// MakeFullName converts b to a protoreflect.FullName,
+// where b must start with a leading dot.
+func (nb *nameBuilder) MakeFullName(b []byte) pref.FullName {
+	if len(b) == 0 || b[0] != '.' {
+		panic("name reference must be fully qualified")
+	}
+	return pref.FullName(nb.MakeString(b[1:]))
+}
+
+// AppendFullName is equivalent to protoreflect.FullName.Append,
+// but optimized for large batches where each name has a shared lifetime.
+func (nb *nameBuilder) AppendFullName(prefix pref.FullName, name []byte) pref.FullName {
+	n := len(prefix) + len(".") + len(name)
+	if len(prefix) == 0 {
+		n -= len(".")
+	}
+	nb.grow(n)
+	nb.sb.WriteString(string(prefix))
+	nb.sb.WriteByte('.')
+	nb.sb.Write(name)
+	return pref.FullName(nb.last(n))
+}
+
+// MakeString is equivalent to string(b), but optimized for large batches
+// with a shared lifetime.
+func (nb *nameBuilder) MakeString(b []byte) string {
+	nb.grow(len(b))
+	nb.sb.Write(b)
+	return nb.last(len(b))
+}
+
+func (nb *nameBuilder) last(n int) string {
+	s := nb.sb.String()
+	return s[len(s)-n:]
+}
+
+func (nb *nameBuilder) grow(n int) {
+	const batchSize = 1 << 16
+	if nb.sb.Cap()-nb.sb.Len() < n {
+		nb.sb.Reset()
+		nb.sb.Grow(batchSize)
+	}
+}
+
+// stringsBuilder is a simplified copy of the strings.Builder from Go1.12:
+//	* removed the shallow copy check
+//	* removed methods that we do not use (e.g. WriteRune)
+//
+// A forked version is used:
+//	* to enable Go1.9 support, but strings.Builder was added in Go1.10
+//	* for the Cap method, which was missing until Go1.12
+//
+// TODO: Remove this when Go1.12 is the minimally supported toolchain version.
+type stringBuilder struct {
+	buf []byte
+}
+
+func (b *stringBuilder) String() string {
+	return *(*string)(unsafe.Pointer(&b.buf))
+}
+func (b *stringBuilder) Len() int {
+	return len(b.buf)
+}
+func (b *stringBuilder) Cap() int {
+	return cap(b.buf)
+}
+func (b *stringBuilder) Reset() {
+	b.buf = nil
+}
+func (b *stringBuilder) grow(n int) {
+	buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
+	copy(buf, b.buf)
+	b.buf = buf
+}
+func (b *stringBuilder) Grow(n int) {
+	if n < 0 {
+		panic("stringBuilder.Grow: negative count")
+	}
+	if cap(b.buf)-len(b.buf) < n {
+		b.grow(n)
+	}
+}
+func (b *stringBuilder) Write(p []byte) (int, error) {
+	b.buf = append(b.buf, p...)
+	return len(p), nil
+}
+func (b *stringBuilder) WriteByte(c byte) error {
+	b.buf = append(b.buf, c)
+	return nil
+}
+func (b *stringBuilder) WriteString(s string) (int, error) {
+	b.buf = append(b.buf, s...)
+	return len(s), nil
+}
diff --git a/internal/filedesc/placeholder.go b/internal/filedesc/placeholder.go
new file mode 100644
index 0000000..be95831
--- /dev/null
+++ b/internal/filedesc/placeholder.go
@@ -0,0 +1,90 @@
+// Copyright 2019 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 filedesc
+
+import (
+	"google.golang.org/protobuf/internal/descopts"
+	"google.golang.org/protobuf/internal/pragma"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+var (
+	emptyNames        = new(Names)
+	emptyEnumRanges   = new(EnumRanges)
+	emptyFieldRanges  = new(FieldRanges)
+	emptyFieldNumbers = new(FieldNumbers)
+
+	emptyFiles      = new(FileImports)
+	emptyMessages   = new(Messages)
+	emptyFields     = new(Fields)
+	emptyOneofs     = new(Oneofs)
+	emptyEnums      = new(Enums)
+	emptyEnumValues = new(EnumValues)
+	emptyExtensions = new(Extensions)
+	emptyServices   = new(Services)
+)
+
+// PlaceholderFile is a placeholder, representing only the file path.
+type PlaceholderFile string
+
+func (f PlaceholderFile) ParentFile() pref.FileDescriptor       { return f }
+func (f PlaceholderFile) Parent() pref.Descriptor               { return nil }
+func (f PlaceholderFile) Index() int                            { return 0 }
+func (f PlaceholderFile) Syntax() pref.Syntax                   { return 0 }
+func (f PlaceholderFile) Name() pref.Name                       { return "" }
+func (f PlaceholderFile) FullName() pref.FullName               { return "" }
+func (f PlaceholderFile) IsPlaceholder() bool                   { return true }
+func (f PlaceholderFile) Options() pref.ProtoMessage            { return descopts.File }
+func (f PlaceholderFile) Path() string                          { return string(f) }
+func (f PlaceholderFile) Package() pref.FullName                { return "" }
+func (f PlaceholderFile) Imports() pref.FileImports             { return emptyFiles }
+func (f PlaceholderFile) Messages() pref.MessageDescriptors     { return emptyMessages }
+func (f PlaceholderFile) Enums() pref.EnumDescriptors           { return emptyEnums }
+func (f PlaceholderFile) Extensions() pref.ExtensionDescriptors { return emptyExtensions }
+func (f PlaceholderFile) Services() pref.ServiceDescriptors     { return emptyServices }
+func (f PlaceholderFile) ProtoType(pref.FileDescriptor)         { return }
+func (f PlaceholderFile) ProtoInternal(pragma.DoNotImplement)   { return }
+
+// PlaceholderEnum is a placeholder, representing only the full name.
+type PlaceholderEnum pref.FullName
+
+func (e PlaceholderEnum) ParentFile() pref.FileDescriptor     { return nil }
+func (e PlaceholderEnum) Parent() pref.Descriptor             { return nil }
+func (e PlaceholderEnum) Index() int                          { return 0 }
+func (e PlaceholderEnum) Syntax() pref.Syntax                 { return 0 }
+func (e PlaceholderEnum) Name() pref.Name                     { return pref.FullName(e).Name() }
+func (e PlaceholderEnum) FullName() pref.FullName             { return pref.FullName(e) }
+func (e PlaceholderEnum) IsPlaceholder() bool                 { return true }
+func (e PlaceholderEnum) Options() pref.ProtoMessage          { return descopts.Enum }
+func (e PlaceholderEnum) Values() pref.EnumValueDescriptors   { return emptyEnumValues }
+func (e PlaceholderEnum) ReservedNames() pref.Names           { return emptyNames }
+func (e PlaceholderEnum) ReservedRanges() pref.EnumRanges     { return emptyEnumRanges }
+func (e PlaceholderEnum) ProtoType(pref.EnumDescriptor)       { return }
+func (e PlaceholderEnum) ProtoInternal(pragma.DoNotImplement) { return }
+
+// PlaceholderMessage is a placeholder, representing only the full name.
+type PlaceholderMessage pref.FullName
+
+func (m PlaceholderMessage) ParentFile() pref.FileDescriptor             { return nil }
+func (m PlaceholderMessage) Parent() pref.Descriptor                     { return nil }
+func (m PlaceholderMessage) Index() int                                  { return 0 }
+func (m PlaceholderMessage) Syntax() pref.Syntax                         { return 0 }
+func (m PlaceholderMessage) Name() pref.Name                             { return pref.FullName(m).Name() }
+func (m PlaceholderMessage) FullName() pref.FullName                     { return pref.FullName(m) }
+func (m PlaceholderMessage) IsPlaceholder() bool                         { return true }
+func (m PlaceholderMessage) Options() pref.ProtoMessage                  { return descopts.Message }
+func (m PlaceholderMessage) IsMapEntry() bool                            { return false }
+func (m PlaceholderMessage) Fields() pref.FieldDescriptors               { return emptyFields }
+func (m PlaceholderMessage) Oneofs() pref.OneofDescriptors               { return emptyOneofs }
+func (m PlaceholderMessage) ReservedNames() pref.Names                   { return emptyNames }
+func (m PlaceholderMessage) ReservedRanges() pref.FieldRanges            { return emptyFieldRanges }
+func (m PlaceholderMessage) RequiredNumbers() pref.FieldNumbers          { return emptyFieldNumbers }
+func (m PlaceholderMessage) ExtensionRanges() pref.FieldRanges           { return emptyFieldRanges }
+func (m PlaceholderMessage) ExtensionRangeOptions(int) pref.ProtoMessage { panic("index out of range") }
+func (m PlaceholderMessage) Messages() pref.MessageDescriptors           { return emptyMessages }
+func (m PlaceholderMessage) Enums() pref.EnumDescriptors                 { return emptyEnums }
+func (m PlaceholderMessage) Extensions() pref.ExtensionDescriptors       { return emptyExtensions }
+func (m PlaceholderMessage) ProtoType(pref.MessageDescriptor)            { return }
+func (m PlaceholderMessage) ProtoInternal(pragma.DoNotImplement)         { return }