[protobuf] reflect/protoregistry: add Types registry

449 views
Skip to first unread message

Joe Tsai (Gerrit)

unread,
Aug 24, 2018, 9:15:09 PM8/24/18
to Ian Lance Taylor, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Joe Tsai has uploaded this change for review.

View Change

reflect/protoregistry: add Types registry

The first commit of protoregistry only added a registry for files.
However, a separate type of registry is needed to provide a mapping between
protobuf names and actual Go types representing those names.

Additional high-level API:
var GlobalTypes = new(Types)
type Type interface{ ... }
type Types struct{ ... }
func NewTypes(...Type) *Types
func (*Types) FindEnumByName(pref.FullName) (pref.EnumType, error)
func (*Types) FindExtensionByName(pref.FullName) (pref.ExtensionType, error)
func (*Types) FindExtensionByNumber(pref.FullName, pref.FieldNumber) (pref.ExtensionType, error)
func (*Types) FindMessageByName(pref.FullName) (pref.MessageType, error)
func (*Types) FindMessageByURL(string) (pref.MessageType, error)
func (*Types) RangeEnums(func(pref.EnumType) bool)
func (*Types) RangeExtensions(func(pref.ExtensionType) bool)
func (*Types) RangeExtensionsByMessage(pref.FullName, func(pref.ExtensionType) bool)
func (*Types) RangeMessages(func(pref.MessageType) bool)
func (*Types) Register(...Type) error

Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
---
M reflect/protoregistry/registry.go
1 file changed, 314 insertions(+), 25 deletions(-)

diff --git a/reflect/protoregistry/registry.go b/reflect/protoregistry/registry.go
index 5e21043..9a19985 100644
--- a/reflect/protoregistry/registry.go
+++ b/reflect/protoregistry/registry.go
@@ -4,9 +4,20 @@

// Package protoregistry provides data structures to register and lookup
// protobuf descriptor types.
+//
+// The Files registry only 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 only 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.
package protoregistry

import (
+ "fmt"
+ "reflect"
"sort"
"strings"

@@ -19,34 +30,13 @@
// registration issues. This presumes that we provide a way to disable automatic
// registration in generated code.

-// TODO: Add a type registry:
-/*
-var GlobalTypes = new(Types)
-
-type Type interface {
- protoreflect.Descriptor
- GoType() reflect.Type
-}
-type Types struct {
- Parent *Types
- Resolver func(url string) (Type, error)
-}
-func NewTypes(typs ...Type) *Types
-func (*Types) Register(typs ...Type) error
-func (*Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumType, error)
-func (*Types) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error)
-func (*Types) FindMessageByURL(url string) (protoreflect.MessageType, error)
-func (*Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
-func (*Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
-func (*Types) RangeEnums(f func(protoreflect.EnumType) bool)
-func (*Types) RangeMessages(f func(protoreflect.MessageType) bool)
-func (*Types) RangeExtensions(f func(protoreflect.ExtensionType) bool)
-func (*Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool)
-*/
-
// GlobalFiles is a global registry of file descriptors.
var GlobalFiles = new(Files)

+// GlobalTypes is the registry used by default for type lookups
+// unless a local registry is provided by the user.
+var GlobalTypes = new(Types)
+
// NotFound is a sentinel error value to indicate that the type was not found.
var NotFound = errors.New("not found")

@@ -289,3 +279,302 @@
f(fd.Services().Get(i).Name())
}
}
+
+// Type is an interface satisfied by the following: protoreflect.MessageType,
+// protoreflect.EnumType, and protoreflect.ExtensionType.
+type Type interface {
+ protoreflect.Descriptor
+ GoType() reflect.Type
+}
+
+// Types is a registry for looking up or iterating over descriptor types.
+// The Lookup and Range methods are safe for concurrent use.
+type Types struct {
+ // Parent sets the parent registry to consult if a find operation
+ // could not locate the appropriate entry.
+ //
+ // Setting a parent results in each Range operation also iterating over the
+ // entries contained within the parent. In such a case, it is possible for
+ // Range to emit duplicates (since they may exist in both child and parent).
+ // Range iteration is guaranteed to to iterate over local entries before
+ // iterating over parent entries.
+ Parent *Types
+
+ // Resolver sets the local resolver to consult if the local registry does
+ // not contain an entry. The resolver takes precedence over the parent.
+ //
+ // The url is a URL where the full name of the type is the last segment
+ // of the path (i.e. string following the last '/' character).
+ // When missing a '/' character, the URL is the full name of the type.
+ // See documentation on the google.protobuf.Any.type_url field for details.
+ //
+ // If the resolver returns a result, it is not automatically registered
+ // into the local registry. Thus, a resolver function should cache results
+ // such that it deterministically returns the same result given the
+ // same URL assuming the error returned is nil or NotFound.
+ //
+ // Setting a resolver has no effect on the result of each Range operation.
+ Resolver func(url string) (Type, error)
+
+ // TODO: Should we prevent setting Parent and Resolver for GlobalTypes?
+ // TODO: Should we support registering enum types?
+ // TODO: Should we separate this out as a registry for each type?
+ //
+ // In Java, the extension and message registry are distinct classes.
+ // Their extension registry has knowledge of distinct Java types,
+ // while their message registry only contains descriptor information.
+ //
+ // In Go, we have always registered messages, enums, and extensions.
+ // Messages and extensions are registered with Go information, while enums
+ // are only registered with descriptor information. We cannot drop Go type
+ // information for messages otherwise we would be unable to implement
+ // portions of the v1 API such as ptypes.DynamicAny.
+ //
+ // There is no enum registry in Java. In v1, we used the enum registry
+ // because enum types provided no reflective methods. The addition of
+ // ProtoReflect removes that need.
+
+ typesByName typesByName
+ extensionsByMessage extensionsByMessage
+}
+
+type (
+ typesByName map[protoreflect.FullName]Type
+ extensionsByMessage map[protoreflect.FullName]extensionsByNumber
+ extensionsByNumber map[protoreflect.FieldNumber]protoreflect.ExtensionType
+)
+
+// NewTypes returns a registry initialized with the provided set of types.
+// If there are conflicts, the first one takes precedence.
+func NewTypes(typs ...Type) *Types {
+ // TODO: Allow setting resolver and parent via constructor?
+ r := new(Types)
+ r.Register(typs...) // ignore errors; first takes precedence
+ return r
+}
+
+func (r *Types) Register(typs ...Type) error {
+ var firstErr error
+typeLoop:
+ for _, typ := range typs {
+ switch typ.(type) {
+ case protoreflect.EnumType, protoreflect.MessageType, protoreflect.ExtensionType:
+ // Check for conflicts in typesByName.
+ name := typ.FullName()
+ if r.typesByName[name] != nil {
+ if firstErr == nil {
+ firstErr = errors.New("%v %v is already registered", typeName(typ), name)
+ }
+ continue typeLoop
+ }
+
+ // Check for conflicts in extensionsByMessage.
+ if xt, _ := typ.(protoreflect.ExtensionType); xt != nil {
+ field := xt.Number()
+ message := xt.MessageType().FullName()
+ if r.extensionsByMessage[message][field] != nil {
+ if firstErr == nil {
+ firstErr = errors.New("extension %v is already registered on message %v", name, message)
+ }
+ continue typeLoop
+ }
+
+ // Update extensionsByMessage.
+ if r.extensionsByMessage == nil {
+ r.extensionsByMessage = make(extensionsByMessage)
+ }
+ if r.extensionsByMessage[message] == nil {
+ r.extensionsByMessage[message] = make(extensionsByNumber)
+ }
+ r.extensionsByMessage[message][field] = xt
+ }
+
+ // Update typesByName.
+ if r.typesByName == nil {
+ r.typesByName = make(typesByName)
+ }
+ r.typesByName[name] = typ
+ default:
+ if firstErr == nil {
+ errors.New("invalid type: %v", typeName(typ))
+ }
+ }
+ }
+ return firstErr
+}
+
+// FindEnumByName looks up an enum by its full name.
+// E.g., "google.protobuf.Field.Kind".
+//
+// This return (nil, NotFound) if not found.
+func (r *Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumType, error) {
+ v, _ := r.typesByName[enum]
+ if v == nil && r.Resolver != nil {
+ var err error
+ v, err = r.Resolver(string(enum))
+ if err != nil && err != NotFound {
+ return nil, err
+ }
+ }
+ if v != nil {
+ if et, _ := v.(protoreflect.EnumType); et != nil {
+ return et, nil
+ }
+ return nil, errors.New("found wrong type: got %v, want enum", typeName(v))
+ }
+ if r.Parent != nil {
+ return r.Parent.FindEnumByName(enum)
+ }
+ return nil, NotFound
+}
+
+// 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.
+// See Resolver for the format of the URL.
+//
+// This return (nil, NotFound) if not found.
+func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error) {
+ message := protoreflect.FullName(url)
+ if i := strings.LastIndexByte(url, '/'); i >= 0 {
+ message = message[i+len("/"):]
+ }
+
+ v, _ := r.typesByName[message]
+ if v == nil && r.Resolver != nil {
+ var err error
+ v, err = r.Resolver(url)
+ if err != nil && err != NotFound {
+ return nil, err
+ }
+ }
+ if v != nil {
+ if mt, _ := v.(protoreflect.MessageType); mt != nil {
+ return mt, nil
+ }
+ return nil, errors.New("found wrong type: got %v, want message", typeName(v))
+ }
+ if r.Parent != nil {
+ return r.Parent.FindMessageByURL(url)
+ }
+ return nil, NotFound
+}
+
+// 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 return (nil, NotFound) if not found.
+func (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
+ v, _ := r.typesByName[field]
+ if v == nil && r.Resolver != nil {
+ var err error
+ v, err = r.Resolver(string(field))
+ if err != nil && err != NotFound {
+ return nil, err
+ }
+ }
+ if v != nil {
+ if xt, _ := v.(protoreflect.ExtensionType); xt != nil {
+ return xt, nil
+ }
+ return nil, errors.New("found wrong type: got %v, want extension", typeName(v))
+ }
+ if r.Parent != nil {
+ return r.Parent.FindExtensionByName(field)
+ }
+ return nil, NotFound
+}
+
+// FindExtensionByNumber looks up a extension field by the field number
+// within some parent message, identified by full name.
+//
+// This return (nil, NotFound) if not found.
+func (r *Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
+ if xt, ok := r.extensionsByMessage[message][field]; ok {
+ return xt, nil
+ }
+ if r.Parent != nil {
+ return r.Parent.FindExtensionByNumber(message, field)
+ }
+ return nil, NotFound
+}
+
+// RangeEnums iterates over all registered enums.
+// Iteration order is undefined.
+func (r *Types) RangeEnums(f func(protoreflect.EnumType) bool) {
+ for _, typ := range r.typesByName {
+ if et, ok := typ.(protoreflect.EnumType); ok {
+ if !f(et) {
+ return
+ }
+ }
+ }
+ if r.Parent != nil {
+ r.Parent.RangeEnums(f)
+ }
+}
+
+// RangeMessages iterates over all registered messages.
+// Iteration order is undefined.
+func (r *Types) RangeMessages(f func(protoreflect.MessageType) bool) {
+ for _, typ := range r.typesByName {
+ if mt, ok := typ.(protoreflect.MessageType); ok {
+ if !f(mt) {
+ return
+ }
+ }
+ }
+ if r.Parent != nil {
+ r.Parent.RangeMessages(f)
+ }
+}
+
+// RangeExtensions iterates over all registered extensions.
+// Iteration order is undefined.
+func (r *Types) RangeExtensions(f func(protoreflect.ExtensionType) bool) {
+ for _, typ := range r.typesByName {
+ if xt, ok := typ.(protoreflect.ExtensionType); ok {
+ if !f(xt) {
+ return
+ }
+ }
+ }
+ if r.Parent != nil {
+ r.Parent.RangeExtensions(f)
+ }
+}
+
+// RangeExtensionsByMessage iterates over all registered extensions filtered
+// by a given message type. Iteration order is undefined.
+func (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) {
+ for _, xt := range r.extensionsByMessage[message] {
+ if !f(xt) {
+ return
+ }
+ }
+ if r.Parent != nil {
+ r.Parent.RangeExtensionsByMessage(message, f)
+ }
+}
+
+func typeName(t Type) string {
+ switch t.(type) {
+ case protoreflect.EnumType:
+ return "enum"
+ case protoreflect.MessageType:
+ return "message"
+ case protoreflect.ExtensionType:
+ return "extension"
+ default:
+ return fmt.Sprintf("%T", t)
+ }
+}

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 1
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-MessageType: newchange

Joe Tsai (Gerrit)

unread,
Aug 24, 2018, 9:15:52 PM8/24/18
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Joe Tsai uploaded patch set #2 to this change.

View Change

reflect/protoregistry: add Types registry

The first commit of protoregistry only added a registry for files.
However, a separate type of registry is needed to provide a mapping between
protobuf names and actual Go types representing those names.

Additional high-level API:
var GlobalTypes = new(Types)
type Type interface{ ... }
type Types struct{ ... }
func NewTypes(...Type) *Types
func (*Types) FindEnumByName(pref.FullName) (pref.EnumType, error)
func (*Types) FindExtensionByName(pref.FullName) (pref.ExtensionType, error)
func (*Types) FindExtensionByNumber(pref.FullName, pref.FieldNumber) (pref.ExtensionType, error)
func (*Types) FindMessageByName(pref.FullName) (pref.MessageType, error)
func (*Types) FindMessageByURL(string) (pref.MessageType, error)
func (*Types) RangeEnums(func(pref.EnumType) bool)
func (*Types) RangeExtensions(func(pref.ExtensionType) bool)
func (*Types) RangeExtensionsByMessage(pref.FullName, func(pref.ExtensionType) bool)
func (*Types) RangeMessages(func(pref.MessageType) bool)
func (*Types) Register(...Type) error

Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
---
M reflect/protoregistry/registry.go
M reflect/protoregistry/registry_test.go
2 files changed, 318 insertions(+), 25 deletions(-)

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 2
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-MessageType: newpatchset

Joe Tsai (Gerrit)

unread,
Aug 31, 2018, 2:51:31 PM8/31/18
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Joe Tsai uploaded patch set #3 to this change.

View Change

reflect/protoregistry: add Types registry

The first commit of protoregistry only added a registry for files.
However, a separate type of registry is needed to provide a mapping between
protobuf names and actual Go types representing those names.

Additional high-level API:
var GlobalTypes = new(Types)
type Type interface{ ... }
type Types struct{ ... }
func NewTypes(...Type) *Types
		func (*Types) Register(...Type) error

func (*Types) FindMessageByName(pref.FullName) (pref.MessageType, error)
func (*Types) FindMessageByURL(string) (pref.MessageType, error)
func (*Types) FindEnumByName(pref.FullName) (pref.EnumType, error)
func (*Types) FindExtensionByName(pref.FullName) (pref.ExtensionType, error)
func (*Types) FindExtensionByNumber(pref.FullName, pref.FieldNumber) (pref.ExtensionType, error)
		func (*Types) RangeMessages(func(pref.MessageType) bool)

func (*Types) RangeEnums(func(pref.EnumType) bool)
func (*Types) RangeExtensions(func(pref.ExtensionType) bool)
func (*Types) RangeExtensionsByMessage(pref.FullName, func(pref.ExtensionType) bool)

Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
---
M reflect/protoregistry/registry.go
M reflect/protoregistry/registry_test.go
2 files changed, 352 insertions(+), 28 deletions(-)

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 3
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-MessageType: newpatchset

Joe Tsai (Gerrit)

unread,
Dec 7, 2018, 5:58:24 PM12/7/18
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Joe Tsai uploaded patch set #4 to this change.

View Change

reflect/protoregistry: add Types registry

The first commit of protoregistry only added a registry for files.
However, a separate type of registry is needed to provide a mapping between
protobuf names and actual Go types representing those names.

Additional high-level API:
var GlobalTypes = new(Types)
type Type interface{ ... }
type Types struct{ ... }
func NewTypes(...Type) *Types
        func (*Types) Register(...Type) error

func (*Types) FindEnumByName(pref.FullName) (pref.EnumType, error)
        func (*Types) FindMessageByName(pref.FullName) (pref.MessageType, error)
func (*Types) FindMessageByURL(string) (pref.MessageType, error)
func (*Types) FindExtensionByName(pref.FullName) (pref.ExtensionType, error)
func (*Types) FindExtensionByNumber(pref.FullName, pref.FieldNumber) (pref.ExtensionType, error)
        func (*Types) RangeEnums(func(pref.EnumType) bool)

func (*Types) RangeMessages(func(pref.MessageType) bool)
func (*Types) RangeExtensions(func(pref.ExtensionType) bool)
func (*Types) RangeExtensionsByMessage(pref.FullName, func(pref.ExtensionType) bool)

Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
---
M reflect/protoregistry/registry.go
M reflect/protoregistry/registry_test.go
2 files changed, 351 insertions(+), 26 deletions(-)

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 4
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-MessageType: newpatchset

Herbie Ong (Gerrit)

unread,
Dec 7, 2018, 8:28:03 PM12/7/18
to Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

View Change

3 comments:

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 4
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Herbie Ong <her...@google.com>
Gerrit-CC: Joe Tsai <thebroke...@gmail.com>
Gerrit-Comment-Date: Sat, 08 Dec 2018 01:28:01 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment

Herbie Ong (Gerrit)

unread,
Dec 18, 2018, 7:56:39 PM12/18/18
to Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

View Change

1 comment:

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 5
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Herbie Ong <her...@google.com>
Gerrit-CC: Joe Tsai <thebroke...@gmail.com>
Gerrit-Comment-Date: Wed, 19 Dec 2018 00:56:36 +0000

Herbie Ong (Gerrit)

unread,
Dec 26, 2018, 6:58:12 PM12/26/18
to Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

Herbie Ong uploaded patch set #6 to the change originally created by Joe Tsai.

View Change

reflect/protoregistry: add Types registry

The first commit of protoregistry only added a registry for files.
However, a separate type of registry is needed to provide a mapping between
protobuf names and actual Go types representing those names.

Additional high-level API:
var GlobalTypes = new(Types)
type Type interface{ ... }
type Types struct{ ... }
func NewTypes(...Type) *Types
        func (*Types) Register(...Type) error

func (*Types) FindEnumByName(pref.FullName) (pref.EnumType, error)
        func (*Types) FindMessageByName(pref.FullName) (pref.MessageType, error)
func (*Types) FindMessageByURL(string) (pref.MessageType, error)
func (*Types) FindExtensionByName(pref.FullName) (pref.ExtensionType, error)
func (*Types) FindExtensionByNumber(pref.FullName, pref.FieldNumber) (pref.ExtensionType, error)
        func (*Types) RangeEnums(func(pref.EnumType) bool)

func (*Types) RangeMessages(func(pref.MessageType) bool)
func (*Types) RangeExtensions(func(pref.ExtensionType) bool)
func (*Types) RangeExtensionsByMessage(pref.FullName, func(pref.ExtensionType) bool)

Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
---
M reflect/protoregistry/registry.go
M reflect/protoregistry/registry_test.go
A reflect/protoregistry/testprotos/test.pb.go
A reflect/protoregistry/testprotos/test.proto
M regenerate.bash
5 files changed, 1,335 insertions(+), 26 deletions(-)

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 6
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Herbie Ong <her...@google.com>
Gerrit-CC: Joe Tsai <thebroke...@gmail.com>
Gerrit-MessageType: newpatchset

Herbie Ong (Gerrit)

unread,
Dec 26, 2018, 7:03:24 PM12/26/18
to Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

Herbie Ong uploaded patch set #7 to the change originally created by Joe Tsai.

View Change

reflect/protoregistry: add Types registry

The first commit of protoregistry only added a registry for files.
However, a separate type of registry is needed to provide a mapping between
protobuf names and actual Go types representing those names.

Additional high-level API:
var GlobalTypes = new(Types)
type Type interface{ ... }
type Types struct{ ... }
func NewTypes(...Type) *Types
        func (*Types) Register(...Type) error

func (*Types) FindEnumByName(pref.FullName) (pref.EnumType, error)
        func (*Types) FindMessageByName(pref.FullName) (pref.MessageType, error)
func (*Types) FindMessageByURL(string) (pref.MessageType, error)
func (*Types) FindExtensionByName(pref.FullName) (pref.ExtensionType, error)
func (*Types) FindExtensionByNumber(pref.FullName, pref.FieldNumber) (pref.ExtensionType, error)
        func (*Types) RangeEnums(func(pref.EnumType) bool)

func (*Types) RangeMessages(func(pref.MessageType) bool)
func (*Types) RangeExtensions(func(pref.ExtensionType) bool)
func (*Types) RangeExtensionsByMessage(pref.FullName, func(pref.ExtensionType) bool)

Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
---
M reflect/protoregistry/registry.go
M reflect/protoregistry/registry_test.go
A reflect/protoregistry/testprotos/test.pb.go
A reflect/protoregistry/testprotos/test.proto
M regenerate.bash
5 files changed, 1,335 insertions(+), 26 deletions(-)

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 7

Herbie Ong (Gerrit)

unread,
Dec 26, 2018, 7:06:40 PM12/26/18
to Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, Joe Tsai, golang-co...@googlegroups.com

Somehow I was able to maintain the same gerrit change-ID for this and hence uses the same review thread.

Ready for review.

View Change

3 comments:

    • Did you intend to assign this to firstErr?

    • Done

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 7
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Herbie Ong <her...@google.com>
Gerrit-Reviewer: Joe Tsai <joe...@google.com>
Gerrit-CC: Joe Tsai <thebroke...@gmail.com>
Gerrit-Comment-Date: Thu, 27 Dec 2018 00:06:38 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Herbie Ong <her...@google.com>
Gerrit-MessageType: comment

Joe Tsai (Gerrit)

unread,
Dec 26, 2018, 10:11:07 PM12/26/18
to Herbie Ong, Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, Joe Tsai, golang-co...@googlegroups.com

I had no idea that you could modify my CLs, hahaha. Thank you for taking this over.

Patch set 7:Code-Review +2

View Change

2 comments:

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 7
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Joe Tsai <joe...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-CC: Herbie Ong <her...@google.com>
Gerrit-Comment-Date: Thu, 27 Dec 2018 03:11:03 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

Herbie Ong (Gerrit)

unread,
Dec 27, 2018, 1:55:51 AM12/27/18
to Joe Tsai, Joe Tsai, Joe Tsai, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Herbie Ong uploaded patch set #8 to the change originally created by Joe Tsai.

View Change

reflect/protoregistry: add Types registry

The first commit of protoregistry only added a registry for files.
However, a separate type of registry is needed to provide a mapping between
protobuf names and actual Go types representing those names.

Additional high-level API:
var GlobalTypes = new(Types)
type Type interface{ ... }
type Types struct{ ... }
func NewTypes(...Type) *Types
        func (*Types) Register(...Type) error

func (*Types) FindEnumByName(pref.FullName) (pref.EnumType, error)
        func (*Types) FindMessageByName(pref.FullName) (pref.MessageType, error)
func (*Types) FindMessageByURL(string) (pref.MessageType, error)
func (*Types) FindExtensionByName(pref.FullName) (pref.ExtensionType, error)
func (*Types) FindExtensionByNumber(pref.FullName, pref.FieldNumber) (pref.ExtensionType, error)
        func (*Types) RangeEnums(func(pref.EnumType) bool)

func (*Types) RangeMessages(func(pref.MessageType) bool)
func (*Types) RangeExtensions(func(pref.ExtensionType) bool)
func (*Types) RangeExtensionsByMessage(pref.FullName, func(pref.ExtensionType) bool)

Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
---
M reflect/protoregistry/registry.go
M reflect/protoregistry/registry_test.go
A reflect/protoregistry/testprotos/test.pb.go
A reflect/protoregistry/testprotos/test.proto
M regenerate.bash
5 files changed, 1,339 insertions(+), 26 deletions(-)

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 8
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Herbie Ong <her...@google.com>
Gerrit-Reviewer: Joe Tsai <joe...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-MessageType: newpatchset

Herbie Ong (Gerrit)

unread,
Dec 27, 2018, 1:59:22 AM12/27/18
to Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, Joe Tsai, golang-co...@googlegroups.com

View Change

3 comments:

    • Resolver sets the local resolver to consult if the local registry does

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 8
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Herbie Ong <her...@google.com>
Gerrit-Reviewer: Joe Tsai <joe...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-Comment-Date: Thu, 27 Dec 2018 06:59:19 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Joe Tsai <thebroke...@gmail.com>
Gerrit-MessageType: comment

Joe Tsai (Gerrit)

unread,
Dec 27, 2018, 2:06:12 PM12/27/18
to Herbie Ong, Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, Joe Tsai, golang-co...@googlegroups.com

View Change

1 comment:

    • Resolver sets the local resolver to consult if the local registry does


    • // not contain an entry

    • Out of curiosity, shouldn't the resolver have precedence over the local registry? What do you think […]

      It's not obvious. As a general rule of thumb, things that are more in the control of the user have higher precedence over than not. Thus, local registry takes precedence over parent registry.

      Now, both the contents in the registry and the those accessible by the Resolver function are more or less at the same level of user control, so it's not clear which of the two takes precedence. I think we should just choose one and document it.

      Light reasons why I think local registry takes precedence over the local resolver func:

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 8
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Herbie Ong <her...@google.com>
Gerrit-Reviewer: Joe Tsai <joe...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-Comment-Date: Thu, 27 Dec 2018 19:06:10 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No

Herbie Ong (Gerrit)

unread,
Dec 27, 2018, 2:21:16 PM12/27/18
to Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, Joe Tsai, golang-co...@googlegroups.com

Submitting.

View Change

1 comment:

    • Resolver sets the local resolver to consult if the local registry does


    • // not contain an entry

    • It's not obvious. […]

      Agree. I had second thoughts about this. I think having users being able to create their own registry instances to begin with (as provided by this feature) does make sense to use it directly and only fallback to a custom resolver if necessary.

      I take it that one should generally not set GlobalTypes.Resolver as well in a library.

To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
Gerrit-Change-Number: 131345
Gerrit-PatchSet: 8
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Herbie Ong <her...@google.com>
Gerrit-Reviewer: Joe Tsai <joe...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-Comment-Date: Thu, 27 Dec 2018 19:21:14 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Joe Tsai <thebroke...@gmail.com>

Herbie Ong (Gerrit)

unread,
Dec 27, 2018, 2:23:03 PM12/27/18
to Joe Tsai, goph...@pubsubhelper.golang.org, Joe Tsai, Joe Tsai, golang-co...@googlegroups.com

Oops, I think I'll submit after CL/154657 is reviewed+submitted. Not required, but prefer to keep the relation chain.

View Change

    To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: protobuf
    Gerrit-Branch: master
    Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
    Gerrit-Change-Number: 131345
    Gerrit-PatchSet: 8
    Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
    Gerrit-Reviewer: Herbie Ong <her...@google.com>
    Gerrit-Reviewer: Joe Tsai <joe...@google.com>
    Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
    Gerrit-Comment-Date: Thu, 27 Dec 2018 19:23:01 +0000
    Gerrit-HasComments: No
    Gerrit-Has-Labels: No
    Gerrit-MessageType: comment

    Herbie Ong (Gerrit)

    unread,
    Dec 27, 2018, 7:06:33 PM12/27/18
    to Joe Tsai, goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Joe Tsai, Joe Tsai, golang-co...@googlegroups.com

    Herbie Ong merged this change.

    View Change

    Approvals: Joe Tsai: Looks good to me, approved
    reflect/protoregistry: add Types registry

    The first commit of protoregistry only added a registry for files.
    However, a separate type of registry is needed to provide a mapping between
    protobuf names and actual Go types representing those names.

    Additional high-level API:
    var GlobalTypes = new(Types)
    type Type interface{ ... }
    type Types struct{ ... }
    func NewTypes(...Type) *Types
            func (*Types) Register(...Type) error

    func (*Types) FindEnumByName(pref.FullName) (pref.EnumType, error)
            func (*Types) FindMessageByName(pref.FullName) (pref.MessageType, error)
    func (*Types) FindMessageByURL(string) (pref.MessageType, error)
    func (*Types) FindExtensionByName(pref.FullName) (pref.ExtensionType, error)
    func (*Types) FindExtensionByNumber(pref.FullName, pref.FieldNumber) (pref.ExtensionType, error)
            func (*Types) RangeEnums(func(pref.EnumType) bool)

    func (*Types) RangeMessages(func(pref.MessageType) bool)
    func (*Types) RangeExtensions(func(pref.ExtensionType) bool)
    func (*Types) RangeExtensionsByMessage(pref.FullName, func(pref.ExtensionType) bool)

    Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
    Reviewed-on: https://go-review.googlesource.com/c/131345
    Reviewed-by: Joe Tsai <thebroke...@gmail.com>

    ---
    M reflect/protoregistry/registry.go
    M reflect/protoregistry/registry_test.go
    A reflect/protoregistry/testprotos/test.pb.go
    A reflect/protoregistry/testprotos/test.proto
    M regenerate.bash
    5 files changed, 1,339 insertions(+), 26 deletions(-)

    diff --git a/reflect/protoregistry/registry.go b/reflect/protoregistry/registry.go
    index e190bda..5524452 100644

    --- a/reflect/protoregistry/registry.go
    +++ b/reflect/protoregistry/registry.go
    @@ -4,9 +4,20 @@

    // Package protoregistry provides data structures to register and lookup
    // protobuf descriptor types.
    +//
    +// 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.
    package protoregistry

    import (
    + "fmt"
    + "reflect"
    "sort"
    "strings"

    @@ -19,33 +30,12 @@
    -var GlobalFiles = new(Files)
    +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)


    // NotFound is a sentinel error value to indicate that the type was not found.
    var NotFound = errors.New("not found")
    @@ -298,3 +288,337 @@
    f(fd.Services().Get(i).Name())
    }
    }
    +
    +// Type is an interface satisfied by protoreflect.EnumType,
    +// protoreflect.MessageType, or protoreflect.ExtensionType.

    +type Type interface {
    + protoreflect.Descriptor
    + GoType() reflect.Type
    +}
    +
    +var (
    + _ Type = protoreflect.EnumType(nil)
    + _ Type = protoreflect.MessageType(nil)
    + _ Type = protoreflect.ExtensionType(nil)
    +)

    +
    +// 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 {
    + // Parent sets the parent registry to consult if a find operation
    + // could not locate the appropriate entry.
    + //
    + // Setting a parent results in each Range operation also iterating over the
    + // entries contained within the parent. In such a case, it is possible for
    + // Range to emit duplicates (since they may exist in both child and parent).
    +	// Range iteration is guaranteed to iterate over local entries before

    + // iterating over parent entries.
    + Parent *Types
    +
    + // Resolver sets the local resolver to consult if the local registry does
    + // not contain an entry. The resolver takes precedence over the parent.
    + //
    + // The url is a URL where the full name of the type is the last segment
    + // of the path (i.e. string following the last '/' character).
    + // When missing a '/' character, the URL is the full name of the type.
    + // See documentation on the google.protobuf.Any.type_url field for details.
    + //
    + // If the resolver returns a result, it is not automatically registered
    + // into the local registry. Thus, a resolver function should cache results
    + // such that it deterministically returns the same result given the
    + // same URL assuming the error returned is nil or NotFound.
    + //
    +	// If the resolver returns the NotFound error, the registry will consult the
    + // parent registry if it is set.

    + //
    + // Setting a resolver has no effect on the result of each Range operation.
    + Resolver func(url string) (Type, error)
    +
    +	// TODO: The syntax of the URL is ill-defined and the protobuf team recently
    + // changed the documented semantics in a way that breaks prior usages.
    + // I do not believe they can do this and need to sync up with the
    + // protobuf team again to hash out what the proper syntax of the URL is.
    +
    +// Register registers the provided list of descriptor types.
    +//
    +// If a registration conflict occurs for enum, message, or extension types
    +// (e.g., two different types have the same full name),
    +// then the first type takes precedence and an error is returned.

    +func (r *Types) Register(typs ...Type) error {
    + var firstErr error
    +typeLoop:
    + for _, typ := range typs {
    + switch typ.(type) {
    + case protoreflect.EnumType, protoreflect.MessageType, protoreflect.ExtensionType:
    + // Check for conflicts in typesByName.
    + name := typ.FullName()
    + if r.typesByName[name] != nil {
    + if firstErr == nil {
    + firstErr = errors.New("%v %v is already registered", typeName(typ), name)
    + }
    + continue typeLoop
    + }
    +
    + // Check for conflicts in extensionsByMessage.
    + if xt, _ := typ.(protoreflect.ExtensionType); xt != nil {
    + field := xt.Number()
    +				message := xt.ExtendedType().FullName()

    + if r.extensionsByMessage[message][field] != nil {
    + if firstErr == nil {
    + firstErr = errors.New("extension %v is already registered on message %v", name, message)
    + }
    + continue typeLoop
    + }
    +
    + // Update extensionsByMessage.
    + if r.extensionsByMessage == nil {
    + r.extensionsByMessage = make(extensionsByMessage)
    + }
    + if r.extensionsByMessage[message] == nil {
    + r.extensionsByMessage[message] = make(extensionsByNumber)
    + }
    + r.extensionsByMessage[message][field] = xt
    + }
    +
    + // Update typesByName.
    + if r.typesByName == nil {
    + r.typesByName = make(typesByName)
    + }
    + r.typesByName[name] = typ
    + default:
    + if firstErr == nil {
    +				firstErr = errors.New("invalid type: %v", typeName(typ))

    + }
    + }
    + }
    + return firstErr
    +}
    +
    +// 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) {
    +	r.globalCheck()
    + if r == nil {

    + return nil, NotFound
    + }
    +	v, _ := r.typesByName[enum]
    + if v == nil && r.Resolver != nil {
    + var err error
    + v, err = r.Resolver(string(enum))
    + if err != nil && err != NotFound {
    + return nil, err
    + }
    + }
    + if v != nil {
    + if et, _ := v.(protoreflect.EnumType); et != nil {
    + return et, nil
    + }
    + return nil, errors.New("found wrong type: got %v, want enum", typeName(v))
    + }
    +	return r.Parent.FindEnumByName(enum)

    +}
    +
    +// 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.
    +// See Resolver for the format of the URL.
    +//
    +// This returns (nil, NotFound) if not found.

    +func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error) {
    +	r.globalCheck()
    + if r == nil {

    + return nil, NotFound
    + }
    +	message := protoreflect.FullName(url)
    + if i := strings.LastIndexByte(url, '/'); i >= 0 {
    + message = message[i+len("/"):]
    + }
    +
    + v, _ := r.typesByName[message]
    + if v == nil && r.Resolver != nil {
    + var err error
    + v, err = r.Resolver(url)
    + if err != nil && err != NotFound {
    + return nil, err
    + }
    + }
    + if v != nil {
    + if mt, _ := v.(protoreflect.MessageType); mt != nil {
    + return mt, nil
    + }
    + return nil, errors.New("found wrong type: got %v, want message", typeName(v))
    + }
    +	return r.Parent.FindMessageByURL(url)

    +}
    +
    +// 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) {
    +	r.globalCheck()
    + if r == nil {

    + return nil, NotFound
    + }
    +	v, _ := r.typesByName[field]
    + if v == nil && r.Resolver != nil {
    + var err error
    + v, err = r.Resolver(string(field))
    + if err != nil && err != NotFound {
    + return nil, err
    + }
    + }
    + if v != nil {
    + if xt, _ := v.(protoreflect.ExtensionType); xt != nil {
    + return xt, nil
    + }
    + return nil, errors.New("found wrong type: got %v, want extension", typeName(v))
    + }
    +	return r.Parent.FindExtensionByName(field)

    +}
    +
    +// 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) {
    +	r.globalCheck()
    + if r == nil {

    + return nil, NotFound
    + }
    +	if xt, ok := r.extensionsByMessage[message][field]; ok {
    + return xt, nil
    + }
    +	return r.Parent.FindExtensionByNumber(message, field)
    +}
    +
    +// RangeEnums iterates over all registered enums.
    +// Iteration order is undefined.
    +func (r *Types) RangeEnums(f func(protoreflect.EnumType) bool) {
    +	r.globalCheck()
    + if r == nil {
    + return
    + }

    + for _, typ := range r.typesByName {
    + if et, ok := typ.(protoreflect.EnumType); ok {
    + if !f(et) {
    + return
    + }
    + }
    + }
    +	r.Parent.RangeEnums(f)

    +}
    +
    +// RangeMessages iterates over all registered messages.
    +// Iteration order is undefined.
    +func (r *Types) RangeMessages(f func(protoreflect.MessageType) bool) {
    +	r.globalCheck()
    + if r == nil {
    + return
    + }

    + for _, typ := range r.typesByName {
    + if mt, ok := typ.(protoreflect.MessageType); ok {
    + if !f(mt) {
    + return
    + }
    + }
    + }
    +	r.Parent.RangeMessages(f)

    +}
    +
    +// RangeExtensions iterates over all registered extensions.
    +// Iteration order is undefined.
    +func (r *Types) RangeExtensions(f func(protoreflect.ExtensionType) bool) {
    +	r.globalCheck()
    + if r == nil {
    + return
    + }

    + for _, typ := range r.typesByName {
    + if xt, ok := typ.(protoreflect.ExtensionType); ok {
    + if !f(xt) {
    + return
    + }
    + }
    + }
    +	r.Parent.RangeExtensions(f)

    +}
    +
    +// RangeExtensionsByMessage iterates over all registered extensions filtered
    +// by a given message type. Iteration order is undefined.
    +func (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) {
    +	r.globalCheck()
    + if r == nil {
    + return
    + }

    + for _, xt := range r.extensionsByMessage[message] {
    + if !f(xt) {
    + return
    + }
    + }
    +	r.Parent.RangeExtensionsByMessage(message, f)
    +}
    +
    +func (r *Types) globalCheck() {
    + if r == GlobalTypes && (r.Parent != nil || r.Resolver != nil) {
    + panic("GlobalTypes.Parent and GlobalTypes.Resolver cannot be set")

    + }
    +}
    +
    +func typeName(t Type) string {
    + switch t.(type) {
    + case protoreflect.EnumType:
    + return "enum"
    + case protoreflect.MessageType:
    + return "message"
    + case protoreflect.ExtensionType:
    + return "extension"
    + default:
    + return fmt.Sprintf("%T", t)
    + }
    +}
    diff --git a/reflect/protoregistry/registry_test.go b/reflect/protoregistry/registry_test.go
    index 73a73a0..5be19b1 100644
    --- a/reflect/protoregistry/registry_test.go
    +++ b/reflect/protoregistry/registry_test.go
    @@ -12,9 +12,13 @@
    "github.com/google/go-cmp/cmp"
    "github.com/google/go-cmp/cmp/cmpopts"

    + "github.com/golang/protobuf/protoapi"
    + "github.com/golang/protobuf/v2/internal/legacy"
    +
    + testpb "github.com/golang/protobuf/v2/reflect/protoregistry/testprotos"
    )

    func TestFiles(t *testing.T) {
    @@ -312,3 +316,357 @@
    })
    }
    }
    +
    +func extensionType(xd *protoapi.ExtensionDesc) pref.ExtensionType {
    + return legacy.Export{}.ExtensionTypeFromDesc(xd)
    +}
    +
    +func TestTypes(t *testing.T) {
    + // Suffix 1 in registry, 2 in parent, 3 in resolver.
    + mt1 := (&testpb.Message1{}).ProtoReflect().Type()
    + mt2 := (&testpb.Message2{}).ProtoReflect().Type()
    + mt3 := (&testpb.Message3{}).ProtoReflect().Type()
    + et1 := testpb.Enum1_ONE.ProtoReflect().Type()
    + et2 := testpb.Enum2_UNO.ProtoReflect().Type()
    + et3 := testpb.Enum3_YI.ProtoReflect().Type()
    + // Suffix indicates field number.
    + xt11 := extensionType(testpb.E_StringField)
    + xt12 := extensionType(testpb.E_EnumField)
    + xt13 := extensionType(testpb.E_MessageField)
    + xt21 := extensionType(testpb.E_Message4_MessageField)
    + xt22 := extensionType(testpb.E_Message4_EnumField)
    + xt23 := extensionType(testpb.E_Message4_StringField)
    + parent := &preg.Types{}
    + if err := parent.Register(mt2, et2, xt12, xt22); err != nil {
    + t.Fatalf("parent.Register() returns unexpected error: %v", err)
    + }
    + registry := &preg.Types{
    + Parent: parent,
    + Resolver: func(url string) (preg.Type, error) {
    + switch {
    + case strings.HasSuffix(url, "testprotos.Message3"):
    + return mt3, nil
    + case strings.HasSuffix(url, "testprotos.Enum3"):
    + return et3, nil
    + case strings.HasSuffix(url, "testprotos.message_field"):
    + return xt13, nil
    + case strings.HasSuffix(url, "testprotos.Message4.string_field"):
    + return xt23, nil
    + }
    + return nil, preg.NotFound
    + },
    + }
    + if err := registry.Register(mt1, et1, xt11, xt21); err != nil {
    + t.Fatalf("registry.Register() returns unexpected error: %v", err)
    + }
    +
    + t.Run("FindMessageByName", func(t *testing.T) {
    + tests := []struct {
    + name string
    + messageType pref.MessageType
    + wantErr bool
    + wantNotFound bool
    + }{{
    + name: "testprotos.Message1",
    + messageType: mt1,
    + }, {
    + name: "testprotos.Message2",
    + messageType: mt2,
    + }, {
    + name: "testprotos.Message3",
    + messageType: mt3,
    + }, {
    + name: "testprotos.NoSuchMessage",
    + wantErr: true,
    + wantNotFound: true,
    + }, {
    + name: "testprotos.Enum1",
    + wantErr: true,
    + }, {
    + name: "testprotos.Enum2",
    + wantErr: true,
    + }, {
    + name: "testprotos.Enum3",
    + wantErr: true,
    + }}
    + for _, tc := range tests {
    + got, err := registry.FindMessageByName(pref.FullName(tc.name))
    + gotErr := err != nil
    + if gotErr != tc.wantErr {
    + t.Errorf("FindMessageByName(%v) = (_, %v), want error? %t", tc.name, err, tc.wantErr)
    + continue
    + }
    + if tc.wantNotFound && err != preg.NotFound {
    + t.Errorf("FindMessageByName(%v) got error: %v, want NotFound error", tc.name, err)
    + continue
    + }
    + if got != tc.messageType {
    + t.Errorf("FindMessageByName(%v) got wrong value: %v", tc.name, got)
    + }
    + }
    + })
    +
    + t.Run("FindMessageByURL", func(t *testing.T) {
    + tests := []struct {
    + name string
    + messageType pref.MessageType
    + wantErr bool
    + wantNotFound bool
    + }{{
    + name: "testprotos.Message1",
    + messageType: mt1,
    + }, {
    + name: "foo.com/testprotos.Message2",
    + messageType: mt2,
    + }, {
    + name: "/testprotos.Message3",
    + messageType: mt3,
    + }, {
    + name: "type.googleapis.com/testprotos.Nada",
    + wantErr: true,
    + wantNotFound: true,
    + }, {
    + name: "testprotos.Enum1",
    + wantErr: true,
    + }}
    + for _, tc := range tests {
    + got, err := registry.FindMessageByURL(tc.name)
    + gotErr := err != nil
    + if gotErr != tc.wantErr {
    + t.Errorf("FindMessageByURL(%v) = (_, %v), want error? %t", tc.name, err, tc.wantErr)
    + continue
    + }
    + if tc.wantNotFound && err != preg.NotFound {
    + t.Errorf("FindMessageByURL(%v) got error: %v, want NotFound error", tc.name, err)
    + continue
    + }
    + if got != tc.messageType {
    + t.Errorf("FindMessageByURL(%v) got wrong value: %v", tc.name, got)
    + }
    + }
    + })
    +
    + t.Run("FindEnumByName", func(t *testing.T) {
    + tests := []struct {
    + name string
    + enumType pref.EnumType
    + wantErr bool
    + wantNotFound bool
    + }{{
    + name: "testprotos.Enum1",
    + enumType: et1,
    + }, {
    + name: "testprotos.Enum2",
    + enumType: et2,
    + }, {
    + name: "testprotos.Enum3",
    + enumType: et3,
    + }, {
    + name: "testprotos.None",
    + wantErr: true,
    + wantNotFound: true,
    + }, {
    + name: "testprotos.Message1",
    + wantErr: true,
    + }}
    + for _, tc := range tests {
    + got, err := registry.FindEnumByName(pref.FullName(tc.name))
    + gotErr := err != nil
    + if gotErr != tc.wantErr {
    + t.Errorf("FindEnumByName(%v) = (_, %v), want error? %t", tc.name, err, tc.wantErr)
    + continue
    + }
    + if tc.wantNotFound && err != preg.NotFound {
    + t.Errorf("FindEnumByName(%v) got error: %v, want NotFound error", tc.name, err)
    + continue
    + }
    + if got != tc.enumType {
    + t.Errorf("FindEnumByName(%v) got wrong value: %v", tc.name, got)
    + }
    + }
    + })
    +
    + t.Run("FindExtensionByName", func(t *testing.T) {
    + tests := []struct {
    + name string
    + extensionType pref.ExtensionType
    + wantErr bool
    + wantNotFound bool
    + }{{
    + name: "testprotos.string_field",
    + extensionType: xt11,
    + }, {
    + name: "testprotos.enum_field",
    + extensionType: xt12,
    + }, {
    + name: "testprotos.message_field",
    + extensionType: xt13,
    + }, {
    + name: "testprotos.Message4.message_field",
    + extensionType: xt21,
    + }, {
    + name: "testprotos.Message4.enum_field",
    + extensionType: xt22,
    + }, {
    + name: "testprotos.Message4.string_field",
    + extensionType: xt23,
    + }, {
    + name: "testprotos.None",
    + wantErr: true,
    + wantNotFound: true,
    + }, {
    + name: "testprotos.Message1",
    + wantErr: true,
    + }}
    + for _, tc := range tests {
    + got, err := registry.FindExtensionByName(pref.FullName(tc.name))
    + gotErr := err != nil
    + if gotErr != tc.wantErr {
    + t.Errorf("FindExtensionByName(%v) = (_, %v), want error? %t", tc.name, err, tc.wantErr)
    + continue
    + }
    + if tc.wantNotFound && err != preg.NotFound {
    + t.Errorf("FindExtensionByName(%v) got error: %v, want NotFound error", tc.name, err)
    + continue
    + }
    + if got != tc.extensionType {
    + t.Errorf("FindExtensionByName(%v) got wrong value: %v", tc.name, got)
    + }
    + }
    + })
    +
    + t.Run("FindExtensionByNumber", func(t *testing.T) {
    + tests := []struct {
    + parent string
    + number int32
    + extensionType pref.ExtensionType
    + wantErr bool
    + wantNotFound bool
    + }{{
    + parent: "testprotos.Message1",
    + number: 11,
    + extensionType: xt11,
    + }, {
    + parent: "testprotos.Message1",
    + number: 12,
    + extensionType: xt12,
    + }, {
    + // FindExtensionByNumber does not use Resolver.
    + parent: "testprotos.Message1",
    + number: 13,
    + wantErr: true,
    + wantNotFound: true,
    + }, {
    + parent: "testprotos.Message1",
    + number: 21,
    + extensionType: xt21,
    + }, {
    + parent: "testprotos.Message1",
    + number: 22,
    + extensionType: xt22,
    + }, {
    + // FindExtensionByNumber does not use Resolver.
    + parent: "testprotos.Message1",
    + number: 23,
    + wantErr: true,
    + wantNotFound: true,
    + }, {
    + parent: "testprotos.NoSuchMessage",
    + number: 11,
    + wantErr: true,
    + wantNotFound: true,
    + }, {
    + parent: "testprotos.Message1",
    + number: 30,
    + wantErr: true,
    + wantNotFound: true,
    + }, {
    + parent: "testprotos.Message1",
    + number: 99,
    + wantErr: true,
    + wantNotFound: true,
    + }}
    + for _, tc := range tests {
    + got, err := registry.FindExtensionByNumber(pref.FullName(tc.parent), pref.FieldNumber(tc.number))
    + gotErr := err != nil
    + if gotErr != tc.wantErr {
    + t.Errorf("FindExtensionByNumber(%v, %d) = (_, %v), want error? %t", tc.parent, tc.number, err, tc.wantErr)
    + continue
    + }
    + if tc.wantNotFound && err != preg.NotFound {
    + t.Errorf("FindExtensionByNumber(%v, %d) got error %v, want NotFound error", tc.parent, tc.number, err)
    + continue
    + }
    + if got != tc.extensionType {
    + t.Errorf("FindExtensionByNumber(%v, %d) got wrong value: %v", tc.parent, tc.number, got)
    + }
    + }
    + })
    +
    + sortTypes := cmpopts.SortSlices(func(x, y preg.Type) bool {
    + return x.FullName() < y.FullName()
    + })
    + compare := cmp.Comparer(func(x, y preg.Type) bool {
    + return x == y
    + })
    +
    + t.Run("RangeMessages", func(t *testing.T) {
    + // RangeMessages do not include messages from Resolver.
    + want := []preg.Type{mt1, mt2}
    + var got []preg.Type
    + registry.RangeMessages(func(mt pref.MessageType) bool {
    + got = append(got, mt)
    + return true
    + })
    +
    + diff := cmp.Diff(want, got, sortTypes, compare)
    + if diff != "" {
    + t.Errorf("RangeMessages() mismatch (-want +got):\n%v", diff)
    + }
    + })
    +
    + t.Run("RangeEnums", func(t *testing.T) {
    + // RangeEnums do not include enums from Resolver.
    + want := []preg.Type{et1, et2}
    + var got []preg.Type
    + registry.RangeEnums(func(et pref.EnumType) bool {
    + got = append(got, et)
    + return true
    + })
    +
    + diff := cmp.Diff(want, got, sortTypes, compare)
    + if diff != "" {
    + t.Errorf("RangeEnums() mismatch (-want +got):\n%v", diff)
    + }
    + })
    +
    + t.Run("RangeExtensions", func(t *testing.T) {
    + // RangeExtensions do not include messages from Resolver.
    + want := []preg.Type{xt11, xt12, xt21, xt22}
    + var got []preg.Type
    + registry.RangeExtensions(func(xt pref.ExtensionType) bool {
    + got = append(got, xt)
    + return true
    + })
    +
    + diff := cmp.Diff(want, got, sortTypes, compare)
    + if diff != "" {
    + t.Errorf("RangeExtensions() mismatch (-want +got):\n%v", diff)
    + }
    + })
    +
    + t.Run("RangeExtensionsByMessage", func(t *testing.T) {
    + // RangeExtensions do not include messages from Resolver.
    + want := []preg.Type{xt11, xt12, xt21, xt22}
    + var got []preg.Type
    + registry.RangeExtensionsByMessage(pref.FullName("testprotos.Message1"), func(xt pref.ExtensionType) bool {
    + got = append(got, xt)
    + return true
    + })
    +
    + diff := cmp.Diff(want, got, sortTypes, compare)
    + if diff != "" {
    + t.Errorf("RangeExtensionsByMessage() mismatch (-want +got):\n%v", diff)
    + }
    + })
    +}
    diff --git a/reflect/protoregistry/testprotos/test.pb.go b/reflect/protoregistry/testprotos/test.pb.go
    new file mode 100644
    index 0000000..94362b3
    --- /dev/null
    +++ b/reflect/protoregistry/testprotos/test.pb.go
    @@ -0,0 +1,582 @@
    +// Code generated by protoc-gen-go. DO NOT EDIT.
    +// source: reflect/protoregistry/testprotos/test.proto
    +
    +package testprotos
    +
    +import (
    + proto "github.com/golang/protobuf/proto"
    + protoreflect "github.com/golang/protobuf/v2/reflect/protoreflect"
    + prototype "github.com/golang/protobuf/v2/reflect/prototype"
    + protoimpl "github.com/golang/protobuf/v2/runtime/protoimpl"
    +)
    +
    +// This is a compile-time assertion to ensure that this generated file
    +// is compatible with the proto package it is being compiled against.
    +// A compilation error at this line likely means your copy of the
    +// proto package needs to be updated.
    +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
    +
    +type Enum1 int32
    +
    +const (
    + Enum1_ONE Enum1 = 1
    +)
    +
    +type xxx_Enum1 Enum1
    +
    +func (e Enum1) ProtoReflect() protoreflect.Enum {
    + return (xxx_Enum1)(e)
    +}
    +func (e xxx_Enum1) Type() protoreflect.EnumType {
    + return xxx_Test_ProtoFile_EnumTypes[0]
    +}
    +func (e xxx_Enum1) Number() protoreflect.EnumNumber {
    + return protoreflect.EnumNumber(e)
    +}
    +
    +var Enum1_name = map[int32]string{
    + 1: "ONE",
    +}
    +
    +var Enum1_value = map[string]int32{
    + "ONE": 1,
    +}
    +
    +func (x Enum1) Enum() *Enum1 {
    + p := new(Enum1)
    + *p = x
    + return p
    +}
    +
    +func (x Enum1) String() string {
    + return proto.EnumName(Enum1_name, int32(x))
    +}
    +
    +func (x *Enum1) UnmarshalJSON(data []byte) error {
    + value, err := proto.UnmarshalJSONEnum(Enum1_value, data, "Enum1")
    + if err != nil {
    + return err
    + }
    + *x = Enum1(value)
    + return nil
    +}
    +
    +func (Enum1) EnumDescriptor() ([]byte, []int) {
    + return fileDescriptor_3628d63611f7063d, []int{0}
    +}
    +
    +type Enum2 int32
    +
    +const (
    + Enum2_UNO Enum2 = 1
    +)
    +
    +type xxx_Enum2 Enum2
    +
    +func (e Enum2) ProtoReflect() protoreflect.Enum {
    + return (xxx_Enum2)(e)
    +}
    +func (e xxx_Enum2) Type() protoreflect.EnumType {
    + return xxx_Test_ProtoFile_EnumTypes[1]
    +}
    +func (e xxx_Enum2) Number() protoreflect.EnumNumber {
    + return protoreflect.EnumNumber(e)
    +}
    +
    +var Enum2_name = map[int32]string{
    + 1: "UNO",
    +}
    +
    +var Enum2_value = map[string]int32{
    + "UNO": 1,
    +}
    +
    +func (x Enum2) Enum() *Enum2 {
    + p := new(Enum2)
    + *p = x
    + return p
    +}
    +
    +func (x Enum2) String() string {
    + return proto.EnumName(Enum2_name, int32(x))
    +}
    +
    +func (x *Enum2) UnmarshalJSON(data []byte) error {
    + value, err := proto.UnmarshalJSONEnum(Enum2_value, data, "Enum2")
    + if err != nil {
    + return err
    + }
    + *x = Enum2(value)
    + return nil
    +}
    +
    +func (Enum2) EnumDescriptor() ([]byte, []int) {
    + return fileDescriptor_3628d63611f7063d, []int{1}
    +}
    +
    +type Enum3 int32
    +
    +const (
    + Enum3_YI Enum3 = 1
    +)
    +
    +type xxx_Enum3 Enum3
    +
    +func (e Enum3) ProtoReflect() protoreflect.Enum {
    + return (xxx_Enum3)(e)
    +}
    +func (e xxx_Enum3) Type() protoreflect.EnumType {
    + return xxx_Test_ProtoFile_EnumTypes[2]
    +}
    +func (e xxx_Enum3) Number() protoreflect.EnumNumber {
    + return protoreflect.EnumNumber(e)
    +}
    +
    +var Enum3_name = map[int32]string{
    + 1: "YI",
    +}
    +
    +var Enum3_value = map[string]int32{
    + "YI": 1,
    +}
    +
    +func (x Enum3) Enum() *Enum3 {
    + p := new(Enum3)
    + *p = x
    + return p
    +}
    +
    +func (x Enum3) String() string {
    + return proto.EnumName(Enum3_name, int32(x))
    +}
    +
    +func (x *Enum3) UnmarshalJSON(data []byte) error {
    + value, err := proto.UnmarshalJSONEnum(Enum3_value, data, "Enum3")
    + if err != nil {
    + return err
    + }
    + *x = Enum3(value)
    + return nil
    +}
    +
    +func (Enum3) EnumDescriptor() ([]byte, []int) {
    + return fileDescriptor_3628d63611f7063d, []int{2}
    +}
    +
    +type Message1 struct {
    + XXX_NoUnkeyedLiteral struct{} `json:"-"`
    + proto.XXX_InternalExtensions `json:"-"`
    + XXX_unrecognized []byte `json:"-"`
    + XXX_sizecache int32 `json:"-"`
    +}
    +
    +type xxx_Message1 struct{ m *Message1 }
    +
    +func (m *Message1) ProtoReflect() protoreflect.Message {
    + return xxx_Message1{m}
    +}
    +func (m xxx_Message1) Type() protoreflect.MessageType {
    + return xxx_Test_ProtoFile_MessageTypes[0].Type
    +}
    +func (m xxx_Message1) KnownFields() protoreflect.KnownFields {
    + return xxx_Test_ProtoFile_MessageTypes[0].KnownFieldsOf(m.m)
    +}
    +func (m xxx_Message1) UnknownFields() protoreflect.UnknownFields {
    + return xxx_Test_ProtoFile_MessageTypes[0].UnknownFieldsOf(m.m)
    +}
    +func (m xxx_Message1) Interface() protoreflect.ProtoMessage {
    + return m.m
    +}
    +
    +func (m *Message1) Reset() { *m = Message1{} }
    +func (m *Message1) String() string { return proto.CompactTextString(m) }
    +func (*Message1) ProtoMessage() {}
    +func (*Message1) Descriptor() ([]byte, []int) {
    + return fileDescriptor_3628d63611f7063d, []int{0}
    +}
    +
    +var extRange_Message1 = []proto.ExtensionRange{
    + {Start: 10, End: 536870911},
    +}
    +
    +func (*Message1) ExtensionRangeArray() []proto.ExtensionRange {
    + return extRange_Message1
    +}
    +
    +func (m *Message1) XXX_Unmarshal(b []byte) error {
    + return xxx_messageInfo_Message1.Unmarshal(m, b)
    +}
    +func (m *Message1) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    + return xxx_messageInfo_Message1.Marshal(b, m, deterministic)
    +}
    +func (m *Message1) XXX_Merge(src proto.Message) {
    + xxx_messageInfo_Message1.Merge(m, src)
    +}
    +func (m *Message1) XXX_Size() int {
    + return xxx_messageInfo_Message1.Size(m)
    +}
    +func (m *Message1) XXX_DiscardUnknown() {
    + xxx_messageInfo_Message1.DiscardUnknown(m)
    +}
    +
    +var xxx_messageInfo_Message1 proto.InternalMessageInfo
    +
    +type Message2 struct {
    + XXX_NoUnkeyedLiteral struct{} `json:"-"`
    + XXX_unrecognized []byte `json:"-"`
    + XXX_sizecache int32 `json:"-"`
    +}
    +
    +type xxx_Message2 struct{ m *Message2 }
    +
    +func (m *Message2) ProtoReflect() protoreflect.Message {
    + return xxx_Message2{m}
    +}
    +func (m xxx_Message2) Type() protoreflect.MessageType {
    + return xxx_Test_ProtoFile_MessageTypes[1].Type
    +}
    +func (m xxx_Message2) KnownFields() protoreflect.KnownFields {
    + return xxx_Test_ProtoFile_MessageTypes[1].KnownFieldsOf(m.m)
    +}
    +func (m xxx_Message2) UnknownFields() protoreflect.UnknownFields {
    + return xxx_Test_ProtoFile_MessageTypes[1].UnknownFieldsOf(m.m)
    +}
    +func (m xxx_Message2) Interface() protoreflect.ProtoMessage {
    + return m.m
    +}
    +
    +func (m *Message2) Reset() { *m = Message2{} }
    +func (m *Message2) String() string { return proto.CompactTextString(m) }
    +func (*Message2) ProtoMessage() {}
    +func (*Message2) Descriptor() ([]byte, []int) {
    + return fileDescriptor_3628d63611f7063d, []int{1}
    +}
    +
    +func (m *Message2) XXX_Unmarshal(b []byte) error {
    + return xxx_messageInfo_Message2.Unmarshal(m, b)
    +}
    +func (m *Message2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    + return xxx_messageInfo_Message2.Marshal(b, m, deterministic)
    +}
    +func (m *Message2) XXX_Merge(src proto.Message) {
    + xxx_messageInfo_Message2.Merge(m, src)
    +}
    +func (m *Message2) XXX_Size() int {
    + return xxx_messageInfo_Message2.Size(m)
    +}
    +func (m *Message2) XXX_DiscardUnknown() {
    + xxx_messageInfo_Message2.DiscardUnknown(m)
    +}
    +
    +var xxx_messageInfo_Message2 proto.InternalMessageInfo
    +
    +type Message3 struct {
    + XXX_NoUnkeyedLiteral struct{} `json:"-"`
    + XXX_unrecognized []byte `json:"-"`
    + XXX_sizecache int32 `json:"-"`
    +}
    +
    +type xxx_Message3 struct{ m *Message3 }
    +
    +func (m *Message3) ProtoReflect() protoreflect.Message {
    + return xxx_Message3{m}
    +}
    +func (m xxx_Message3) Type() protoreflect.MessageType {
    + return xxx_Test_ProtoFile_MessageTypes[2].Type
    +}
    +func (m xxx_Message3) KnownFields() protoreflect.KnownFields {
    + return xxx_Test_ProtoFile_MessageTypes[2].KnownFieldsOf(m.m)
    +}
    +func (m xxx_Message3) UnknownFields() protoreflect.UnknownFields {
    + return xxx_Test_ProtoFile_MessageTypes[2].UnknownFieldsOf(m.m)
    +}
    +func (m xxx_Message3) Interface() protoreflect.ProtoMessage {
    + return m.m
    +}
    +
    +func (m *Message3) Reset() { *m = Message3{} }
    +func (m *Message3) String() string { return proto.CompactTextString(m) }
    +func (*Message3) ProtoMessage() {}
    +func (*Message3) Descriptor() ([]byte, []int) {
    + return fileDescriptor_3628d63611f7063d, []int{2}
    +}
    +
    +func (m *Message3) XXX_Unmarshal(b []byte) error {
    + return xxx_messageInfo_Message3.Unmarshal(m, b)
    +}
    +func (m *Message3) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    + return xxx_messageInfo_Message3.Marshal(b, m, deterministic)
    +}
    +func (m *Message3) XXX_Merge(src proto.Message) {
    + xxx_messageInfo_Message3.Merge(m, src)
    +}
    +func (m *Message3) XXX_Size() int {
    + return xxx_messageInfo_Message3.Size(m)
    +}
    +func (m *Message3) XXX_DiscardUnknown() {
    + xxx_messageInfo_Message3.DiscardUnknown(m)
    +}
    +
    +var xxx_messageInfo_Message3 proto.InternalMessageInfo
    +
    +type Message4 struct {
    + BoolField *bool `protobuf:"varint,30,opt,name=bool_field,json=boolField" json:"bool_field,omitempty"`
    + XXX_NoUnkeyedLiteral struct{} `json:"-"`
    + XXX_unrecognized []byte `json:"-"`
    + XXX_sizecache int32 `json:"-"`
    +}
    +
    +type xxx_Message4 struct{ m *Message4 }
    +
    +func (m *Message4) ProtoReflect() protoreflect.Message {
    + return xxx_Message4{m}
    +}
    +func (m xxx_Message4) Type() protoreflect.MessageType {
    + return xxx_Test_ProtoFile_MessageTypes[3].Type
    +}
    +func (m xxx_Message4) KnownFields() protoreflect.KnownFields {
    + return xxx_Test_ProtoFile_MessageTypes[3].KnownFieldsOf(m.m)
    +}
    +func (m xxx_Message4) UnknownFields() protoreflect.UnknownFields {
    + return xxx_Test_ProtoFile_MessageTypes[3].UnknownFieldsOf(m.m)
    +}
    +func (m xxx_Message4) Interface() protoreflect.ProtoMessage {
    + return m.m
    +}
    +
    +func (m *Message4) Reset() { *m = Message4{} }
    +func (m *Message4) String() string { return proto.CompactTextString(m) }
    +func (*Message4) ProtoMessage() {}
    +func (*Message4) Descriptor() ([]byte, []int) {
    + return fileDescriptor_3628d63611f7063d, []int{3}
    +}
    +
    +func (m *Message4) XXX_Unmarshal(b []byte) error {
    + return xxx_messageInfo_Message4.Unmarshal(m, b)
    +}
    +func (m *Message4) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    + return xxx_messageInfo_Message4.Marshal(b, m, deterministic)
    +}
    +func (m *Message4) XXX_Merge(src proto.Message) {
    + xxx_messageInfo_Message4.Merge(m, src)
    +}
    +func (m *Message4) XXX_Size() int {
    + return xxx_messageInfo_Message4.Size(m)
    +}
    +func (m *Message4) XXX_DiscardUnknown() {
    + xxx_messageInfo_Message4.DiscardUnknown(m)
    +}
    +
    +var xxx_messageInfo_Message4 proto.InternalMessageInfo
    +
    +func (m *Message4) GetBoolField() bool {
    + if m != nil && m.BoolField != nil {
    + return *m.BoolField
    + }
    + return false
    +}
    +
    +var E_StringField = &proto.ExtensionDesc{
    + ExtendedType: (*Message1)(nil),
    + ExtensionType: (*string)(nil),
    + Field: 11,
    + Name: "testprotos.string_field",
    + Tag: "bytes,11,opt,name=string_field",
    + Filename: "reflect/protoregistry/testprotos/test.proto",
    +}
    +
    +var E_EnumField = &proto.ExtensionDesc{
    + ExtendedType: (*Message1)(nil),
    + ExtensionType: (*Enum1)(nil),
    + Field: 12,
    + Name: "testprotos.enum_field",
    + Tag: "varint,12,opt,name=enum_field,enum=testprotos.Enum1",
    + Filename: "reflect/protoregistry/testprotos/test.proto",
    +}
    +
    +var E_MessageField = &proto.ExtensionDesc{
    + ExtendedType: (*Message1)(nil),
    + ExtensionType: (*Message2)(nil),
    + Field: 13,
    + Name: "testprotos.message_field",
    + Tag: "bytes,13,opt,name=message_field",
    + Filename: "reflect/protoregistry/testprotos/test.proto",
    +}
    +
    +var E_Message4_MessageField = &proto.ExtensionDesc{
    + ExtendedType: (*Message1)(nil),
    + ExtensionType: (*Message2)(nil),
    + Field: 21,
    + Name: "testprotos.Message4.message_field",
    + Tag: "bytes,21,opt,name=message_field",
    + Filename: "reflect/protoregistry/testprotos/test.proto",
    +}
    +
    +var E_Message4_EnumField = &proto.ExtensionDesc{
    + ExtendedType: (*Message1)(nil),
    + ExtensionType: (*Enum1)(nil),
    + Field: 22,
    + Name: "testprotos.Message4.enum_field",
    + Tag: "varint,22,opt,name=enum_field,enum=testprotos.Enum1",
    + Filename: "reflect/protoregistry/testprotos/test.proto",
    +}
    +
    +var E_Message4_StringField = &proto.ExtensionDesc{
    + ExtendedType: (*Message1)(nil),
    + ExtensionType: (*string)(nil),
    + Field: 23,
    + Name: "testprotos.Message4.string_field",
    + Tag: "bytes,23,opt,name=string_field",
    + Filename: "reflect/protoregistry/testprotos/test.proto",
    +}
    +
    +func init() {
    + proto.RegisterFile("reflect/protoregistry/testprotos/test.proto", fileDescriptor_3628d63611f7063d)
    + proto.RegisterEnum("testprotos.Enum1", Enum1_name, Enum1_value)
    + proto.RegisterEnum("testprotos.Enum2", Enum2_name, Enum2_value)
    + proto.RegisterEnum("testprotos.Enum3", Enum3_name, Enum3_value)
    + proto.RegisterType((*Message1)(nil), "testprotos.Message1")
    + proto.RegisterType((*Message2)(nil), "testprotos.Message2")
    + proto.RegisterType((*Message3)(nil), "testprotos.Message3")
    + proto.RegisterType((*Message4)(nil), "testprotos.Message4")
    + proto.RegisterExtension(E_StringField)
    + proto.RegisterExtension(E_EnumField)
    + proto.RegisterExtension(E_MessageField)
    + proto.RegisterExtension(E_Message4_MessageField)
    + proto.RegisterExtension(E_Message4_EnumField)
    + proto.RegisterExtension(E_Message4_StringField)
    +}
    +
    +var fileDescriptor_3628d63611f7063d = []byte{
    + // 304 bytes of a gzipped FileDescriptorProto
    + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2e, 0x4a, 0x4d, 0xcb,
    + 0x49, 0x4d, 0x2e, 0xd1, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x2f, 0x4a, 0x4d, 0xcf, 0x2c, 0x2e, 0x29,
    + 0xaa, 0xd4, 0x2f, 0x49, 0x2d, 0x2e, 0x01, 0x8b, 0x14, 0x83, 0x99, 0x7a, 0x60, 0xb6, 0x10, 0x17,
    + 0x42, 0x58, 0x49, 0x84, 0x8b, 0xc3, 0x37, 0xb5, 0xb8, 0x38, 0x31, 0x3d, 0xd5, 0x50, 0x8b, 0x83,
    + 0x83, 0x4b, 0xa0, 0xa1, 0xa1, 0xa1, 0x81, 0x49, 0x89, 0x0b, 0x2e, 0x6a, 0x84, 0xc4, 0x36, 0x56,
    + 0xfa, 0xcd, 0x08, 0xe7, 0x98, 0x08, 0xc9, 0x72, 0x71, 0x25, 0xe5, 0xe7, 0xe7, 0xc4, 0xa7, 0x65,
    + 0xa6, 0xe6, 0xa4, 0x48, 0xc8, 0x29, 0x30, 0x6a, 0x70, 0x04, 0x71, 0x82, 0x44, 0xdc, 0x40, 0x02,
    + 0x46, 0xfe, 0x5c, 0xbc, 0xb9, 0x10, 0xa5, 0x10, 0x15, 0x42, 0x22, 0x7a, 0x08, 0x7b, 0xf5, 0x60,
    + 0x96, 0x4a, 0x88, 0x2a, 0x30, 0x6a, 0x70, 0x1b, 0x61, 0x93, 0x33, 0x0a, 0xe2, 0x81, 0x1a, 0x00,
    + 0x31, 0xd0, 0x8d, 0x8b, 0x2b, 0x35, 0xaf, 0x34, 0x17, 0xaf, 0x69, 0x62, 0x0a, 0x8c, 0x1a, 0x7c,
    + 0x46, 0x82, 0xc8, 0x72, 0xae, 0x79, 0xa5, 0xb9, 0x86, 0x41, 0x9c, 0x20, 0xad, 0x10, 0x73, 0xcc,
    + 0xb9, 0x78, 0x8a, 0x4b, 0x8a, 0x32, 0xf3, 0xd2, 0xf1, 0x9a, 0x24, 0xae, 0xc0, 0xa8, 0xc1, 0x19,
    + 0xc4, 0x0d, 0x51, 0x09, 0xd6, 0xa8, 0x25, 0xc0, 0xc5, 0x0a, 0x36, 0x4c, 0x88, 0x9d, 0x8b, 0xd9,
    + 0xdf, 0xcf, 0x55, 0x80, 0x11, 0x26, 0x62, 0x04, 0x12, 0x09, 0xf5, 0xf3, 0x17, 0x60, 0xd4, 0xe2,
    + 0x87, 0x88, 0x18, 0x0b, 0xb1, 0x71, 0x31, 0x45, 0x7a, 0x0a, 0x30, 0x5a, 0x11, 0x67, 0x1b, 0x37,
    + 0x86, 0x6d, 0x56, 0xc4, 0x78, 0x97, 0x87, 0xb0, 0x77, 0xad, 0x88, 0x8c, 0x07, 0x5e, 0x62, 0xe3,
    + 0xc1, 0xc9, 0x21, 0xca, 0x2e, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x3f,
    + 0x3d, 0x3f, 0x27, 0x31, 0x2f, 0x1d, 0x92, 0xee, 0x92, 0x4a, 0xd3, 0xf4, 0xcb, 0x8c, 0xf4, 0x09,
    + 0xa5, 0x45, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x76, 0x64, 0x81, 0x59, 0xae, 0x02, 0x00, 0x00,
    +}
    +
    +func init() {
    + xxx_Test_ProtoFile_FileDesc.Enums = xxx_Test_ProtoFile_EnumDescs[0:3]
    + xxx_Test_ProtoFile_FileDesc.Messages = xxx_Test_ProtoFile_MessageDescs[0:4]
    + var err error
    + Test_ProtoFile, err = prototype.NewFile(&xxx_Test_ProtoFile_FileDesc)
    + if err != nil {
    + panic(err)
    + }
    +}
    +
    +const _ = protoimpl.EnforceVersion(protoimpl.Version - 0)
    +
    +var Test_ProtoFile protoreflect.FileDescriptor
    +
    +var xxx_Test_ProtoFile_FileDesc = prototype.File{
    + Syntax: protoreflect.Proto2,
    + Path: "reflect/protoregistry/testprotos/test.proto",
    + Package: "testprotos",
    +}
    +var xxx_Test_ProtoFile_EnumTypes = [3]protoreflect.EnumType{
    + prototype.GoEnum(
    + xxx_Test_ProtoFile_EnumDescs[0].Reference(),
    + func(_ protoreflect.EnumType, n protoreflect.EnumNumber) protoreflect.ProtoEnum {
    + return Enum1(n)
    + },
    + ),
    + prototype.GoEnum(
    + xxx_Test_ProtoFile_EnumDescs[1].Reference(),
    + func(_ protoreflect.EnumType, n protoreflect.EnumNumber) protoreflect.ProtoEnum {
    + return Enum2(n)
    + },
    + ),
    + prototype.GoEnum(
    + xxx_Test_ProtoFile_EnumDescs[2].Reference(),
    + func(_ protoreflect.EnumType, n protoreflect.EnumNumber) protoreflect.ProtoEnum {
    + return Enum3(n)
    + },
    + ),
    +}
    +var xxx_Test_ProtoFile_EnumDescs = [3]prototype.Enum{
    + {
    + Name: "Enum1",
    + Values: []prototype.EnumValue{
    + {Name: "ONE", Number: 1},
    + },
    + },
    + {
    + Name: "Enum2",
    + Values: []prototype.EnumValue{
    + {Name: "UNO", Number: 1},
    + },
    + },
    + {
    + Name: "Enum3",
    + Values: []prototype.EnumValue{
    + {Name: "YI", Number: 1},
    + },
    + },
    +}
    +var xxx_Test_ProtoFile_MessageTypes = [4]protoimpl.MessageType{
    + {Type: prototype.GoMessage(
    + xxx_Test_ProtoFile_MessageDescs[0].Reference(),
    + func(protoreflect.MessageType) protoreflect.ProtoMessage {
    + return new(Message1)
    + },
    + )},
    + {Type: prototype.GoMessage(
    + xxx_Test_ProtoFile_MessageDescs[1].Reference(),
    + func(protoreflect.MessageType) protoreflect.ProtoMessage {
    + return new(Message2)
    + },
    + )},
    + {Type: prototype.GoMessage(
    + xxx_Test_ProtoFile_MessageDescs[2].Reference(),
    + func(protoreflect.MessageType) protoreflect.ProtoMessage {
    + return new(Message3)
    + },
    + )},
    + {Type: prototype.GoMessage(
    + xxx_Test_ProtoFile_MessageDescs[3].Reference(),
    + func(protoreflect.MessageType) protoreflect.ProtoMessage {
    + return new(Message4)
    + },
    + )},
    +}
    +var xxx_Test_ProtoFile_MessageDescs = [4]prototype.Message{
    + {
    + Name: "Message1",
    + ExtensionRanges: [][2]protoreflect.FieldNumber{{10, 536870912}},
    + },
    + {
    + Name: "Message2",
    + },
    + {
    + Name: "Message3",
    + },
    + {
    + Name: "Message4",
    + Fields: []prototype.Field{
    + {
    + Name: "bool_field",
    + Number: 30,
    + Cardinality: protoreflect.Optional,
    + Kind: protoreflect.BoolKind,
    + JSONName: "boolField",
    + IsPacked: prototype.False,
    + },
    + },
    + },
    +}
    diff --git a/reflect/protoregistry/testprotos/test.proto b/reflect/protoregistry/testprotos/test.proto
    new file mode 100644
    index 0000000..5a4c77b
    --- /dev/null
    +++ b/reflect/protoregistry/testprotos/test.proto
    @@ -0,0 +1,45 @@
    +// 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.
    +
    +// Different proto type definitions for testing the Types registry.
    +syntax = "proto2";
    +
    +package testprotos;
    +option go_package = "github.com/golang/protobuf/v2/reflect/protoregistry/testprotos";
    +
    +message Message1 {
    + extensions 10 to max;
    +}
    +
    +message Message2 {}
    +
    +message Message3 {}
    +
    +enum Enum1 {
    + ONE = 1;
    +}
    +
    +enum Enum2 {
    + UNO = 1;
    +}
    +
    +enum Enum3 {
    + YI = 1;
    +}
    +
    +extend Message1 {
    + optional string string_field = 11;
    + optional Enum1 enum_field = 12;
    + optional Message2 message_field = 13;
    +}
    +
    +message Message4 {
    + optional bool bool_field = 30;
    +
    + extend Message1 {
    + optional Message2 message_field = 21;
    + optional Enum1 enum_field = 22;
    + optional string string_field = 23;
    + }
    +}
    diff --git a/regenerate.bash b/regenerate.bash
    index d5ae4d8..534def4 100755
    --- a/regenerate.bash
    +++ b/regenerate.bash
    @@ -53,3 +53,7 @@
    echo "# encoding/textpb/testprotos/pb?/test.proto"
    PROTOC_GEN_GO_ENABLE_REFLECT=1 protoc --go_out=paths=source_relative:. \
    encoding/textpb/testprotos/pb?/test.proto
    +
    +echo "# reflect/protoregistry/testprotos/test.proto"
    +PROTOC_GEN_GO_ENABLE_REFLECT=1 protoc --go_out=paths=source_relative:. \
    + reflect/protoregistry/testprotos/test.proto

    To view, visit change 131345. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: protobuf
    Gerrit-Branch: master
    Gerrit-Change-Id: I0d07705801684a1eb5853bcd05fcce12598a0047
    Gerrit-Change-Number: 131345
    Gerrit-PatchSet: 11
    Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
    Gerrit-Reviewer: Herbie Ong <her...@google.com>
    Gerrit-Reviewer: Joe Tsai <joe...@google.com>
    Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
    Gerrit-MessageType: merged
    Reply all
    Reply to author
    Forward
    0 new messages