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