blob: 5f46e34dea0bf2cd43d690862bd9bb3699f70b47 [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)
Joe Tsaie407ee12019-06-24 16:19:06 -070043 FindDescriptorByName(pref.FullName) (pref.Descriptor, error)
Joe Tsaid8881392019-06-06 13:01:53 -070044 Register(...pref.FileDescriptor) error
45 }
46}
47
48// resolverByIndex is an interface DescBuilder.FileRegistry may implement.
49// If so, it permits looking up an enum or message dependency based on the
50// sub-list and element index into filetype.TypeBuilder.DependencyIndexes.
51type resolverByIndex interface {
52 FindEnumByIndex(int32, int32, []Enum, []Message) pref.EnumDescriptor
53 FindMessageByIndex(int32, int32, []Enum, []Message) pref.MessageDescriptor
54}
55
56// Indexes of each sub-list in filetype.TypeBuilder.DependencyIndexes.
57const (
58 listFieldDeps int32 = iota
59 listExtTargets
60 listExtDeps
61 listMethInDeps
62 listMethOutDeps
63)
64
65// Build constructs a FileDescriptor given the parameters set in DescBuilder.
66// It assumes that the inputs are well-formed and panics if any inconsistencies
67// are encountered.
68//
69// If NumEnums+NumMessages+NumExtensions+NumServices is zero,
70// then Build automatically derives them from the raw descriptor.
71func (db DescBuilder) Build() (out struct {
72 File pref.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 // Populate the counts if uninitialized.
85 if db.NumEnums+db.NumMessages+db.NumExtensions+db.NumServices == 0 {
86 db.unmarshalCounts(db.RawDescriptor, true)
87 }
88
89 // Initialize resolvers and registries if unpopulated.
90 if db.TypeResolver == nil {
91 db.TypeResolver = preg.GlobalTypes
92 }
93 if db.FileRegistry == nil {
94 db.FileRegistry = preg.GlobalFiles
95 }
96
97 fd := newRawFile(db)
98 out.File = fd
99 out.Enums = fd.allEnums
100 out.Messages = fd.allMessages
101 out.Extensions = fd.allExtensions
102 out.Services = fd.allServices
103
104 if err := db.FileRegistry.Register(fd); err != nil {
105 panic(err)
106 }
107 return out
108}
109
110// unmarshalCounts counts the number of enum, message, extension, and service
111// declarations in the raw message, which is either a FileDescriptorProto
112// or a MessageDescriptorProto depending on whether isFile is set.
113func (db *DescBuilder) unmarshalCounts(b []byte, isFile bool) {
114 for len(b) > 0 {
115 num, typ, n := wire.ConsumeTag(b)
116 b = b[n:]
117 switch typ {
118 case wire.BytesType:
119 v, m := wire.ConsumeBytes(b)
120 b = b[m:]
121 if isFile {
122 switch num {
123 case fieldnum.FileDescriptorProto_EnumType:
124 db.NumEnums++
125 case fieldnum.FileDescriptorProto_MessageType:
126 db.unmarshalCounts(v, false)
127 db.NumMessages++
128 case fieldnum.FileDescriptorProto_Extension:
129 db.NumExtensions++
130 case fieldnum.FileDescriptorProto_Service:
131 db.NumServices++
132 }
133 } else {
134 switch num {
135 case fieldnum.DescriptorProto_EnumType:
136 db.NumEnums++
137 case fieldnum.DescriptorProto_NestedType:
138 db.unmarshalCounts(v, false)
139 db.NumMessages++
140 case fieldnum.DescriptorProto_Extension:
141 db.NumExtensions++
142 }
143 }
144 default:
145 m := wire.ConsumeFieldValue(num, typ, b)
146 b = b[m:]
147 }
148 }
149}