blob: 52cbdf14ed7e31e1373a911389ec05f6022fe485 [file] [log] [blame]
Joe Tsaid8881392019-06-06 13:01:53 -07001// 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 (
Joe Tsai1ac7b532019-07-15 00:04:27 -07009 "log"
10
Joe Tsaid8881392019-06-06 13:01:53 -070011 "google.golang.org/protobuf/internal/encoding/wire"
12 "google.golang.org/protobuf/internal/fieldnum"
13 "google.golang.org/protobuf/reflect/protoreflect"
14 pref "google.golang.org/protobuf/reflect/protoreflect"
15 preg "google.golang.org/protobuf/reflect/protoregistry"
16)
17
18// DescBuilder construct a protoreflect.FileDescriptor from the raw descriptor.
19type DescBuilder struct {
Joe Tsaiaf570872019-07-14 23:04:40 -070020 // GoPackagePath is the Go package path that is invoking this builder.
21 GoPackagePath string
22
Joe Tsaid8881392019-06-06 13:01:53 -070023 // 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 preg.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)
Joe Tsaie407ee12019-06-24 16:19:06 -070048 FindDescriptorByName(pref.FullName) (pref.Descriptor, error)
Joe Tsaid8881392019-06-06 13:01:53 -070049 Register(...pref.FileDescriptor) error
50 }
51}
52
53// resolverByIndex is an interface DescBuilder.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.TypeBuilder.DependencyIndexes.
56type resolverByIndex interface {
57 FindEnumByIndex(int32, int32, []Enum, []Message) pref.EnumDescriptor
58 FindMessageByIndex(int32, int32, []Enum, []Message) pref.MessageDescriptor
59}
60
61// Indexes of each sub-list in filetype.TypeBuilder.DependencyIndexes.
62const (
63 listFieldDeps int32 = iota
64 listExtTargets
65 listExtDeps
66 listMethInDeps
67 listMethOutDeps
68)
69
70// Build constructs a FileDescriptor given the parameters set in DescBuilder.
71// It assumes that the inputs are well-formed and panics if any inconsistencies
72// are encountered.
73//
74// If NumEnums+NumMessages+NumExtensions+NumServices is zero,
75// then Build automatically derives them from the raw descriptor.
76func (db DescBuilder) Build() (out struct {
77 File pref.FileDescriptor
78
79 // Enums is all enum descriptors in "flattened ordering".
80 Enums []Enum
81 // Messages is all message descriptors in "flattened ordering".
82 // It includes the implicit message declarations for map entries.
83 Messages []Message
84 // Extensions is all extension descriptors in "flattened ordering".
85 Extensions []Extension
86 // Service is all service descriptors in "flattened ordering".
87 Services []Service
88}) {
89 // Populate the counts if uninitialized.
90 if db.NumEnums+db.NumMessages+db.NumExtensions+db.NumServices == 0 {
91 db.unmarshalCounts(db.RawDescriptor, true)
92 }
93
94 // Initialize resolvers and registries if unpopulated.
95 if db.TypeResolver == nil {
96 db.TypeResolver = preg.GlobalTypes
97 }
98 if db.FileRegistry == nil {
99 db.FileRegistry = preg.GlobalFiles
100 }
101
102 fd := newRawFile(db)
103 out.File = fd
104 out.Enums = fd.allEnums
105 out.Messages = fd.allMessages
106 out.Extensions = fd.allExtensions
107 out.Services = fd.allServices
108
109 if err := db.FileRegistry.Register(fd); err != nil {
Joe Tsai1ac7b532019-07-15 00:04:27 -0700110 CheckRegistryError(err)
Joe Tsaid8881392019-06-06 13:01:53 -0700111 }
112 return out
113}
114
115// unmarshalCounts counts the number of enum, message, extension, and service
116// declarations in the raw message, which is either a FileDescriptorProto
117// or a MessageDescriptorProto depending on whether isFile is set.
118func (db *DescBuilder) unmarshalCounts(b []byte, isFile bool) {
119 for len(b) > 0 {
120 num, typ, n := wire.ConsumeTag(b)
121 b = b[n:]
122 switch typ {
123 case wire.BytesType:
124 v, m := wire.ConsumeBytes(b)
125 b = b[m:]
126 if isFile {
127 switch num {
128 case fieldnum.FileDescriptorProto_EnumType:
129 db.NumEnums++
130 case fieldnum.FileDescriptorProto_MessageType:
131 db.unmarshalCounts(v, false)
132 db.NumMessages++
133 case fieldnum.FileDescriptorProto_Extension:
134 db.NumExtensions++
135 case fieldnum.FileDescriptorProto_Service:
136 db.NumServices++
137 }
138 } else {
139 switch num {
140 case fieldnum.DescriptorProto_EnumType:
141 db.NumEnums++
142 case fieldnum.DescriptorProto_NestedType:
143 db.unmarshalCounts(v, false)
144 db.NumMessages++
145 case fieldnum.DescriptorProto_Extension:
146 db.NumExtensions++
147 }
148 }
149 default:
150 m := wire.ConsumeFieldValue(num, typ, b)
151 b = b[m:]
152 }
153 }
154}
Joe Tsai1ac7b532019-07-15 00:04:27 -0700155
156// CheckRegistryError handles registration errors.
157// It is a variable so that its behavior can be replaced in another source file.
158var CheckRegistryError = func(err error) {
159 log.Printf(""+
160 "WARNING: %v\n"+
161 "A future release will panic on registration conflicts.\n"+
162 // TODO: Add a URL pointing to documentation on how to resolve conflicts.
163 "\n", err)
164}