blob: 427e26e605d4f62bda342ea848b29ca47d5125e8 [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 (
9 "google.golang.org/protobuf/internal/encoding/wire"
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// DescBuilder construct a protoreflect.FileDescriptor from the raw descriptor.
17type DescBuilder struct {
Joe Tsaiaf570872019-07-14 23:04:40 -070018 // GoPackagePath is the Go package path that is invoking this builder.
19 GoPackagePath string
20
Joe Tsaid8881392019-06-06 13:01:53 -070021 // 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)
Joe Tsaie407ee12019-06-24 16:19:06 -070046 FindDescriptorByName(pref.FullName) (pref.Descriptor, error)
Joe Tsaid8881392019-06-06 13:01:53 -070047 Register(...pref.FileDescriptor) error
48 }
49}
50
51// resolverByIndex is an interface DescBuilder.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.TypeBuilder.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.TypeBuilder.DependencyIndexes.
60const (
61 listFieldDeps int32 = iota
62 listExtTargets
63 listExtDeps
64 listMethInDeps
65 listMethOutDeps
66)
67
68// Build constructs a FileDescriptor given the parameters set in DescBuilder.
69// It assumes that the inputs are well-formed and panics if any inconsistencies
70// are encountered.
71//
72// If NumEnums+NumMessages+NumExtensions+NumServices is zero,
73// then Build automatically derives them from the raw descriptor.
74func (db DescBuilder) Build() (out struct {
75 File pref.FileDescriptor
76
77 // Enums is all enum descriptors in "flattened ordering".
78 Enums []Enum
79 // Messages is all message descriptors in "flattened ordering".
80 // It includes the implicit message declarations for map entries.
81 Messages []Message
82 // Extensions is all extension descriptors in "flattened ordering".
83 Extensions []Extension
84 // Service is all service descriptors in "flattened ordering".
85 Services []Service
86}) {
87 // Populate the counts if uninitialized.
88 if db.NumEnums+db.NumMessages+db.NumExtensions+db.NumServices == 0 {
89 db.unmarshalCounts(db.RawDescriptor, true)
90 }
91
92 // Initialize resolvers and registries if unpopulated.
93 if db.TypeResolver == nil {
94 db.TypeResolver = preg.GlobalTypes
95 }
96 if db.FileRegistry == nil {
97 db.FileRegistry = preg.GlobalFiles
98 }
99
100 fd := newRawFile(db)
101 out.File = fd
102 out.Enums = fd.allEnums
103 out.Messages = fd.allMessages
104 out.Extensions = fd.allExtensions
105 out.Services = fd.allServices
106
107 if err := db.FileRegistry.Register(fd); err != nil {
Joe Tsai831b8f52019-07-17 19:08:21 -0700108 panic(err)
Joe Tsaid8881392019-06-06 13:01:53 -0700109 }
110 return out
111}
112
113// unmarshalCounts counts the number of enum, message, extension, and service
114// declarations in the raw message, which is either a FileDescriptorProto
115// or a MessageDescriptorProto depending on whether isFile is set.
116func (db *DescBuilder) unmarshalCounts(b []byte, isFile bool) {
117 for len(b) > 0 {
118 num, typ, n := wire.ConsumeTag(b)
119 b = b[n:]
120 switch typ {
121 case wire.BytesType:
122 v, m := wire.ConsumeBytes(b)
123 b = b[m:]
124 if isFile {
125 switch num {
126 case fieldnum.FileDescriptorProto_EnumType:
127 db.NumEnums++
128 case fieldnum.FileDescriptorProto_MessageType:
129 db.unmarshalCounts(v, false)
130 db.NumMessages++
131 case fieldnum.FileDescriptorProto_Extension:
132 db.NumExtensions++
133 case fieldnum.FileDescriptorProto_Service:
134 db.NumServices++
135 }
136 } else {
137 switch num {
138 case fieldnum.DescriptorProto_EnumType:
139 db.NumEnums++
140 case fieldnum.DescriptorProto_NestedType:
141 db.unmarshalCounts(v, false)
142 db.NumMessages++
143 case fieldnum.DescriptorProto_Extension:
144 db.NumExtensions++
145 }
146 }
147 default:
148 m := wire.ConsumeFieldValue(num, typ, b)
149 b = b[m:]
150 }
151 }
152}