2018-08-15 17:07:18 -07:00
|
|
|
// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Package protoregistry provides data structures to register and lookup
|
|
|
|
// protobuf descriptor types.
|
2018-12-26 15:57:16 -08:00
|
|
|
//
|
|
|
|
// The Files registry contains file descriptors and provides the ability
|
|
|
|
// to iterate over the files or lookup a specific descriptor within the files.
|
|
|
|
// Files only contains protobuf descriptors and has no understanding of Go
|
|
|
|
// type information that may be associated with each descriptor.
|
|
|
|
//
|
|
|
|
// The Types registry contains descriptor types for which there is a known
|
|
|
|
// Go type associated with that descriptor. It provides the ability to iterate
|
|
|
|
// over the registered types or lookup a type by name.
|
2018-08-15 17:07:18 -07:00
|
|
|
package protoregistry
|
|
|
|
|
|
|
|
import (
|
2018-12-26 15:57:16 -08:00
|
|
|
"fmt"
|
2019-07-17 19:08:21 -07:00
|
|
|
"log"
|
2018-08-15 17:07:18 -07:00
|
|
|
"strings"
|
2019-10-04 14:58:46 -07:00
|
|
|
"sync"
|
2018-08-15 17:07:18 -07:00
|
|
|
|
2019-05-13 23:55:40 -07:00
|
|
|
"google.golang.org/protobuf/internal/errors"
|
|
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
2018-08-15 17:07:18 -07:00
|
|
|
)
|
|
|
|
|
2019-07-17 19:08:21 -07:00
|
|
|
// ignoreConflict reports whether to ignore a registration conflict
|
|
|
|
// given the descriptor being registered and the error.
|
|
|
|
// It is a variable so that the behavior is easily overridden in another file.
|
|
|
|
var ignoreConflict = func(d protoreflect.Descriptor, err error) bool {
|
|
|
|
log.Printf(""+
|
|
|
|
"WARNING: %v\n"+
|
2020-03-02 09:43:12 -08:00
|
|
|
"A future release will panic on registration conflicts. See:\n"+
|
|
|
|
"https://developers.google.com/protocol-buffers/docs/reference/go/faq#namespace-conflict\n"+
|
2019-07-17 19:08:21 -07:00
|
|
|
"\n", err)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2019-10-04 14:58:46 -07:00
|
|
|
var globalMutex sync.RWMutex
|
|
|
|
|
2018-08-15 17:07:18 -07:00
|
|
|
// GlobalFiles is a global registry of file descriptors.
|
2018-12-26 15:57:16 -08:00
|
|
|
var GlobalFiles *Files = new(Files)
|
|
|
|
|
|
|
|
// GlobalTypes is the registry used by default for type lookups
|
|
|
|
// unless a local registry is provided by the user.
|
|
|
|
var GlobalTypes *Types = new(Types)
|
2018-08-15 17:07:18 -07:00
|
|
|
|
|
|
|
// NotFound is a sentinel error value to indicate that the type was not found.
|
2020-01-17 13:40:51 -08:00
|
|
|
//
|
|
|
|
// Since registry lookup can happen in the critical performance path, resolvers
|
|
|
|
// must return this exact error value, not an error wrapping it.
|
2018-08-15 17:07:18 -07:00
|
|
|
var NotFound = errors.New("not found")
|
|
|
|
|
|
|
|
// Files is a registry for looking up or iterating over files and the
|
|
|
|
// descriptors contained within them.
|
|
|
|
// The Find and Range methods are safe for concurrent use.
|
|
|
|
type Files struct {
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
// The map of descsByName contains:
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
// EnumDescriptor
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
// EnumValueDescriptor
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
// MessageDescriptor
|
|
|
|
// ExtensionDescriptor
|
|
|
|
// ServiceDescriptor
|
|
|
|
// *packageDescriptor
|
|
|
|
//
|
|
|
|
// Note that files are stored as a slice, since a package may contain
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
// multiple files. Only top-level declarations are registered.
|
|
|
|
// Note that enum values are in the top-level since that are in the same
|
|
|
|
// scope as the parent enum.
|
|
|
|
descsByName map[protoreflect.FullName]interface{}
|
2019-06-15 05:39:04 -07:00
|
|
|
filesByPath map[string]protoreflect.FileDescriptor
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
}
|
2018-08-15 17:07:18 -07:00
|
|
|
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
type packageDescriptor struct {
|
|
|
|
files []protoreflect.FileDescriptor
|
|
|
|
}
|
2018-08-15 17:07:18 -07:00
|
|
|
|
2019-10-08 13:28:53 -07:00
|
|
|
// RegisterFile registers the provided file descriptor.
|
|
|
|
//
|
|
|
|
// If any descriptor within the file conflicts with the descriptor of any
|
2018-08-15 17:07:18 -07:00
|
|
|
// previously registered file (e.g., two enums with the same full name),
|
2019-10-08 13:28:53 -07:00
|
|
|
// then the file is not registered and an error is returned.
|
2018-08-15 17:07:18 -07:00
|
|
|
//
|
|
|
|
// It is permitted for multiple files to have the same file path.
|
2019-10-08 13:28:53 -07:00
|
|
|
func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error {
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalFiles {
|
|
|
|
globalMutex.Lock()
|
|
|
|
defer globalMutex.Unlock()
|
|
|
|
}
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
if r.descsByName == nil {
|
|
|
|
r.descsByName = map[protoreflect.FullName]interface{}{
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
"": &packageDescriptor{},
|
|
|
|
}
|
2019-06-15 05:39:04 -07:00
|
|
|
r.filesByPath = make(map[string]protoreflect.FileDescriptor)
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
}
|
2019-10-08 13:28:53 -07:00
|
|
|
path := file.Path()
|
2019-07-14 23:04:40 -07:00
|
|
|
if prev := r.filesByPath[path]; prev != nil {
|
2019-10-08 13:28:53 -07:00
|
|
|
err := errors.New("file %q is already registered", file.Path())
|
|
|
|
err = amendErrorWithCaller(err, prev, file)
|
|
|
|
if r == GlobalFiles && ignoreConflict(file, err) {
|
2019-07-17 19:08:21 -07:00
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
return err
|
2019-06-15 05:39:04 -07:00
|
|
|
}
|
|
|
|
|
2019-10-08 13:28:53 -07:00
|
|
|
for name := file.Package(); name != ""; name = name.Parent() {
|
2019-07-14 23:04:40 -07:00
|
|
|
switch prev := r.descsByName[name]; prev.(type) {
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
case nil, *packageDescriptor:
|
|
|
|
default:
|
2019-10-08 13:28:53 -07:00
|
|
|
err := errors.New("file %q has a package name conflict over %v", file.Path(), name)
|
|
|
|
err = amendErrorWithCaller(err, prev, file)
|
|
|
|
if r == GlobalFiles && ignoreConflict(file, err) {
|
2019-07-17 19:08:21 -07:00
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
return err
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
}
|
|
|
|
var err error
|
2019-07-17 19:08:21 -07:00
|
|
|
var hasConflict bool
|
2019-10-08 13:28:53 -07:00
|
|
|
rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) {
|
2019-07-14 23:04:40 -07:00
|
|
|
if prev := r.descsByName[d.FullName()]; prev != nil {
|
2019-07-17 19:08:21 -07:00
|
|
|
hasConflict = true
|
2019-10-08 13:28:53 -07:00
|
|
|
err = errors.New("file %q has a name conflict over %v", file.Path(), d.FullName())
|
|
|
|
err = amendErrorWithCaller(err, prev, file)
|
2019-07-17 19:08:21 -07:00
|
|
|
if r == GlobalFiles && ignoreConflict(d, err) {
|
|
|
|
err = nil
|
|
|
|
}
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
})
|
2019-07-17 19:08:21 -07:00
|
|
|
if hasConflict {
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-10-08 13:28:53 -07:00
|
|
|
for name := file.Package(); name != ""; name = name.Parent() {
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
if r.descsByName[name] == nil {
|
|
|
|
r.descsByName[name] = &packageDescriptor{}
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
|
|
|
}
|
2019-10-08 13:28:53 -07:00
|
|
|
p := r.descsByName[file.Package()].(*packageDescriptor)
|
|
|
|
p.files = append(p.files, file)
|
|
|
|
rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) {
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
r.descsByName[d.FullName()] = d
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
})
|
2019-10-08 13:28:53 -07:00
|
|
|
r.filesByPath[path] = file
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
return nil
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
|
|
|
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
// FindDescriptorByName looks up a descriptor by the full name.
|
2018-08-15 17:07:18 -07:00
|
|
|
//
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
// This returns (nil, NotFound) if not found.
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
func (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) {
|
2018-08-26 22:48:17 -07:00
|
|
|
if r == nil {
|
|
|
|
return nil, NotFound
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalFiles {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
prefix := name
|
|
|
|
suffix := nameSuffix("")
|
|
|
|
for prefix != "" {
|
|
|
|
if d, ok := r.descsByName[prefix]; ok {
|
|
|
|
switch d := d.(type) {
|
|
|
|
case protoreflect.EnumDescriptor:
|
|
|
|
if d.FullName() == name {
|
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
case protoreflect.EnumValueDescriptor:
|
|
|
|
if d.FullName() == name {
|
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
case protoreflect.MessageDescriptor:
|
|
|
|
if d.FullName() == name {
|
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
if d := findDescriptorInMessage(d, suffix); d != nil && d.FullName() == name {
|
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
case protoreflect.ExtensionDescriptor:
|
|
|
|
if d.FullName() == name {
|
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
case protoreflect.ServiceDescriptor:
|
|
|
|
if d.FullName() == name {
|
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
if d := d.Methods().ByName(suffix.Pop()); d != nil && d.FullName() == name {
|
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, NotFound
|
|
|
|
}
|
|
|
|
prefix = prefix.Parent()
|
|
|
|
suffix = nameSuffix(name[len(prefix)+len("."):])
|
|
|
|
}
|
|
|
|
return nil, NotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
func findDescriptorInMessage(md protoreflect.MessageDescriptor, suffix nameSuffix) protoreflect.Descriptor {
|
|
|
|
name := suffix.Pop()
|
|
|
|
if suffix == "" {
|
|
|
|
if ed := md.Enums().ByName(name); ed != nil {
|
|
|
|
return ed
|
|
|
|
}
|
|
|
|
for i := md.Enums().Len() - 1; i >= 0; i-- {
|
|
|
|
if vd := md.Enums().Get(i).Values().ByName(name); vd != nil {
|
|
|
|
return vd
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if xd := md.Extensions().ByName(name); xd != nil {
|
|
|
|
return xd
|
|
|
|
}
|
|
|
|
if fd := md.Fields().ByName(name); fd != nil {
|
|
|
|
return fd
|
|
|
|
}
|
|
|
|
if od := md.Oneofs().ByName(name); od != nil {
|
|
|
|
return od
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if md := md.Messages().ByName(name); md != nil {
|
|
|
|
if suffix == "" {
|
|
|
|
return md
|
|
|
|
}
|
|
|
|
return findDescriptorInMessage(md, suffix)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type nameSuffix string
|
|
|
|
|
|
|
|
func (s *nameSuffix) Pop() (name protoreflect.Name) {
|
|
|
|
if i := strings.IndexByte(string(*s), '.'); i >= 0 {
|
|
|
|
name, *s = protoreflect.Name((*s)[:i]), (*s)[i+1:]
|
|
|
|
} else {
|
|
|
|
name, *s = protoreflect.Name((*s)), ""
|
|
|
|
}
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
2019-06-15 05:39:04 -07:00
|
|
|
// FindFileByPath looks up a file by the path.
|
|
|
|
//
|
|
|
|
// This returns (nil, NotFound) if not found.
|
|
|
|
func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) {
|
|
|
|
if r == nil {
|
|
|
|
return nil, NotFound
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalFiles {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-06-15 05:39:04 -07:00
|
|
|
if fd, ok := r.filesByPath[path]; ok {
|
|
|
|
return fd, nil
|
|
|
|
}
|
|
|
|
return nil, NotFound
|
|
|
|
}
|
|
|
|
|
2019-09-05 10:19:36 -07:00
|
|
|
// NumFiles reports the number of registered files.
|
|
|
|
func (r *Files) NumFiles() int {
|
|
|
|
if r == nil {
|
|
|
|
return 0
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalFiles {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-09-05 10:19:36 -07:00
|
|
|
return len(r.filesByPath)
|
|
|
|
}
|
|
|
|
|
2020-02-14 11:47:10 -08:00
|
|
|
// RangeFiles iterates over all registered files while f returns true.
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
// The iteration order is undefined.
|
|
|
|
func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) {
|
|
|
|
if r == nil {
|
|
|
|
return
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalFiles {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-09-05 10:19:36 -07:00
|
|
|
for _, file := range r.filesByPath {
|
|
|
|
if !f(file) {
|
|
|
|
return
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 10:19:36 -07:00
|
|
|
// NumFilesByPackage reports the number of registered files in a proto package.
|
|
|
|
func (r *Files) NumFilesByPackage(name protoreflect.FullName) int {
|
|
|
|
if r == nil {
|
|
|
|
return 0
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalFiles {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-09-05 10:19:36 -07:00
|
|
|
p, ok := r.descsByName[name].(*packageDescriptor)
|
|
|
|
if !ok {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return len(p.files)
|
|
|
|
}
|
|
|
|
|
2020-02-14 11:47:10 -08:00
|
|
|
// RangeFilesByPackage iterates over all registered files in a given proto package
|
|
|
|
// while f returns true. The iteration order is undefined.
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
func (r *Files) RangeFilesByPackage(name protoreflect.FullName, f func(protoreflect.FileDescriptor) bool) {
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalFiles {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
p, ok := r.descsByName[name].(*packageDescriptor)
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, file := range p.files {
|
|
|
|
if !f(file) {
|
|
|
|
return
|
|
|
|
}
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
// rangeTopLevelDescriptors iterates over all top-level descriptors in a file
|
|
|
|
// which will be directly entered into the registry.
|
|
|
|
func rangeTopLevelDescriptors(fd protoreflect.FileDescriptor, f func(protoreflect.Descriptor)) {
|
|
|
|
eds := fd.Enums()
|
|
|
|
for i := eds.Len() - 1; i >= 0; i-- {
|
|
|
|
f(eds.Get(i))
|
|
|
|
vds := eds.Get(i).Values()
|
|
|
|
for i := vds.Len() - 1; i >= 0; i-- {
|
|
|
|
f(vds.Get(i))
|
|
|
|
}
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
mds := fd.Messages()
|
|
|
|
for i := mds.Len() - 1; i >= 0; i-- {
|
|
|
|
f(mds.Get(i))
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
xds := fd.Extensions()
|
|
|
|
for i := xds.Len() - 1; i >= 0; i-- {
|
|
|
|
f(xds.Get(i))
|
reflect/protoreflect, reflect/protoregistry: move descriptor lookup to registry
Drop the protoreflect.FileDescriptor.DescriptorByName method.
Descriptor lookup will always happen through a protoregistry.Files, which
is more generally useful (it's rare that you want to find a descriptor in a
specific file, as opposed to a package which may be composed of multiple files).
Split protoregistry.Files descriptor lookup into individual per-type functions
(enum, message, extension, service), matching the preg.Types API.
Drop the ability to look up enum values, message fields, and service methods
for now. This can be easily added later if needed, and is trivial to implement
in user code. (e.g., look up the service and then consult sd.Methods.ByName().)
Change-Id: I2b3d8ef888921a8464ba1434eddab20c7d3a458e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/172118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-04-15 13:05:13 -07:00
|
|
|
}
|
reflect/protoreflect: register all desciptors in Files
This change makes it such that Files now functionally registers all
descriptors in a file (not just enums, messages, extensions, and services),
but also including enum values, messages fields/oneofs, and service methods.
The ability to look up any descriptor by full name is needed to:
1) properly detect namespace conflicts on enum values
2) properly implement the relative name lookup logic in reflect/protodesc
The approach taken:
1) Assumes that a FileDescriptor has no internal name conflicts.
This will (in a future CL) be guaranteed by reflect/protodesc and
is guaranteed today by protoc for generated descriptors.
2) Observes that the only declarations that can possibly conflict
with another file are top-level declarations (i.e., enums, enum values,
messages, extensions, and services). Enum values are annoying
since they live in the same scope as the parent enum, rather than
being under the enum.
For the internal data structure of Files, we only register the top-level
declarations. This is the bare minimum needed to detect whether the file
being registered has any namespace conflicts with previously registered files.
We shift the effort to lookups, where we now need to peel off the end fragments
of a full name until we find a match in the internal registry. If a match
is found, we may need to descend into that declaration to find a nested
declaration by name.
For initialization, we modify internal/filedesc to initialize the
enum values for all top-level enums. This performance cost is offsetted
by the fact that Files.Register now avoids internally registering
nested enums, messages, and extensions.
For lookup, the cost has shifted from O(1) to O(N),
where N is the number of segments in the full name.
Top-level descriptors still have O(1) lookup times.
Nested descriptors have O(M) lookup times,
where M is the level of nesting within a single file.
Change-Id: I950163423431f04a503b6201ddcc20a62ccba017
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183697
Reviewed-by: Damien Neil <dneil@google.com>
2019-06-21 14:25:16 -07:00
|
|
|
sds := fd.Services()
|
|
|
|
for i := sds.Len() - 1; i >= 0; i-- {
|
|
|
|
f(sds.Get(i))
|
2018-08-15 17:07:18 -07:00
|
|
|
}
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
|
2019-05-14 14:28:19 -07:00
|
|
|
// MessageTypeResolver is an interface for looking up messages.
|
|
|
|
//
|
|
|
|
// A compliant implementation must deterministically return the same type
|
|
|
|
// if no error is encountered.
|
|
|
|
//
|
|
|
|
// The Types type implements this interface.
|
|
|
|
type MessageTypeResolver interface {
|
|
|
|
// FindMessageByName looks up a message by its full name.
|
|
|
|
// E.g., "google.protobuf.Any"
|
2018-12-26 15:57:16 -08:00
|
|
|
//
|
2019-05-14 14:28:19 -07:00
|
|
|
// This return (nil, NotFound) if not found.
|
|
|
|
FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error)
|
|
|
|
|
|
|
|
// FindMessageByURL looks up a message by a URL identifier.
|
|
|
|
// See documentation on google.protobuf.Any.type_url for the URL format.
|
2018-12-26 15:57:16 -08:00
|
|
|
//
|
2019-05-14 14:28:19 -07:00
|
|
|
// This returns (nil, NotFound) if not found.
|
|
|
|
FindMessageByURL(url string) (protoreflect.MessageType, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExtensionTypeResolver is an interface for looking up extensions.
|
|
|
|
//
|
|
|
|
// A compliant implementation must deterministically return the same type
|
|
|
|
// if no error is encountered.
|
|
|
|
//
|
|
|
|
// The Types type implements this interface.
|
|
|
|
type ExtensionTypeResolver interface {
|
|
|
|
// FindExtensionByName looks up a extension field by the field's full name.
|
|
|
|
// Note that this is the full name of the field as determined by
|
|
|
|
// where the extension is declared and is unrelated to the full name of the
|
|
|
|
// message being extended.
|
2018-12-26 15:57:16 -08:00
|
|
|
//
|
2019-05-14 14:28:19 -07:00
|
|
|
// This returns (nil, NotFound) if not found.
|
|
|
|
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
|
|
|
|
|
|
|
|
// FindExtensionByNumber looks up a extension field by the field number
|
|
|
|
// within some parent message, identified by full name.
|
2018-12-26 15:57:16 -08:00
|
|
|
//
|
2019-05-14 14:28:19 -07:00
|
|
|
// This returns (nil, NotFound) if not found.
|
|
|
|
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
_ MessageTypeResolver = (*Types)(nil)
|
|
|
|
_ ExtensionTypeResolver = (*Types)(nil)
|
|
|
|
)
|
2018-12-26 15:57:16 -08:00
|
|
|
|
2019-05-14 14:28:19 -07:00
|
|
|
// Types is a registry for looking up or iterating over descriptor types.
|
|
|
|
// The Find and Range methods are safe for concurrent use.
|
|
|
|
type Types struct {
|
2018-12-26 15:57:16 -08:00
|
|
|
typesByName typesByName
|
|
|
|
extensionsByMessage extensionsByMessage
|
2019-09-05 10:19:36 -07:00
|
|
|
|
|
|
|
numEnums int
|
|
|
|
numMessages int
|
|
|
|
numExtensions int
|
2018-12-26 15:57:16 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
type (
|
2019-10-08 13:28:53 -07:00
|
|
|
typesByName map[protoreflect.FullName]interface{}
|
2018-12-26 15:57:16 -08:00
|
|
|
extensionsByMessage map[protoreflect.FullName]extensionsByNumber
|
|
|
|
extensionsByNumber map[protoreflect.FieldNumber]protoreflect.ExtensionType
|
|
|
|
)
|
|
|
|
|
2019-10-08 13:28:53 -07:00
|
|
|
// RegisterMessage registers the provided message type.
|
|
|
|
//
|
|
|
|
// If a naming conflict occurs, the type is not registered and an error is returned.
|
|
|
|
func (r *Types) RegisterMessage(mt protoreflect.MessageType) error {
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.Lock()
|
|
|
|
defer globalMutex.Unlock()
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
|
2019-10-08 13:28:53 -07:00
|
|
|
if err := r.register("message", mt.Descriptor(), mt); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.numMessages++
|
|
|
|
return nil
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
|
2019-10-08 13:28:53 -07:00
|
|
|
// RegisterEnum registers the provided enum type.
|
|
|
|
//
|
|
|
|
// If a naming conflict occurs, the type is not registered and an error is returned.
|
|
|
|
func (r *Types) RegisterEnum(et protoreflect.EnumType) error {
|
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.Lock()
|
|
|
|
defer globalMutex.Unlock()
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
|
2019-10-08 13:28:53 -07:00
|
|
|
if err := r.register("enum", et.Descriptor(), et); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.numEnums++
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterExtension registers the provided extension type.
|
|
|
|
//
|
|
|
|
// If a naming conflict occurs, the type is not registered and an error is returned.
|
|
|
|
func (r *Types) RegisterExtension(xt protoreflect.ExtensionType) error {
|
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.Lock()
|
|
|
|
defer globalMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
xd := xt.TypeDescriptor()
|
|
|
|
field := xd.Number()
|
|
|
|
message := xd.ContainingMessage().FullName()
|
|
|
|
if prev := r.extensionsByMessage[message][field]; prev != nil {
|
|
|
|
err := errors.New("extension number %d is already registered on message %v", field, message)
|
|
|
|
err = amendErrorWithCaller(err, prev, xt)
|
|
|
|
if !(r == GlobalTypes && ignoreConflict(xd, err)) {
|
|
|
|
return err
|
2018-12-26 15:57:16 -08:00
|
|
|
}
|
|
|
|
}
|
2019-10-08 13:28:53 -07:00
|
|
|
|
|
|
|
if err := r.register("extension", xt.TypeDescriptor(), xt); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if r.extensionsByMessage == nil {
|
|
|
|
r.extensionsByMessage = make(extensionsByMessage)
|
|
|
|
}
|
|
|
|
if r.extensionsByMessage[message] == nil {
|
|
|
|
r.extensionsByMessage[message] = make(extensionsByNumber)
|
|
|
|
}
|
|
|
|
r.extensionsByMessage[message][field] = xt
|
|
|
|
r.numExtensions++
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Types) register(kind string, desc protoreflect.Descriptor, typ interface{}) error {
|
|
|
|
name := desc.FullName()
|
|
|
|
prev := r.typesByName[name]
|
|
|
|
if prev != nil {
|
|
|
|
err := errors.New("%v %v is already registered", kind, name)
|
|
|
|
err = amendErrorWithCaller(err, prev, typ)
|
|
|
|
if !(r == GlobalTypes && ignoreConflict(desc, err)) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if r.typesByName == nil {
|
|
|
|
r.typesByName = make(typesByName)
|
|
|
|
}
|
|
|
|
r.typesByName[name] = typ
|
|
|
|
return nil
|
2018-12-26 15:57:16 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindEnumByName looks up an enum by its full name.
|
|
|
|
// E.g., "google.protobuf.Field.Kind".
|
|
|
|
//
|
|
|
|
// This returns (nil, NotFound) if not found.
|
|
|
|
func (r *Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumType, error) {
|
|
|
|
if r == nil {
|
|
|
|
return nil, NotFound
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-08-05 16:58:23 -07:00
|
|
|
if v := r.typesByName[enum]; v != nil {
|
2018-12-26 15:57:16 -08:00
|
|
|
if et, _ := v.(protoreflect.EnumType); et != nil {
|
|
|
|
return et, nil
|
|
|
|
}
|
|
|
|
return nil, errors.New("found wrong type: got %v, want enum", typeName(v))
|
|
|
|
}
|
2019-05-14 14:28:19 -07:00
|
|
|
return nil, NotFound
|
2018-12-26 15:57:16 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindMessageByName looks up a message by its full name.
|
|
|
|
// E.g., "google.protobuf.Any"
|
|
|
|
//
|
|
|
|
// This return (nil, NotFound) if not found.
|
|
|
|
func (r *Types) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) {
|
|
|
|
// The full name by itself is a valid URL.
|
|
|
|
return r.FindMessageByURL(string(message))
|
|
|
|
}
|
|
|
|
|
|
|
|
// FindMessageByURL looks up a message by a URL identifier.
|
2019-05-14 14:28:19 -07:00
|
|
|
// See documentation on google.protobuf.Any.type_url for the URL format.
|
2018-12-26 15:57:16 -08:00
|
|
|
//
|
|
|
|
// This returns (nil, NotFound) if not found.
|
|
|
|
func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error) {
|
|
|
|
if r == nil {
|
|
|
|
return nil, NotFound
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
message := protoreflect.FullName(url)
|
|
|
|
if i := strings.LastIndexByte(url, '/'); i >= 0 {
|
|
|
|
message = message[i+len("/"):]
|
|
|
|
}
|
|
|
|
|
2019-08-05 16:58:23 -07:00
|
|
|
if v := r.typesByName[message]; v != nil {
|
2018-12-26 15:57:16 -08:00
|
|
|
if mt, _ := v.(protoreflect.MessageType); mt != nil {
|
|
|
|
return mt, nil
|
|
|
|
}
|
|
|
|
return nil, errors.New("found wrong type: got %v, want message", typeName(v))
|
|
|
|
}
|
2019-05-14 14:28:19 -07:00
|
|
|
return nil, NotFound
|
2018-12-26 15:57:16 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindExtensionByName looks up a extension field by the field's full name.
|
|
|
|
// Note that this is the full name of the field as determined by
|
|
|
|
// where the extension is declared and is unrelated to the full name of the
|
|
|
|
// message being extended.
|
|
|
|
//
|
|
|
|
// This returns (nil, NotFound) if not found.
|
|
|
|
func (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
|
|
|
|
if r == nil {
|
|
|
|
return nil, NotFound
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-08-05 16:58:23 -07:00
|
|
|
if v := r.typesByName[field]; v != nil {
|
2018-12-26 15:57:16 -08:00
|
|
|
if xt, _ := v.(protoreflect.ExtensionType); xt != nil {
|
|
|
|
return xt, nil
|
|
|
|
}
|
|
|
|
return nil, errors.New("found wrong type: got %v, want extension", typeName(v))
|
|
|
|
}
|
2019-05-14 14:28:19 -07:00
|
|
|
return nil, NotFound
|
2018-12-26 15:57:16 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindExtensionByNumber looks up a extension field by the field number
|
|
|
|
// within some parent message, identified by full name.
|
|
|
|
//
|
|
|
|
// This returns (nil, NotFound) if not found.
|
|
|
|
func (r *Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
|
|
|
|
if r == nil {
|
|
|
|
return nil, NotFound
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
if xt, ok := r.extensionsByMessage[message][field]; ok {
|
|
|
|
return xt, nil
|
|
|
|
}
|
2019-05-14 14:28:19 -07:00
|
|
|
return nil, NotFound
|
2018-12-26 15:57:16 -08:00
|
|
|
}
|
|
|
|
|
2019-09-05 10:19:36 -07:00
|
|
|
// NumEnums reports the number of registered enums.
|
|
|
|
func (r *Types) NumEnums() int {
|
|
|
|
if r == nil {
|
|
|
|
return 0
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-09-05 10:19:36 -07:00
|
|
|
return r.numEnums
|
|
|
|
}
|
|
|
|
|
2020-02-14 11:47:10 -08:00
|
|
|
// RangeEnums iterates over all registered enums while f returns true.
|
2018-12-26 15:57:16 -08:00
|
|
|
// Iteration order is undefined.
|
|
|
|
func (r *Types) RangeEnums(f func(protoreflect.EnumType) bool) {
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
for _, typ := range r.typesByName {
|
|
|
|
if et, ok := typ.(protoreflect.EnumType); ok {
|
|
|
|
if !f(et) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 10:19:36 -07:00
|
|
|
// NumMessages reports the number of registered messages.
|
|
|
|
func (r *Types) NumMessages() int {
|
|
|
|
if r == nil {
|
|
|
|
return 0
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-09-05 10:19:36 -07:00
|
|
|
return r.numMessages
|
|
|
|
}
|
|
|
|
|
2020-02-14 11:47:10 -08:00
|
|
|
// RangeMessages iterates over all registered messages while f returns true.
|
2018-12-26 15:57:16 -08:00
|
|
|
// Iteration order is undefined.
|
|
|
|
func (r *Types) RangeMessages(f func(protoreflect.MessageType) bool) {
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
for _, typ := range r.typesByName {
|
|
|
|
if mt, ok := typ.(protoreflect.MessageType); ok {
|
|
|
|
if !f(mt) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 10:19:36 -07:00
|
|
|
// NumExtensions reports the number of registered extensions.
|
|
|
|
func (r *Types) NumExtensions() int {
|
|
|
|
if r == nil {
|
|
|
|
return 0
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-09-05 10:19:36 -07:00
|
|
|
return r.numExtensions
|
|
|
|
}
|
|
|
|
|
2020-02-14 11:47:10 -08:00
|
|
|
// RangeExtensions iterates over all registered extensions while f returns true.
|
2018-12-26 15:57:16 -08:00
|
|
|
// Iteration order is undefined.
|
|
|
|
func (r *Types) RangeExtensions(f func(protoreflect.ExtensionType) bool) {
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
for _, typ := range r.typesByName {
|
|
|
|
if xt, ok := typ.(protoreflect.ExtensionType); ok {
|
|
|
|
if !f(xt) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 10:19:36 -07:00
|
|
|
// NumExtensionsByMessage reports the number of registered extensions for
|
|
|
|
// a given message type.
|
|
|
|
func (r *Types) NumExtensionsByMessage(message protoreflect.FullName) int {
|
|
|
|
if r == nil {
|
|
|
|
return 0
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2019-09-05 10:19:36 -07:00
|
|
|
return len(r.extensionsByMessage[message])
|
|
|
|
}
|
|
|
|
|
2018-12-26 15:57:16 -08:00
|
|
|
// RangeExtensionsByMessage iterates over all registered extensions filtered
|
2020-02-14 11:47:10 -08:00
|
|
|
// by a given message type while f returns true. Iteration order is undefined.
|
2018-12-26 15:57:16 -08:00
|
|
|
func (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) {
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
2019-10-04 14:58:46 -07:00
|
|
|
if r == GlobalTypes {
|
|
|
|
globalMutex.RLock()
|
|
|
|
defer globalMutex.RUnlock()
|
|
|
|
}
|
2018-12-26 15:57:16 -08:00
|
|
|
for _, xt := range r.extensionsByMessage[message] {
|
|
|
|
if !f(xt) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-07 16:25:57 -08:00
|
|
|
func typeName(t interface{}) string {
|
2018-12-26 15:57:16 -08:00
|
|
|
switch t.(type) {
|
|
|
|
case protoreflect.EnumType:
|
|
|
|
return "enum"
|
|
|
|
case protoreflect.MessageType:
|
|
|
|
return "message"
|
|
|
|
case protoreflect.ExtensionType:
|
|
|
|
return "extension"
|
|
|
|
default:
|
|
|
|
return fmt.Sprintf("%T", t)
|
|
|
|
}
|
|
|
|
}
|
2019-07-14 23:04:40 -07:00
|
|
|
|
|
|
|
func amendErrorWithCaller(err error, prev, curr interface{}) error {
|
|
|
|
prevPkg := goPackage(prev)
|
|
|
|
currPkg := goPackage(curr)
|
|
|
|
if prevPkg == "" || currPkg == "" || prevPkg == currPkg {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-15 00:04:27 -07:00
|
|
|
return errors.New("%s\n\tpreviously from: %q\n\tcurrently from: %q", err, prevPkg, currPkg)
|
2019-07-14 23:04:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func goPackage(v interface{}) string {
|
|
|
|
switch d := v.(type) {
|
|
|
|
case protoreflect.EnumType:
|
|
|
|
v = d.Descriptor()
|
|
|
|
case protoreflect.MessageType:
|
|
|
|
v = d.Descriptor()
|
|
|
|
case protoreflect.ExtensionType:
|
2019-08-28 11:08:22 -07:00
|
|
|
v = d.TypeDescriptor()
|
2019-07-14 23:04:40 -07:00
|
|
|
}
|
|
|
|
if d, ok := v.(protoreflect.Descriptor); ok {
|
|
|
|
v = d.ParentFile()
|
|
|
|
}
|
|
|
|
if d, ok := v.(interface{ GoPackagePath() string }); ok {
|
|
|
|
return d.GoPackagePath()
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|