xref: /aosp_15_r20/external/golang-protobuf/internal/filedesc/build.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
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}
158