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