1// Copyright 2018 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 protoregistry provides data structures to register and lookup
6// protobuf descriptor types.
7//
8// The Files registry contains file descriptors and provides the ability
9// to iterate over the files or lookup a specific descriptor within the files.
10// Files only contains protobuf descriptors and has no understanding of Go
11// type information that may be associated with each descriptor.
12//
13// The Types registry contains descriptor types for which there is a known
14// Go type associated with that descriptor. It provides the ability to iterate
15// over the registered types or lookup a type by name.
16package protoregistry
17
18import (
19	"fmt"
20	"os"
21	"strings"
22	"sync"
23
24	"google.golang.org/protobuf/internal/encoding/messageset"
25	"google.golang.org/protobuf/internal/errors"
26	"google.golang.org/protobuf/internal/flags"
27	"google.golang.org/protobuf/reflect/protoreflect"
28)
29
30// conflictPolicy configures the policy for handling registration conflicts.
31//
32// It can be over-written at compile time with a linker-initialized variable:
33//	go build -ldflags "-X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn"
34//
35// It can be over-written at program execution with an environment variable:
36//	GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn ./main
37//
38// Neither of the above are covered by the compatibility promise and
39// may be removed in a future release of this module.
40var conflictPolicy = "panic" // "panic" | "warn" | "ignore"
41
42// ignoreConflict reports whether to ignore a registration conflict
43// given the descriptor being registered and the error.
44// It is a variable so that the behavior is easily overridden in another file.
45var ignoreConflict = func(d protoreflect.Descriptor, err error) bool {
46	const env = "GOLANG_PROTOBUF_REGISTRATION_CONFLICT"
47	const faq = "https://developers.google.com/protocol-buffers/docs/reference/go/faq#namespace-conflict"
48	policy := conflictPolicy
49	if v := os.Getenv(env); v != "" {
50		policy = v
51	}
52	switch policy {
53	case "panic":
54		panic(fmt.Sprintf("%v\nSee %v\n", err, faq))
55	case "warn":
56		fmt.Fprintf(os.Stderr, "WARNING: %v\nSee %v\n\n", err, faq)
57		return true
58	case "ignore":
59		return true
60	default:
61		panic("invalid " + env + " value: " + os.Getenv(env))
62	}
63}
64
65var globalMutex sync.RWMutex
66
67// GlobalFiles is a global registry of file descriptors.
68var GlobalFiles *Files = new(Files)
69
70// GlobalTypes is the registry used by default for type lookups
71// unless a local registry is provided by the user.
72var GlobalTypes *Types = new(Types)
73
74// NotFound is a sentinel error value to indicate that the type was not found.
75//
76// Since registry lookup can happen in the critical performance path, resolvers
77// must return this exact error value, not an error wrapping it.
78var NotFound = errors.New("not found")
79
80// Files is a registry for looking up or iterating over files and the
81// descriptors contained within them.
82// The Find and Range methods are safe for concurrent use.
83type Files struct {
84	// The map of descsByName contains:
85	//	EnumDescriptor
86	//	EnumValueDescriptor
87	//	MessageDescriptor
88	//	ExtensionDescriptor
89	//	ServiceDescriptor
90	//	*packageDescriptor
91	//
92	// Note that files are stored as a slice, since a package may contain
93	// multiple files. Only top-level declarations are registered.
94	// Note that enum values are in the top-level since that are in the same
95	// scope as the parent enum.
96	descsByName map[protoreflect.FullName]interface{}
97	filesByPath map[string][]protoreflect.FileDescriptor
98	numFiles    int
99}
100
101type packageDescriptor struct {
102	files []protoreflect.FileDescriptor
103}
104
105// RegisterFile registers the provided file descriptor.
106//
107// If any descriptor within the file conflicts with the descriptor of any
108// previously registered file (e.g., two enums with the same full name),
109// then the file is not registered and an error is returned.
110//
111// It is permitted for multiple files to have the same file path.
112func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error {
113	if r == GlobalFiles {
114		globalMutex.Lock()
115		defer globalMutex.Unlock()
116	}
117	if r.descsByName == nil {
118		r.descsByName = map[protoreflect.FullName]interface{}{
119			"": &packageDescriptor{},
120		}
121		r.filesByPath = make(map[string][]protoreflect.FileDescriptor)
122	}
123	path := file.Path()
124	if prev := r.filesByPath[path]; len(prev) > 0 {
125		r.checkGenProtoConflict(path)
126		err := errors.New("file %q is already registered", file.Path())
127		err = amendErrorWithCaller(err, prev[0], file)
128		if !(r == GlobalFiles && ignoreConflict(file, err)) {
129			return err
130		}
131	}
132
133	for name := file.Package(); name != ""; name = name.Parent() {
134		switch prev := r.descsByName[name]; prev.(type) {
135		case nil, *packageDescriptor:
136		default:
137			err := errors.New("file %q has a package name conflict over %v", file.Path(), name)
138			err = amendErrorWithCaller(err, prev, file)
139			if r == GlobalFiles && ignoreConflict(file, err) {
140				err = nil
141			}
142			return err
143		}
144	}
145	var err error
146	var hasConflict bool
147	rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) {
148		if prev := r.descsByName[d.FullName()]; prev != nil {
149			hasConflict = true
150			err = errors.New("file %q has a name conflict over %v", file.Path(), d.FullName())
151			err = amendErrorWithCaller(err, prev, file)
152			if r == GlobalFiles && ignoreConflict(d, err) {
153				err = nil
154			}
155		}
156	})
157	if hasConflict {
158		return err
159	}
160
161	for name := file.Package(); name != ""; name = name.Parent() {
162		if r.descsByName[name] == nil {
163			r.descsByName[name] = &packageDescriptor{}
164		}
165	}
166	p := r.descsByName[file.Package()].(*packageDescriptor)
167	p.files = append(p.files, file)
168	rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) {
169		r.descsByName[d.FullName()] = d
170	})
171	r.filesByPath[path] = append(r.filesByPath[path], file)
172	r.numFiles++
173	return nil
174}
175
176// Several well-known types were hosted in the google.golang.org/genproto module
177// but were later moved to this module. To avoid a weak dependency on the
178// genproto module (and its relatively large set of transitive dependencies),
179// we rely on a registration conflict to determine whether the genproto version
180// is too old (i.e., does not contain aliases to the new type declarations).
181func (r *Files) checkGenProtoConflict(path string) {
182	if r != GlobalFiles {
183		return
184	}
185	var prevPath string
186	const prevModule = "google.golang.org/genproto"
187	const prevVersion = "cb27e3aa (May 26th, 2020)"
188	switch path {
189	case "google/protobuf/field_mask.proto":
190		prevPath = prevModule + "/protobuf/field_mask"
191	case "google/protobuf/api.proto":
192		prevPath = prevModule + "/protobuf/api"
193	case "google/protobuf/type.proto":
194		prevPath = prevModule + "/protobuf/ptype"
195	case "google/protobuf/source_context.proto":
196		prevPath = prevModule + "/protobuf/source_context"
197	default:
198		return
199	}
200	pkgName := strings.TrimSuffix(strings.TrimPrefix(path, "google/protobuf/"), ".proto")
201	pkgName = strings.Replace(pkgName, "_", "", -1) + "pb" // e.g., "field_mask" => "fieldmaskpb"
202	currPath := "google.golang.org/protobuf/types/known/" + pkgName
203	panic(fmt.Sprintf(""+
204		"duplicate registration of %q\n"+
205		"\n"+
206		"The generated definition for this file has moved:\n"+
207		"\tfrom: %q\n"+
208		"\tto:   %q\n"+
209		"A dependency on the %q module must\n"+
210		"be at version %v or higher.\n"+
211		"\n"+
212		"Upgrade the dependency by running:\n"+
213		"\tgo get -u %v\n",
214		path, prevPath, currPath, prevModule, prevVersion, prevPath))
215}
216
217// FindDescriptorByName looks up a descriptor by the full name.
218//
219// This returns (nil, NotFound) if not found.
220func (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) {
221	if r == nil {
222		return nil, NotFound
223	}
224	if r == GlobalFiles {
225		globalMutex.RLock()
226		defer globalMutex.RUnlock()
227	}
228	prefix := name
229	suffix := nameSuffix("")
230	for prefix != "" {
231		if d, ok := r.descsByName[prefix]; ok {
232			switch d := d.(type) {
233			case protoreflect.EnumDescriptor:
234				if d.FullName() == name {
235					return d, nil
236				}
237			case protoreflect.EnumValueDescriptor:
238				if d.FullName() == name {
239					return d, nil
240				}
241			case protoreflect.MessageDescriptor:
242				if d.FullName() == name {
243					return d, nil
244				}
245				if d := findDescriptorInMessage(d, suffix); d != nil && d.FullName() == name {
246					return d, nil
247				}
248			case protoreflect.ExtensionDescriptor:
249				if d.FullName() == name {
250					return d, nil
251				}
252			case protoreflect.ServiceDescriptor:
253				if d.FullName() == name {
254					return d, nil
255				}
256				if d := d.Methods().ByName(suffix.Pop()); d != nil && d.FullName() == name {
257					return d, nil
258				}
259			}
260			return nil, NotFound
261		}
262		prefix = prefix.Parent()
263		suffix = nameSuffix(name[len(prefix)+len("."):])
264	}
265	return nil, NotFound
266}
267
268func findDescriptorInMessage(md protoreflect.MessageDescriptor, suffix nameSuffix) protoreflect.Descriptor {
269	name := suffix.Pop()
270	if suffix == "" {
271		if ed := md.Enums().ByName(name); ed != nil {
272			return ed
273		}
274		for i := md.Enums().Len() - 1; i >= 0; i-- {
275			if vd := md.Enums().Get(i).Values().ByName(name); vd != nil {
276				return vd
277			}
278		}
279		if xd := md.Extensions().ByName(name); xd != nil {
280			return xd
281		}
282		if fd := md.Fields().ByName(name); fd != nil {
283			return fd
284		}
285		if od := md.Oneofs().ByName(name); od != nil {
286			return od
287		}
288	}
289	if md := md.Messages().ByName(name); md != nil {
290		if suffix == "" {
291			return md
292		}
293		return findDescriptorInMessage(md, suffix)
294	}
295	return nil
296}
297
298type nameSuffix string
299
300func (s *nameSuffix) Pop() (name protoreflect.Name) {
301	if i := strings.IndexByte(string(*s), '.'); i >= 0 {
302		name, *s = protoreflect.Name((*s)[:i]), (*s)[i+1:]
303	} else {
304		name, *s = protoreflect.Name((*s)), ""
305	}
306	return name
307}
308
309// FindFileByPath looks up a file by the path.
310//
311// This returns (nil, NotFound) if not found.
312// This returns an error if multiple files have the same path.
313func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) {
314	if r == nil {
315		return nil, NotFound
316	}
317	if r == GlobalFiles {
318		globalMutex.RLock()
319		defer globalMutex.RUnlock()
320	}
321	fds := r.filesByPath[path]
322	switch len(fds) {
323	case 0:
324		return nil, NotFound
325	case 1:
326		return fds[0], nil
327	default:
328		return nil, errors.New("multiple files named %q", path)
329	}
330}
331
332// NumFiles reports the number of registered files,
333// including duplicate files with the same name.
334func (r *Files) NumFiles() int {
335	if r == nil {
336		return 0
337	}
338	if r == GlobalFiles {
339		globalMutex.RLock()
340		defer globalMutex.RUnlock()
341	}
342	return r.numFiles
343}
344
345// RangeFiles iterates over all registered files while f returns true.
346// If multiple files have the same name, RangeFiles iterates over all of them.
347// The iteration order is undefined.
348func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) {
349	if r == nil {
350		return
351	}
352	if r == GlobalFiles {
353		globalMutex.RLock()
354		defer globalMutex.RUnlock()
355	}
356	for _, files := range r.filesByPath {
357		for _, file := range files {
358			if !f(file) {
359				return
360			}
361		}
362	}
363}
364
365// NumFilesByPackage reports the number of registered files in a proto package.
366func (r *Files) NumFilesByPackage(name protoreflect.FullName) int {
367	if r == nil {
368		return 0
369	}
370	if r == GlobalFiles {
371		globalMutex.RLock()
372		defer globalMutex.RUnlock()
373	}
374	p, ok := r.descsByName[name].(*packageDescriptor)
375	if !ok {
376		return 0
377	}
378	return len(p.files)
379}
380
381// RangeFilesByPackage iterates over all registered files in a given proto package
382// while f returns true. The iteration order is undefined.
383func (r *Files) RangeFilesByPackage(name protoreflect.FullName, f func(protoreflect.FileDescriptor) bool) {
384	if r == nil {
385		return
386	}
387	if r == GlobalFiles {
388		globalMutex.RLock()
389		defer globalMutex.RUnlock()
390	}
391	p, ok := r.descsByName[name].(*packageDescriptor)
392	if !ok {
393		return
394	}
395	for _, file := range p.files {
396		if !f(file) {
397			return
398		}
399	}
400}
401
402// rangeTopLevelDescriptors iterates over all top-level descriptors in a file
403// which will be directly entered into the registry.
404func rangeTopLevelDescriptors(fd protoreflect.FileDescriptor, f func(protoreflect.Descriptor)) {
405	eds := fd.Enums()
406	for i := eds.Len() - 1; i >= 0; i-- {
407		f(eds.Get(i))
408		vds := eds.Get(i).Values()
409		for i := vds.Len() - 1; i >= 0; i-- {
410			f(vds.Get(i))
411		}
412	}
413	mds := fd.Messages()
414	for i := mds.Len() - 1; i >= 0; i-- {
415		f(mds.Get(i))
416	}
417	xds := fd.Extensions()
418	for i := xds.Len() - 1; i >= 0; i-- {
419		f(xds.Get(i))
420	}
421	sds := fd.Services()
422	for i := sds.Len() - 1; i >= 0; i-- {
423		f(sds.Get(i))
424	}
425}
426
427// MessageTypeResolver is an interface for looking up messages.
428//
429// A compliant implementation must deterministically return the same type
430// if no error is encountered.
431//
432// The Types type implements this interface.
433type MessageTypeResolver interface {
434	// FindMessageByName looks up a message by its full name.
435	// E.g., "google.protobuf.Any"
436	//
437	// This return (nil, NotFound) if not found.
438	FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error)
439
440	// FindMessageByURL looks up a message by a URL identifier.
441	// See documentation on google.protobuf.Any.type_url for the URL format.
442	//
443	// This returns (nil, NotFound) if not found.
444	FindMessageByURL(url string) (protoreflect.MessageType, error)
445}
446
447// ExtensionTypeResolver is an interface for looking up extensions.
448//
449// A compliant implementation must deterministically return the same type
450// if no error is encountered.
451//
452// The Types type implements this interface.
453type ExtensionTypeResolver interface {
454	// FindExtensionByName looks up a extension field by the field's full name.
455	// Note that this is the full name of the field as determined by
456	// where the extension is declared and is unrelated to the full name of the
457	// message being extended.
458	//
459	// This returns (nil, NotFound) if not found.
460	FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
461
462	// FindExtensionByNumber looks up a extension field by the field number
463	// within some parent message, identified by full name.
464	//
465	// This returns (nil, NotFound) if not found.
466	FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
467}
468
469var (
470	_ MessageTypeResolver   = (*Types)(nil)
471	_ ExtensionTypeResolver = (*Types)(nil)
472)
473
474// Types is a registry for looking up or iterating over descriptor types.
475// The Find and Range methods are safe for concurrent use.
476type Types struct {
477	typesByName         typesByName
478	extensionsByMessage extensionsByMessage
479
480	numEnums      int
481	numMessages   int
482	numExtensions int
483}
484
485type (
486	typesByName         map[protoreflect.FullName]interface{}
487	extensionsByMessage map[protoreflect.FullName]extensionsByNumber
488	extensionsByNumber  map[protoreflect.FieldNumber]protoreflect.ExtensionType
489)
490
491// RegisterMessage registers the provided message type.
492//
493// If a naming conflict occurs, the type is not registered and an error is returned.
494func (r *Types) RegisterMessage(mt protoreflect.MessageType) error {
495	// Under rare circumstances getting the descriptor might recursively
496	// examine the registry, so fetch it before locking.
497	md := mt.Descriptor()
498
499	if r == GlobalTypes {
500		globalMutex.Lock()
501		defer globalMutex.Unlock()
502	}
503
504	if err := r.register("message", md, mt); err != nil {
505		return err
506	}
507	r.numMessages++
508	return nil
509}
510
511// RegisterEnum registers the provided enum type.
512//
513// If a naming conflict occurs, the type is not registered and an error is returned.
514func (r *Types) RegisterEnum(et protoreflect.EnumType) error {
515	// Under rare circumstances getting the descriptor might recursively
516	// examine the registry, so fetch it before locking.
517	ed := et.Descriptor()
518
519	if r == GlobalTypes {
520		globalMutex.Lock()
521		defer globalMutex.Unlock()
522	}
523
524	if err := r.register("enum", ed, et); err != nil {
525		return err
526	}
527	r.numEnums++
528	return nil
529}
530
531// RegisterExtension registers the provided extension type.
532//
533// If a naming conflict occurs, the type is not registered and an error is returned.
534func (r *Types) RegisterExtension(xt protoreflect.ExtensionType) error {
535	// Under rare circumstances getting the descriptor might recursively
536	// examine the registry, so fetch it before locking.
537	//
538	// A known case where this can happen: Fetching the TypeDescriptor for a
539	// legacy ExtensionDesc can consult the global registry.
540	xd := xt.TypeDescriptor()
541
542	if r == GlobalTypes {
543		globalMutex.Lock()
544		defer globalMutex.Unlock()
545	}
546
547	field := xd.Number()
548	message := xd.ContainingMessage().FullName()
549	if prev := r.extensionsByMessage[message][field]; prev != nil {
550		err := errors.New("extension number %d is already registered on message %v", field, message)
551		err = amendErrorWithCaller(err, prev, xt)
552		if !(r == GlobalTypes && ignoreConflict(xd, err)) {
553			return err
554		}
555	}
556
557	if err := r.register("extension", xd, xt); err != nil {
558		return err
559	}
560	if r.extensionsByMessage == nil {
561		r.extensionsByMessage = make(extensionsByMessage)
562	}
563	if r.extensionsByMessage[message] == nil {
564		r.extensionsByMessage[message] = make(extensionsByNumber)
565	}
566	r.extensionsByMessage[message][field] = xt
567	r.numExtensions++
568	return nil
569}
570
571func (r *Types) register(kind string, desc protoreflect.Descriptor, typ interface{}) error {
572	name := desc.FullName()
573	prev := r.typesByName[name]
574	if prev != nil {
575		err := errors.New("%v %v is already registered", kind, name)
576		err = amendErrorWithCaller(err, prev, typ)
577		if !(r == GlobalTypes && ignoreConflict(desc, err)) {
578			return err
579		}
580	}
581	if r.typesByName == nil {
582		r.typesByName = make(typesByName)
583	}
584	r.typesByName[name] = typ
585	return nil
586}
587
588// FindEnumByName looks up an enum by its full name.
589// E.g., "google.protobuf.Field.Kind".
590//
591// This returns (nil, NotFound) if not found.
592func (r *Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumType, error) {
593	if r == nil {
594		return nil, NotFound
595	}
596	if r == GlobalTypes {
597		globalMutex.RLock()
598		defer globalMutex.RUnlock()
599	}
600	if v := r.typesByName[enum]; v != nil {
601		if et, _ := v.(protoreflect.EnumType); et != nil {
602			return et, nil
603		}
604		return nil, errors.New("found wrong type: got %v, want enum", typeName(v))
605	}
606	return nil, NotFound
607}
608
609// FindMessageByName looks up a message by its full name,
610// e.g. "google.protobuf.Any".
611//
612// This returns (nil, NotFound) if not found.
613func (r *Types) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) {
614	if r == nil {
615		return nil, NotFound
616	}
617	if r == GlobalTypes {
618		globalMutex.RLock()
619		defer globalMutex.RUnlock()
620	}
621	if v := r.typesByName[message]; v != nil {
622		if mt, _ := v.(protoreflect.MessageType); mt != nil {
623			return mt, nil
624		}
625		return nil, errors.New("found wrong type: got %v, want message", typeName(v))
626	}
627	return nil, NotFound
628}
629
630// FindMessageByURL looks up a message by a URL identifier.
631// See documentation on google.protobuf.Any.type_url for the URL format.
632//
633// This returns (nil, NotFound) if not found.
634func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error) {
635	// This function is similar to FindMessageByName but
636	// truncates anything before and including '/' in the URL.
637	if r == nil {
638		return nil, NotFound
639	}
640	if r == GlobalTypes {
641		globalMutex.RLock()
642		defer globalMutex.RUnlock()
643	}
644	message := protoreflect.FullName(url)
645	if i := strings.LastIndexByte(url, '/'); i >= 0 {
646		message = message[i+len("/"):]
647	}
648
649	if v := r.typesByName[message]; v != nil {
650		if mt, _ := v.(protoreflect.MessageType); mt != nil {
651			return mt, nil
652		}
653		return nil, errors.New("found wrong type: got %v, want message", typeName(v))
654	}
655	return nil, NotFound
656}
657
658// FindExtensionByName looks up a extension field by the field's full name.
659// Note that this is the full name of the field as determined by
660// where the extension is declared and is unrelated to the full name of the
661// message being extended.
662//
663// This returns (nil, NotFound) if not found.
664func (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
665	if r == nil {
666		return nil, NotFound
667	}
668	if r == GlobalTypes {
669		globalMutex.RLock()
670		defer globalMutex.RUnlock()
671	}
672	if v := r.typesByName[field]; v != nil {
673		if xt, _ := v.(protoreflect.ExtensionType); xt != nil {
674			return xt, nil
675		}
676
677		// MessageSet extensions are special in that the name of the extension
678		// is the name of the message type used to extend the MessageSet.
679		// This naming scheme is used by text and JSON serialization.
680		//
681		// This feature is protected by the ProtoLegacy flag since MessageSets
682		// are a proto1 feature that is long deprecated.
683		if flags.ProtoLegacy {
684			if _, ok := v.(protoreflect.MessageType); ok {
685				field := field.Append(messageset.ExtensionName)
686				if v := r.typesByName[field]; v != nil {
687					if xt, _ := v.(protoreflect.ExtensionType); xt != nil {
688						if messageset.IsMessageSetExtension(xt.TypeDescriptor()) {
689							return xt, nil
690						}
691					}
692				}
693			}
694		}
695
696		return nil, errors.New("found wrong type: got %v, want extension", typeName(v))
697	}
698	return nil, NotFound
699}
700
701// FindExtensionByNumber looks up a extension field by the field number
702// within some parent message, identified by full name.
703//
704// This returns (nil, NotFound) if not found.
705func (r *Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
706	if r == nil {
707		return nil, NotFound
708	}
709	if r == GlobalTypes {
710		globalMutex.RLock()
711		defer globalMutex.RUnlock()
712	}
713	if xt, ok := r.extensionsByMessage[message][field]; ok {
714		return xt, nil
715	}
716	return nil, NotFound
717}
718
719// NumEnums reports the number of registered enums.
720func (r *Types) NumEnums() int {
721	if r == nil {
722		return 0
723	}
724	if r == GlobalTypes {
725		globalMutex.RLock()
726		defer globalMutex.RUnlock()
727	}
728	return r.numEnums
729}
730
731// RangeEnums iterates over all registered enums while f returns true.
732// Iteration order is undefined.
733func (r *Types) RangeEnums(f func(protoreflect.EnumType) bool) {
734	if r == nil {
735		return
736	}
737	if r == GlobalTypes {
738		globalMutex.RLock()
739		defer globalMutex.RUnlock()
740	}
741	for _, typ := range r.typesByName {
742		if et, ok := typ.(protoreflect.EnumType); ok {
743			if !f(et) {
744				return
745			}
746		}
747	}
748}
749
750// NumMessages reports the number of registered messages.
751func (r *Types) NumMessages() int {
752	if r == nil {
753		return 0
754	}
755	if r == GlobalTypes {
756		globalMutex.RLock()
757		defer globalMutex.RUnlock()
758	}
759	return r.numMessages
760}
761
762// RangeMessages iterates over all registered messages while f returns true.
763// Iteration order is undefined.
764func (r *Types) RangeMessages(f func(protoreflect.MessageType) bool) {
765	if r == nil {
766		return
767	}
768	if r == GlobalTypes {
769		globalMutex.RLock()
770		defer globalMutex.RUnlock()
771	}
772	for _, typ := range r.typesByName {
773		if mt, ok := typ.(protoreflect.MessageType); ok {
774			if !f(mt) {
775				return
776			}
777		}
778	}
779}
780
781// NumExtensions reports the number of registered extensions.
782func (r *Types) NumExtensions() int {
783	if r == nil {
784		return 0
785	}
786	if r == GlobalTypes {
787		globalMutex.RLock()
788		defer globalMutex.RUnlock()
789	}
790	return r.numExtensions
791}
792
793// RangeExtensions iterates over all registered extensions while f returns true.
794// Iteration order is undefined.
795func (r *Types) RangeExtensions(f func(protoreflect.ExtensionType) bool) {
796	if r == nil {
797		return
798	}
799	if r == GlobalTypes {
800		globalMutex.RLock()
801		defer globalMutex.RUnlock()
802	}
803	for _, typ := range r.typesByName {
804		if xt, ok := typ.(protoreflect.ExtensionType); ok {
805			if !f(xt) {
806				return
807			}
808		}
809	}
810}
811
812// NumExtensionsByMessage reports the number of registered extensions for
813// a given message type.
814func (r *Types) NumExtensionsByMessage(message protoreflect.FullName) int {
815	if r == nil {
816		return 0
817	}
818	if r == GlobalTypes {
819		globalMutex.RLock()
820		defer globalMutex.RUnlock()
821	}
822	return len(r.extensionsByMessage[message])
823}
824
825// RangeExtensionsByMessage iterates over all registered extensions filtered
826// by a given message type while f returns true. Iteration order is undefined.
827func (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) {
828	if r == nil {
829		return
830	}
831	if r == GlobalTypes {
832		globalMutex.RLock()
833		defer globalMutex.RUnlock()
834	}
835	for _, xt := range r.extensionsByMessage[message] {
836		if !f(xt) {
837			return
838		}
839	}
840}
841
842func typeName(t interface{}) string {
843	switch t.(type) {
844	case protoreflect.EnumType:
845		return "enum"
846	case protoreflect.MessageType:
847		return "message"
848	case protoreflect.ExtensionType:
849		return "extension"
850	default:
851		return fmt.Sprintf("%T", t)
852	}
853}
854
855func amendErrorWithCaller(err error, prev, curr interface{}) error {
856	prevPkg := goPackage(prev)
857	currPkg := goPackage(curr)
858	if prevPkg == "" || currPkg == "" || prevPkg == currPkg {
859		return err
860	}
861	return errors.New("%s\n\tpreviously from: %q\n\tcurrently from:  %q", err, prevPkg, currPkg)
862}
863
864func goPackage(v interface{}) string {
865	switch d := v.(type) {
866	case protoreflect.EnumType:
867		v = d.Descriptor()
868	case protoreflect.MessageType:
869		v = d.Descriptor()
870	case protoreflect.ExtensionType:
871		v = d.TypeDescriptor()
872	}
873	if d, ok := v.(protoreflect.Descriptor); ok {
874		v = d.ParentFile()
875	}
876	if d, ok := v.(interface{ GoPackagePath() string }); ok {
877		return d.GoPackagePath()
878	}
879	return ""
880}
881