Test Failed
Push — main ( 973aa1...436074 )
by Christian
02:37
created

filetype.Builder.Build   F

Complexity

Conditions 31

Size

Total Lines 121
Code Lines 76

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 31
eloc 76
nop 0
dl 0
loc 121
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like filetype.Builder.Build often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
// 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 filetype provides functionality for wrapping descriptors
6
// with Go type information.
7
package filetype
8
9
import (
10
	"reflect"
11
12
	"google.golang.org/protobuf/internal/descopts"
13
	fdesc "google.golang.org/protobuf/internal/filedesc"
14
	pimpl "google.golang.org/protobuf/internal/impl"
15
	pref "google.golang.org/protobuf/reflect/protoreflect"
16
	preg "google.golang.org/protobuf/reflect/protoregistry"
17
)
18
19
// Builder constructs type descriptors from a raw file descriptor
20
// and associated Go types for each enum and message declaration.
21
//
22
//
23
// Flattened Ordering
24
//
25
// The protobuf type system represents declarations as a tree. Certain nodes in
26
// the tree require us to either associate it with a concrete Go type or to
27
// resolve a dependency, which is information that must be provided separately
28
// since it cannot be derived from the file descriptor alone.
29
//
30
// However, representing a tree as Go literals is difficult to simply do in a
31
// space and time efficient way. Thus, we store them as a flattened list of
32
// objects where the serialization order from the tree-based form is important.
33
//
34
// The "flattened ordering" is defined as a tree traversal of all enum, message,
35
// extension, and service declarations using the following algorithm:
36
//
37
//	def VisitFileDecls(fd):
38
//		for e in fd.Enums:      yield e
39
//		for m in fd.Messages:   yield m
40
//		for x in fd.Extensions: yield x
41
//		for s in fd.Services:   yield s
42
//		for m in fd.Messages:   yield from VisitMessageDecls(m)
43
//
44
//	def VisitMessageDecls(md):
45
//		for e in md.Enums:      yield e
46
//		for m in md.Messages:   yield m
47
//		for x in md.Extensions: yield x
48
//		for m in md.Messages:   yield from VisitMessageDecls(m)
49
//
50
// The traversal starts at the root file descriptor and yields each direct
51
// declaration within each node before traversing into sub-declarations
52
// that children themselves may have.
53
type Builder struct {
54
	// File is the underlying file descriptor builder.
55
	File fdesc.Builder
56
57
	// GoTypes is a unique set of the Go types for all declarations and
58
	// dependencies. Each type is represented as a zero value of the Go type.
59
	//
60
	// Declarations are Go types generated for enums and messages directly
61
	// declared (not publicly imported) in the proto source file.
62
	// Messages for map entries are accounted for, but represented by nil.
63
	// Enum declarations in "flattened ordering" come first, followed by
64
	// message declarations in "flattened ordering".
65
	//
66
	// Dependencies are Go types for enums or messages referenced by
67
	// message fields (excluding weak fields), for parent extended messages of
68
	// extension fields, for enums or messages referenced by extension fields,
69
	// and for input and output messages referenced by service methods.
70
	// Dependencies must come after declarations, but the ordering of
71
	// dependencies themselves is unspecified.
72
	GoTypes []interface{}
73
74
	// DependencyIndexes is an ordered list of indexes into GoTypes for the
75
	// dependencies of messages, extensions, or services.
76
	//
77
	// There are 5 sub-lists in "flattened ordering" concatenated back-to-back:
78
	//	0. Message field dependencies: list of the enum or message type
79
	//	referred to by every message field.
80
	//	1. Extension field targets: list of the extended parent message of
81
	//	every extension.
82
	//	2. Extension field dependencies: list of the enum or message type
83
	//	referred to by every extension field.
84
	//	3. Service method inputs: list of the input message type
85
	//	referred to by every service method.
86
	//	4. Service method outputs: list of the output message type
87
	//	referred to by every service method.
88
	//
89
	// The offset into DependencyIndexes for the start of each sub-list
90
	// is appended to the end in reverse order.
91
	DependencyIndexes []int32
92
93
	// EnumInfos is a list of enum infos in "flattened ordering".
94
	EnumInfos []pimpl.EnumInfo
95
96
	// MessageInfos is a list of message infos in "flattened ordering".
97
	// If provided, the GoType and PBType for each element is populated.
98
	//
99
	// Requirement: len(MessageInfos) == len(Build.Messages)
100
	MessageInfos []pimpl.MessageInfo
101
102
	// ExtensionInfos is a list of extension infos in "flattened ordering".
103
	// Each element is initialized and registered with the protoregistry package.
104
	//
105
	// Requirement: len(LegacyExtensions) == len(Build.Extensions)
106
	ExtensionInfos []pimpl.ExtensionInfo
107
108
	// TypeRegistry is the registry to register each type descriptor.
109
	// If nil, it uses protoregistry.GlobalTypes.
110
	TypeRegistry interface {
111
		RegisterMessage(pref.MessageType) error
112
		RegisterEnum(pref.EnumType) error
113
		RegisterExtension(pref.ExtensionType) error
114
	}
115
}
116
117
// Out is the output of the builder.
118
type Out struct {
119
	File pref.FileDescriptor
120
}
121
122
func (tb Builder) Build() (out Out) {
123
	// Replace the resolver with one that resolves dependencies by index,
124
	// which is faster and more reliable than relying on the global registry.
125
	if tb.File.FileRegistry == nil {
126
		tb.File.FileRegistry = preg.GlobalFiles
127
	}
128
	tb.File.FileRegistry = &resolverByIndex{
129
		goTypes:      tb.GoTypes,
130
		depIdxs:      tb.DependencyIndexes,
131
		fileRegistry: tb.File.FileRegistry,
132
	}
133
134
	// Initialize registry if unpopulated.
135
	if tb.TypeRegistry == nil {
136
		tb.TypeRegistry = preg.GlobalTypes
137
	}
138
139
	fbOut := tb.File.Build()
140
	out.File = fbOut.File
141
142
	// Process enums.
143
	enumGoTypes := tb.GoTypes[:len(fbOut.Enums)]
144
	if len(tb.EnumInfos) != len(fbOut.Enums) {
145
		panic("mismatching enum lengths")
146
	}
147
	if len(fbOut.Enums) > 0 {
148
		for i := range fbOut.Enums {
149
			tb.EnumInfos[i] = pimpl.EnumInfo{
150
				GoReflectType: reflect.TypeOf(enumGoTypes[i]),
151
				Desc:          &fbOut.Enums[i],
152
			}
153
			// Register enum types.
154
			if err := tb.TypeRegistry.RegisterEnum(&tb.EnumInfos[i]); err != nil {
155
				panic(err)
156
			}
157
		}
158
	}
159
160
	// Process messages.
161
	messageGoTypes := tb.GoTypes[len(fbOut.Enums):][:len(fbOut.Messages)]
162
	if len(tb.MessageInfos) != len(fbOut.Messages) {
163
		panic("mismatching message lengths")
164
	}
165
	if len(fbOut.Messages) > 0 {
166
		for i := range fbOut.Messages {
167
			if messageGoTypes[i] == nil {
168
				continue // skip map entry
169
			}
170
171
			tb.MessageInfos[i].GoReflectType = reflect.TypeOf(messageGoTypes[i])
172
			tb.MessageInfos[i].Desc = &fbOut.Messages[i]
173
174
			// Register message types.
175
			if err := tb.TypeRegistry.RegisterMessage(&tb.MessageInfos[i]); err != nil {
176
				panic(err)
177
			}
178
		}
179
180
		// As a special-case for descriptor.proto,
181
		// locally register concrete message type for the options.
182
		if out.File.Path() == "google/protobuf/descriptor.proto" && out.File.Package() == "google.protobuf" {
183
			for i := range fbOut.Messages {
184
				switch fbOut.Messages[i].Name() {
185
				case "FileOptions":
186
					descopts.File = messageGoTypes[i].(pref.ProtoMessage)
187
				case "EnumOptions":
188
					descopts.Enum = messageGoTypes[i].(pref.ProtoMessage)
189
				case "EnumValueOptions":
190
					descopts.EnumValue = messageGoTypes[i].(pref.ProtoMessage)
191
				case "MessageOptions":
192
					descopts.Message = messageGoTypes[i].(pref.ProtoMessage)
193
				case "FieldOptions":
194
					descopts.Field = messageGoTypes[i].(pref.ProtoMessage)
195
				case "OneofOptions":
196
					descopts.Oneof = messageGoTypes[i].(pref.ProtoMessage)
197
				case "ExtensionRangeOptions":
198
					descopts.ExtensionRange = messageGoTypes[i].(pref.ProtoMessage)
199
				case "ServiceOptions":
200
					descopts.Service = messageGoTypes[i].(pref.ProtoMessage)
201
				case "MethodOptions":
202
					descopts.Method = messageGoTypes[i].(pref.ProtoMessage)
203
				}
204
			}
205
		}
206
	}
207
208
	// Process extensions.
209
	if len(tb.ExtensionInfos) != len(fbOut.Extensions) {
210
		panic("mismatching extension lengths")
211
	}
212
	var depIdx int32
213
	for i := range fbOut.Extensions {
214
		// For enum and message kinds, determine the referent Go type so
215
		// that we can construct their constructors.
216
		const listExtDeps = 2
217
		var goType reflect.Type
218
		switch fbOut.Extensions[i].L1.Kind {
219
		case pref.EnumKind:
220
			j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx)
221
			goType = reflect.TypeOf(tb.GoTypes[j])
222
			depIdx++
223
		case pref.MessageKind, pref.GroupKind:
224
			j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx)
225
			goType = reflect.TypeOf(tb.GoTypes[j])
226
			depIdx++
227
		default:
228
			goType = goTypeForPBKind[fbOut.Extensions[i].L1.Kind]
229
		}
230
		if fbOut.Extensions[i].IsList() {
231
			goType = reflect.SliceOf(goType)
232
		}
233
234
		pimpl.InitExtensionInfo(&tb.ExtensionInfos[i], &fbOut.Extensions[i], goType)
235
236
		// Register extension types.
237
		if err := tb.TypeRegistry.RegisterExtension(&tb.ExtensionInfos[i]); err != nil {
238
			panic(err)
239
		}
240
	}
241
242
	return out
243
}
244
245
var goTypeForPBKind = map[pref.Kind]reflect.Type{
246
	pref.BoolKind:     reflect.TypeOf(bool(false)),
247
	pref.Int32Kind:    reflect.TypeOf(int32(0)),
248
	pref.Sint32Kind:   reflect.TypeOf(int32(0)),
249
	pref.Sfixed32Kind: reflect.TypeOf(int32(0)),
250
	pref.Int64Kind:    reflect.TypeOf(int64(0)),
251
	pref.Sint64Kind:   reflect.TypeOf(int64(0)),
252
	pref.Sfixed64Kind: reflect.TypeOf(int64(0)),
253
	pref.Uint32Kind:   reflect.TypeOf(uint32(0)),
254
	pref.Fixed32Kind:  reflect.TypeOf(uint32(0)),
255
	pref.Uint64Kind:   reflect.TypeOf(uint64(0)),
256
	pref.Fixed64Kind:  reflect.TypeOf(uint64(0)),
257
	pref.FloatKind:    reflect.TypeOf(float32(0)),
258
	pref.DoubleKind:   reflect.TypeOf(float64(0)),
259
	pref.StringKind:   reflect.TypeOf(string("")),
260
	pref.BytesKind:    reflect.TypeOf([]byte(nil)),
261
}
262
263
type depIdxs []int32
264
265
// Get retrieves the jth element of the ith sub-list.
266
func (x depIdxs) Get(i, j int32) int32 {
267
	return x[x[int32(len(x))-i-1]+j]
268
}
269
270
type (
271
	resolverByIndex struct {
272
		goTypes []interface{}
273
		depIdxs depIdxs
274
		fileRegistry
275
	}
276
	fileRegistry interface {
277
		FindFileByPath(string) (pref.FileDescriptor, error)
278
		FindDescriptorByName(pref.FullName) (pref.Descriptor, error)
279
		RegisterFile(pref.FileDescriptor) error
280
	}
281
)
282
283
func (r *resolverByIndex) FindEnumByIndex(i, j int32, es []fdesc.Enum, ms []fdesc.Message) pref.EnumDescriptor {
284
	if depIdx := int(r.depIdxs.Get(i, j)); int(depIdx) < len(es)+len(ms) {
285
		return &es[depIdx]
286
	} else {
287
		return pimpl.Export{}.EnumDescriptorOf(r.goTypes[depIdx])
288
	}
289
}
290
291
func (r *resolverByIndex) FindMessageByIndex(i, j int32, es []fdesc.Enum, ms []fdesc.Message) pref.MessageDescriptor {
292
	if depIdx := int(r.depIdxs.Get(i, j)); depIdx < len(es)+len(ms) {
293
		return &ms[depIdx-len(es)]
294
	} else {
295
		return pimpl.Export{}.MessageDescriptorOf(r.goTypes[depIdx])
296
	}
297
}
298