build.go

  1// Copyright 2019 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5// Package filedesc provides functionality for constructing descriptors.
  6//
  7// The types in this package implement interfaces in the protoreflect package
  8// related to protobuf descripriptors.
  9package filedesc
 10
 11import (
 12	"google.golang.org/protobuf/encoding/protowire"
 13	"google.golang.org/protobuf/internal/genid"
 14	"google.golang.org/protobuf/reflect/protoreflect"
 15	"google.golang.org/protobuf/reflect/protoregistry"
 16)
 17
 18// Builder construct a protoreflect.FileDescriptor from the raw descriptor.
 19type Builder struct {
 20	// GoPackagePath is the Go package path that is invoking this builder.
 21	GoPackagePath string
 22
 23	// RawDescriptor is the wire-encoded bytes of FileDescriptorProto
 24	// and must be populated.
 25	RawDescriptor []byte
 26
 27	// NumEnums is the total number of enums declared in the file.
 28	NumEnums int32
 29	// NumMessages is the total number of messages declared in the file.
 30	// It includes the implicit message declarations for map entries.
 31	NumMessages int32
 32	// NumExtensions is the total number of extensions declared in the file.
 33	NumExtensions int32
 34	// NumServices is the total number of services declared in the file.
 35	NumServices int32
 36
 37	// TypeResolver resolves extension field types for descriptor options.
 38	// If nil, it uses protoregistry.GlobalTypes.
 39	TypeResolver interface {
 40		protoregistry.ExtensionTypeResolver
 41	}
 42
 43	// FileRegistry is use to lookup file, enum, and message dependencies.
 44	// Once constructed, the file descriptor is registered here.
 45	// If nil, it uses protoregistry.GlobalFiles.
 46	FileRegistry interface {
 47		FindFileByPath(string) (protoreflect.FileDescriptor, error)
 48		FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
 49		RegisterFile(protoreflect.FileDescriptor) error
 50	}
 51}
 52
 53// resolverByIndex is an interface Builder.FileRegistry may implement.
 54// If so, it permits looking up an enum or message dependency based on the
 55// sub-list and element index into filetype.Builder.DependencyIndexes.
 56type resolverByIndex interface {
 57	FindEnumByIndex(int32, int32, []Enum, []Message) protoreflect.EnumDescriptor
 58	FindMessageByIndex(int32, int32, []Enum, []Message) protoreflect.MessageDescriptor
 59}
 60
 61// Indexes of each sub-list in filetype.Builder.DependencyIndexes.
 62const (
 63	listFieldDeps int32 = iota
 64	listExtTargets
 65	listExtDeps
 66	listMethInDeps
 67	listMethOutDeps
 68)
 69
 70// Out is the output of the Builder.
 71type Out struct {
 72	File protoreflect.FileDescriptor
 73
 74	// Enums is all enum descriptors in "flattened ordering".
 75	Enums []Enum
 76	// Messages is all message descriptors in "flattened ordering".
 77	// It includes the implicit message declarations for map entries.
 78	Messages []Message
 79	// Extensions is all extension descriptors in "flattened ordering".
 80	Extensions []Extension
 81	// Service is all service descriptors in "flattened ordering".
 82	Services []Service
 83}
 84
 85// Build constructs a FileDescriptor given the parameters set in Builder.
 86// It assumes that the inputs are well-formed and panics if any inconsistencies
 87// are encountered.
 88//
 89// If NumEnums+NumMessages+NumExtensions+NumServices is zero,
 90// then Build automatically derives them from the raw descriptor.
 91func (db Builder) Build() (out Out) {
 92	// Populate the counts if uninitialized.
 93	if db.NumEnums+db.NumMessages+db.NumExtensions+db.NumServices == 0 {
 94		db.unmarshalCounts(db.RawDescriptor, true)
 95	}
 96
 97	// Initialize resolvers and registries if unpopulated.
 98	if db.TypeResolver == nil {
 99		db.TypeResolver = protoregistry.GlobalTypes
100	}
101	if db.FileRegistry == nil {
102		db.FileRegistry = protoregistry.GlobalFiles
103	}
104
105	fd := newRawFile(db)
106	out.File = fd
107	out.Enums = fd.allEnums
108	out.Messages = fd.allMessages
109	out.Extensions = fd.allExtensions
110	out.Services = fd.allServices
111
112	if err := db.FileRegistry.RegisterFile(fd); err != nil {
113		panic(err)
114	}
115	return out
116}
117
118// unmarshalCounts counts the number of enum, message, extension, and service
119// declarations in the raw message, which is either a FileDescriptorProto
120// or a MessageDescriptorProto depending on whether isFile is set.
121func (db *Builder) unmarshalCounts(b []byte, isFile bool) {
122	for len(b) > 0 {
123		num, typ, n := protowire.ConsumeTag(b)
124		b = b[n:]
125		switch typ {
126		case protowire.BytesType:
127			v, m := protowire.ConsumeBytes(b)
128			b = b[m:]
129			if isFile {
130				switch num {
131				case genid.FileDescriptorProto_EnumType_field_number:
132					db.NumEnums++
133				case genid.FileDescriptorProto_MessageType_field_number:
134					db.unmarshalCounts(v, false)
135					db.NumMessages++
136				case genid.FileDescriptorProto_Extension_field_number:
137					db.NumExtensions++
138				case genid.FileDescriptorProto_Service_field_number:
139					db.NumServices++
140				}
141			} else {
142				switch num {
143				case genid.DescriptorProto_EnumType_field_number:
144					db.NumEnums++
145				case genid.DescriptorProto_NestedType_field_number:
146					db.unmarshalCounts(v, false)
147					db.NumMessages++
148				case genid.DescriptorProto_Extension_field_number:
149					db.NumExtensions++
150				}
151			}
152		default:
153			m := protowire.ConsumeFieldValue(num, typ, b)
154			b = b[m:]
155		}
156	}
157}