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