rebase: vendor dependencies for Vault API

Uses github.com/libopenstorage/secrets to communicate with Vault. This
removes the need for maintaining our own limited Vault APIs.

By adding the new dependency, several other packages got updated in the
process. Unused indirect dependencies have been removed from go.mod.

Signed-off-by: Niels de Vos <ndevos@redhat.com>
This commit is contained in:
Niels de Vos
2020-11-19 08:52:04 +01:00
committed by mergify[bot]
parent 7824cb5ed7
commit 91774fc936
618 changed files with 80427 additions and 31593 deletions

View File

@ -0,0 +1,275 @@
// 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 protodesc provides functionality for converting
// FileDescriptorProto messages to/from protoreflect.FileDescriptor values.
//
// The google.protobuf.FileDescriptorProto is a protobuf message that describes
// the type information for a .proto file in a form that is easily serializable.
// The protoreflect.FileDescriptor is a more structured representation of
// the FileDescriptorProto message where references and remote dependencies
// can be directly followed.
package protodesc
import (
"google.golang.org/protobuf/internal/errors"
"google.golang.org/protobuf/internal/filedesc"
"google.golang.org/protobuf/internal/pragma"
"google.golang.org/protobuf/internal/strs"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/descriptorpb"
)
// Resolver is the resolver used by NewFile to resolve dependencies.
// The enums and messages provided must belong to some parent file,
// which is also registered.
//
// It is implemented by protoregistry.Files.
type Resolver interface {
FindFileByPath(string) (protoreflect.FileDescriptor, error)
FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
}
// FileOptions configures the construction of file descriptors.
type FileOptions struct {
pragma.NoUnkeyedLiterals
// AllowUnresolvable configures New to permissively allow unresolvable
// file, enum, or message dependencies. Unresolved dependencies are replaced
// by placeholder equivalents.
//
// The following dependencies may be left unresolved:
// • Resolving an imported file.
// • Resolving the type for a message field or extension field.
// If the kind of the field is unknown, then a placeholder is used for both
// the Enum and Message accessors on the protoreflect.FieldDescriptor.
// • Resolving an enum value set as the default for an optional enum field.
// If unresolvable, the protoreflect.FieldDescriptor.Default is set to the
// first value in the associated enum (or zero if the also enum dependency
// is also unresolvable). The protoreflect.FieldDescriptor.DefaultEnumValue
// is populated with a placeholder.
// • Resolving the extended message type for an extension field.
// • Resolving the input or output message type for a service method.
//
// If the unresolved dependency uses a relative name,
// then the placeholder will contain an invalid FullName with a "*." prefix,
// indicating that the starting prefix of the full name is unknown.
AllowUnresolvable bool
}
// NewFile creates a new protoreflect.FileDescriptor from the provided
// file descriptor message. See FileOptions.New for more information.
func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
return FileOptions{}.New(fd, r)
}
// NewFiles creates a new protoregistry.Files from the provided
// FileDescriptorSet message. See FileOptions.NewFiles for more information.
func NewFiles(fd *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error) {
return FileOptions{}.NewFiles(fd)
}
// New creates a new protoreflect.FileDescriptor from the provided
// file descriptor message. The file must represent a valid proto file according
// to protobuf semantics. The returned descriptor is a deep copy of the input.
//
// Any imported files, enum types, or message types referenced in the file are
// resolved using the provided registry. When looking up an import file path,
// the path must be unique. The newly created file descriptor is not registered
// back into the provided file registry.
func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
if r == nil {
r = (*protoregistry.Files)(nil) // empty resolver
}
// Handle the file descriptor content.
f := &filedesc.File{L2: &filedesc.FileL2{}}
switch fd.GetSyntax() {
case "proto2", "":
f.L1.Syntax = protoreflect.Proto2
case "proto3":
f.L1.Syntax = protoreflect.Proto3
default:
return nil, errors.New("invalid syntax: %q", fd.GetSyntax())
}
f.L1.Path = fd.GetName()
if f.L1.Path == "" {
return nil, errors.New("file path must be populated")
}
f.L1.Package = protoreflect.FullName(fd.GetPackage())
if !f.L1.Package.IsValid() && f.L1.Package != "" {
return nil, errors.New("invalid package: %q", f.L1.Package)
}
if opts := fd.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.FileOptions)
f.L2.Options = func() protoreflect.ProtoMessage { return opts }
}
f.L2.Imports = make(filedesc.FileImports, len(fd.GetDependency()))
for _, i := range fd.GetPublicDependency() {
if !(0 <= i && int(i) < len(f.L2.Imports)) || f.L2.Imports[i].IsPublic {
return nil, errors.New("invalid or duplicate public import index: %d", i)
}
f.L2.Imports[i].IsPublic = true
}
for _, i := range fd.GetWeakDependency() {
if !(0 <= i && int(i) < len(f.L2.Imports)) || f.L2.Imports[i].IsWeak {
return nil, errors.New("invalid or duplicate weak import index: %d", i)
}
f.L2.Imports[i].IsWeak = true
}
imps := importSet{f.Path(): true}
for i, path := range fd.GetDependency() {
imp := &f.L2.Imports[i]
f, err := r.FindFileByPath(path)
if err == protoregistry.NotFound && (o.AllowUnresolvable || imp.IsWeak) {
f = filedesc.PlaceholderFile(path)
} else if err != nil {
return nil, errors.New("could not resolve import %q: %v", path, err)
}
imp.FileDescriptor = f
if imps[imp.Path()] {
return nil, errors.New("already imported %q", path)
}
imps[imp.Path()] = true
}
for i := range fd.GetDependency() {
imp := &f.L2.Imports[i]
imps.importPublic(imp.Imports())
}
// Handle source locations.
for _, loc := range fd.GetSourceCodeInfo().GetLocation() {
var l protoreflect.SourceLocation
// TODO: Validate that the path points to an actual declaration?
l.Path = protoreflect.SourcePath(loc.GetPath())
s := loc.GetSpan()
switch len(s) {
case 3:
l.StartLine, l.StartColumn, l.EndLine, l.EndColumn = int(s[0]), int(s[1]), int(s[0]), int(s[2])
case 4:
l.StartLine, l.StartColumn, l.EndLine, l.EndColumn = int(s[0]), int(s[1]), int(s[2]), int(s[3])
default:
return nil, errors.New("invalid span: %v", s)
}
// TODO: Validate that the span information is sensible?
// See https://github.com/protocolbuffers/protobuf/issues/6378.
if false && (l.EndLine < l.StartLine || l.StartLine < 0 || l.StartColumn < 0 || l.EndColumn < 0 ||
(l.StartLine == l.EndLine && l.EndColumn <= l.StartColumn)) {
return nil, errors.New("invalid span: %v", s)
}
l.LeadingDetachedComments = loc.GetLeadingDetachedComments()
l.LeadingComments = loc.GetLeadingComments()
l.TrailingComments = loc.GetTrailingComments()
f.L2.Locations.List = append(f.L2.Locations.List, l)
}
// Step 1: Allocate and derive the names for all declarations.
// This copies all fields from the descriptor proto except:
// google.protobuf.FieldDescriptorProto.type_name
// google.protobuf.FieldDescriptorProto.default_value
// google.protobuf.FieldDescriptorProto.oneof_index
// google.protobuf.FieldDescriptorProto.extendee
// google.protobuf.MethodDescriptorProto.input
// google.protobuf.MethodDescriptorProto.output
var err error
sb := new(strs.Builder)
r1 := make(descsByName)
if f.L1.Enums.List, err = r1.initEnumDeclarations(fd.GetEnumType(), f, sb); err != nil {
return nil, err
}
if f.L1.Messages.List, err = r1.initMessagesDeclarations(fd.GetMessageType(), f, sb); err != nil {
return nil, err
}
if f.L1.Extensions.List, err = r1.initExtensionDeclarations(fd.GetExtension(), f, sb); err != nil {
return nil, err
}
if f.L1.Services.List, err = r1.initServiceDeclarations(fd.GetService(), f, sb); err != nil {
return nil, err
}
// Step 2: Resolve every dependency reference not handled by step 1.
r2 := &resolver{local: r1, remote: r, imports: imps, allowUnresolvable: o.AllowUnresolvable}
if err := r2.resolveMessageDependencies(f.L1.Messages.List, fd.GetMessageType()); err != nil {
return nil, err
}
if err := r2.resolveExtensionDependencies(f.L1.Extensions.List, fd.GetExtension()); err != nil {
return nil, err
}
if err := r2.resolveServiceDependencies(f.L1.Services.List, fd.GetService()); err != nil {
return nil, err
}
// Step 3: Validate every enum, message, and extension declaration.
if err := validateEnumDeclarations(f.L1.Enums.List, fd.GetEnumType()); err != nil {
return nil, err
}
if err := validateMessageDeclarations(f.L1.Messages.List, fd.GetMessageType()); err != nil {
return nil, err
}
if err := validateExtensionDeclarations(f.L1.Extensions.List, fd.GetExtension()); err != nil {
return nil, err
}
return f, nil
}
type importSet map[string]bool
func (is importSet) importPublic(imps protoreflect.FileImports) {
for i := 0; i < imps.Len(); i++ {
if imp := imps.Get(i); imp.IsPublic {
is[imp.Path()] = true
is.importPublic(imp.Imports())
}
}
}
// NewFiles creates a new protoregistry.Files from the provided
// FileDescriptorSet message. The descriptor set must include only
// valid files according to protobuf semantics. The returned descriptors
// are a deep copy of the input.
func (o FileOptions) NewFiles(fds *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error) {
files := make(map[string]*descriptorpb.FileDescriptorProto)
for _, fd := range fds.File {
if _, ok := files[fd.GetName()]; ok {
return nil, errors.New("file appears multiple times: %q", fd.GetName())
}
files[fd.GetName()] = fd
}
r := &protoregistry.Files{}
for _, fd := range files {
if err := o.addFileDeps(r, fd, files); err != nil {
return nil, err
}
}
return r, nil
}
func (o FileOptions) addFileDeps(r *protoregistry.Files, fd *descriptorpb.FileDescriptorProto, files map[string]*descriptorpb.FileDescriptorProto) error {
// Set the entry to nil while descending into a file's dependencies to detect cycles.
files[fd.GetName()] = nil
for _, dep := range fd.Dependency {
depfd, ok := files[dep]
if depfd == nil {
if ok {
return errors.New("import cycle in file: %q", dep)
}
continue
}
if err := o.addFileDeps(r, depfd, files); err != nil {
return err
}
}
// Delete the entry once dependencies are processed.
delete(files, fd.GetName())
f, err := o.New(fd, r)
if err != nil {
return err
}
return r.RegisterFile(f)
}

View File

@ -0,0 +1,248 @@
// Copyright 2019 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 protodesc
import (
"google.golang.org/protobuf/internal/errors"
"google.golang.org/protobuf/internal/filedesc"
"google.golang.org/protobuf/internal/strs"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
)
type descsByName map[protoreflect.FullName]protoreflect.Descriptor
func (r descsByName) initEnumDeclarations(eds []*descriptorpb.EnumDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (es []filedesc.Enum, err error) {
es = make([]filedesc.Enum, len(eds)) // allocate up-front to ensure stable pointers
for i, ed := range eds {
e := &es[i]
e.L2 = new(filedesc.EnumL2)
if e.L0, err = r.makeBase(e, parent, ed.GetName(), i, sb); err != nil {
return nil, err
}
if opts := ed.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.EnumOptions)
e.L2.Options = func() protoreflect.ProtoMessage { return opts }
}
for _, s := range ed.GetReservedName() {
e.L2.ReservedNames.List = append(e.L2.ReservedNames.List, protoreflect.Name(s))
}
for _, rr := range ed.GetReservedRange() {
e.L2.ReservedRanges.List = append(e.L2.ReservedRanges.List, [2]protoreflect.EnumNumber{
protoreflect.EnumNumber(rr.GetStart()),
protoreflect.EnumNumber(rr.GetEnd()),
})
}
if e.L2.Values.List, err = r.initEnumValuesFromDescriptorProto(ed.GetValue(), e, sb); err != nil {
return nil, err
}
}
return es, nil
}
func (r descsByName) initEnumValuesFromDescriptorProto(vds []*descriptorpb.EnumValueDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (vs []filedesc.EnumValue, err error) {
vs = make([]filedesc.EnumValue, len(vds)) // allocate up-front to ensure stable pointers
for i, vd := range vds {
v := &vs[i]
if v.L0, err = r.makeBase(v, parent, vd.GetName(), i, sb); err != nil {
return nil, err
}
if opts := vd.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.EnumValueOptions)
v.L1.Options = func() protoreflect.ProtoMessage { return opts }
}
v.L1.Number = protoreflect.EnumNumber(vd.GetNumber())
}
return vs, nil
}
func (r descsByName) initMessagesDeclarations(mds []*descriptorpb.DescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (ms []filedesc.Message, err error) {
ms = make([]filedesc.Message, len(mds)) // allocate up-front to ensure stable pointers
for i, md := range mds {
m := &ms[i]
m.L2 = new(filedesc.MessageL2)
if m.L0, err = r.makeBase(m, parent, md.GetName(), i, sb); err != nil {
return nil, err
}
if opts := md.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.MessageOptions)
m.L2.Options = func() protoreflect.ProtoMessage { return opts }
m.L1.IsMapEntry = opts.GetMapEntry()
m.L1.IsMessageSet = opts.GetMessageSetWireFormat()
}
for _, s := range md.GetReservedName() {
m.L2.ReservedNames.List = append(m.L2.ReservedNames.List, protoreflect.Name(s))
}
for _, rr := range md.GetReservedRange() {
m.L2.ReservedRanges.List = append(m.L2.ReservedRanges.List, [2]protoreflect.FieldNumber{
protoreflect.FieldNumber(rr.GetStart()),
protoreflect.FieldNumber(rr.GetEnd()),
})
}
for _, xr := range md.GetExtensionRange() {
m.L2.ExtensionRanges.List = append(m.L2.ExtensionRanges.List, [2]protoreflect.FieldNumber{
protoreflect.FieldNumber(xr.GetStart()),
protoreflect.FieldNumber(xr.GetEnd()),
})
var optsFunc func() protoreflect.ProtoMessage
if opts := xr.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.ExtensionRangeOptions)
optsFunc = func() protoreflect.ProtoMessage { return opts }
}
m.L2.ExtensionRangeOptions = append(m.L2.ExtensionRangeOptions, optsFunc)
}
if m.L2.Fields.List, err = r.initFieldsFromDescriptorProto(md.GetField(), m, sb); err != nil {
return nil, err
}
if m.L2.Oneofs.List, err = r.initOneofsFromDescriptorProto(md.GetOneofDecl(), m, sb); err != nil {
return nil, err
}
if m.L1.Enums.List, err = r.initEnumDeclarations(md.GetEnumType(), m, sb); err != nil {
return nil, err
}
if m.L1.Messages.List, err = r.initMessagesDeclarations(md.GetNestedType(), m, sb); err != nil {
return nil, err
}
if m.L1.Extensions.List, err = r.initExtensionDeclarations(md.GetExtension(), m, sb); err != nil {
return nil, err
}
}
return ms, nil
}
func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (fs []filedesc.Field, err error) {
fs = make([]filedesc.Field, len(fds)) // allocate up-front to ensure stable pointers
for i, fd := range fds {
f := &fs[i]
if f.L0, err = r.makeBase(f, parent, fd.GetName(), i, sb); err != nil {
return nil, err
}
f.L1.IsProto3Optional = fd.GetProto3Optional()
if opts := fd.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.FieldOptions)
f.L1.Options = func() protoreflect.ProtoMessage { return opts }
f.L1.IsWeak = opts.GetWeak()
f.L1.HasPacked = opts.Packed != nil
f.L1.IsPacked = opts.GetPacked()
}
f.L1.Number = protoreflect.FieldNumber(fd.GetNumber())
f.L1.Cardinality = protoreflect.Cardinality(fd.GetLabel())
if fd.Type != nil {
f.L1.Kind = protoreflect.Kind(fd.GetType())
}
if fd.JsonName != nil {
f.L1.JSONName.Init(fd.GetJsonName())
}
}
return fs, nil
}
func (r descsByName) initOneofsFromDescriptorProto(ods []*descriptorpb.OneofDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (os []filedesc.Oneof, err error) {
os = make([]filedesc.Oneof, len(ods)) // allocate up-front to ensure stable pointers
for i, od := range ods {
o := &os[i]
if o.L0, err = r.makeBase(o, parent, od.GetName(), i, sb); err != nil {
return nil, err
}
if opts := od.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.OneofOptions)
o.L1.Options = func() protoreflect.ProtoMessage { return opts }
}
}
return os, nil
}
func (r descsByName) initExtensionDeclarations(xds []*descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (xs []filedesc.Extension, err error) {
xs = make([]filedesc.Extension, len(xds)) // allocate up-front to ensure stable pointers
for i, xd := range xds {
x := &xs[i]
x.L2 = new(filedesc.ExtensionL2)
if x.L0, err = r.makeBase(x, parent, xd.GetName(), i, sb); err != nil {
return nil, err
}
if opts := xd.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.FieldOptions)
x.L2.Options = func() protoreflect.ProtoMessage { return opts }
x.L2.IsPacked = opts.GetPacked()
}
x.L1.Number = protoreflect.FieldNumber(xd.GetNumber())
x.L1.Cardinality = protoreflect.Cardinality(xd.GetLabel())
if xd.Type != nil {
x.L1.Kind = protoreflect.Kind(xd.GetType())
}
if xd.JsonName != nil {
x.L2.JSONName.Init(xd.GetJsonName())
}
}
return xs, nil
}
func (r descsByName) initServiceDeclarations(sds []*descriptorpb.ServiceDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (ss []filedesc.Service, err error) {
ss = make([]filedesc.Service, len(sds)) // allocate up-front to ensure stable pointers
for i, sd := range sds {
s := &ss[i]
s.L2 = new(filedesc.ServiceL2)
if s.L0, err = r.makeBase(s, parent, sd.GetName(), i, sb); err != nil {
return nil, err
}
if opts := sd.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.ServiceOptions)
s.L2.Options = func() protoreflect.ProtoMessage { return opts }
}
if s.L2.Methods.List, err = r.initMethodsFromDescriptorProto(sd.GetMethod(), s, sb); err != nil {
return nil, err
}
}
return ss, nil
}
func (r descsByName) initMethodsFromDescriptorProto(mds []*descriptorpb.MethodDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (ms []filedesc.Method, err error) {
ms = make([]filedesc.Method, len(mds)) // allocate up-front to ensure stable pointers
for i, md := range mds {
m := &ms[i]
if m.L0, err = r.makeBase(m, parent, md.GetName(), i, sb); err != nil {
return nil, err
}
if opts := md.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.MethodOptions)
m.L1.Options = func() protoreflect.ProtoMessage { return opts }
}
m.L1.IsStreamingClient = md.GetClientStreaming()
m.L1.IsStreamingServer = md.GetServerStreaming()
}
return ms, nil
}
func (r descsByName) makeBase(child, parent protoreflect.Descriptor, name string, idx int, sb *strs.Builder) (filedesc.BaseL0, error) {
if !protoreflect.Name(name).IsValid() {
return filedesc.BaseL0{}, errors.New("descriptor %q has an invalid nested name: %q", parent.FullName(), name)
}
// Derive the full name of the child.
// Note that enum values are a sibling to the enum parent in the namespace.
var fullName protoreflect.FullName
if _, ok := parent.(protoreflect.EnumDescriptor); ok {
fullName = sb.AppendFullName(parent.FullName().Parent(), protoreflect.Name(name))
} else {
fullName = sb.AppendFullName(parent.FullName(), protoreflect.Name(name))
}
if _, ok := r[fullName]; ok {
return filedesc.BaseL0{}, errors.New("descriptor %q already declared", fullName)
}
r[fullName] = child
// TODO: Verify that the full name does not already exist in the resolver?
// This is not as critical since most usages of NewFile will register
// the created file back into the registry, which will perform this check.
return filedesc.BaseL0{
FullName: fullName,
ParentFile: parent.ParentFile().(*filedesc.File),
Parent: parent,
Index: idx,
}, nil
}

View File

@ -0,0 +1,286 @@
// Copyright 2019 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 protodesc
import (
"google.golang.org/protobuf/internal/encoding/defval"
"google.golang.org/protobuf/internal/errors"
"google.golang.org/protobuf/internal/filedesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/descriptorpb"
)
// resolver is a wrapper around a local registry of declarations within the file
// and the remote resolver. The remote resolver is restricted to only return
// descriptors that have been imported.
type resolver struct {
local descsByName
remote Resolver
imports importSet
allowUnresolvable bool
}
func (r *resolver) resolveMessageDependencies(ms []filedesc.Message, mds []*descriptorpb.DescriptorProto) (err error) {
for i, md := range mds {
m := &ms[i]
for j, fd := range md.GetField() {
f := &m.L2.Fields.List[j]
if f.L1.Cardinality == protoreflect.Required {
m.L2.RequiredNumbers.List = append(m.L2.RequiredNumbers.List, f.L1.Number)
}
if fd.OneofIndex != nil {
k := int(fd.GetOneofIndex())
if !(0 <= k && k < len(md.GetOneofDecl())) {
return errors.New("message field %q has an invalid oneof index: %d", f.FullName(), k)
}
o := &m.L2.Oneofs.List[k]
f.L1.ContainingOneof = o
o.L1.Fields.List = append(o.L1.Fields.List, f)
}
if f.L1.Kind, f.L1.Enum, f.L1.Message, err = r.findTarget(f.Kind(), f.Parent().FullName(), partialName(fd.GetTypeName()), f.IsWeak()); err != nil {
return errors.New("message field %q cannot resolve type: %v", f.FullName(), err)
}
if fd.DefaultValue != nil {
v, ev, err := unmarshalDefault(fd.GetDefaultValue(), f, r.allowUnresolvable)
if err != nil {
return errors.New("message field %q has invalid default: %v", f.FullName(), err)
}
f.L1.Default = filedesc.DefaultValue(v, ev)
}
}
if err := r.resolveMessageDependencies(m.L1.Messages.List, md.GetNestedType()); err != nil {
return err
}
if err := r.resolveExtensionDependencies(m.L1.Extensions.List, md.GetExtension()); err != nil {
return err
}
}
return nil
}
func (r *resolver) resolveExtensionDependencies(xs []filedesc.Extension, xds []*descriptorpb.FieldDescriptorProto) (err error) {
for i, xd := range xds {
x := &xs[i]
if x.L1.Extendee, err = r.findMessageDescriptor(x.Parent().FullName(), partialName(xd.GetExtendee()), false); err != nil {
return errors.New("extension field %q cannot resolve extendee: %v", x.FullName(), err)
}
if x.L1.Kind, x.L2.Enum, x.L2.Message, err = r.findTarget(x.Kind(), x.Parent().FullName(), partialName(xd.GetTypeName()), false); err != nil {
return errors.New("extension field %q cannot resolve type: %v", x.FullName(), err)
}
if xd.DefaultValue != nil {
v, ev, err := unmarshalDefault(xd.GetDefaultValue(), x, r.allowUnresolvable)
if err != nil {
return errors.New("extension field %q has invalid default: %v", x.FullName(), err)
}
x.L2.Default = filedesc.DefaultValue(v, ev)
}
}
return nil
}
func (r *resolver) resolveServiceDependencies(ss []filedesc.Service, sds []*descriptorpb.ServiceDescriptorProto) (err error) {
for i, sd := range sds {
s := &ss[i]
for j, md := range sd.GetMethod() {
m := &s.L2.Methods.List[j]
m.L1.Input, err = r.findMessageDescriptor(m.Parent().FullName(), partialName(md.GetInputType()), false)
if err != nil {
return errors.New("service method %q cannot resolve input: %v", m.FullName(), err)
}
m.L1.Output, err = r.findMessageDescriptor(s.FullName(), partialName(md.GetOutputType()), false)
if err != nil {
return errors.New("service method %q cannot resolve output: %v", m.FullName(), err)
}
}
}
return nil
}
// findTarget finds an enum or message descriptor if k is an enum, message,
// group, or unknown. If unknown, and the name could be resolved, the kind
// returned kind is set based on the type of the resolved descriptor.
func (r *resolver) findTarget(k protoreflect.Kind, scope protoreflect.FullName, ref partialName, isWeak bool) (protoreflect.Kind, protoreflect.EnumDescriptor, protoreflect.MessageDescriptor, error) {
switch k {
case protoreflect.EnumKind:
ed, err := r.findEnumDescriptor(scope, ref, isWeak)
if err != nil {
return 0, nil, nil, err
}
return k, ed, nil, nil
case protoreflect.MessageKind, protoreflect.GroupKind:
md, err := r.findMessageDescriptor(scope, ref, isWeak)
if err != nil {
return 0, nil, nil, err
}
return k, nil, md, nil
case 0:
// Handle unspecified kinds (possible with parsers that operate
// on a per-file basis without knowledge of dependencies).
d, err := r.findDescriptor(scope, ref)
if err == protoregistry.NotFound && (r.allowUnresolvable || isWeak) {
return k, filedesc.PlaceholderEnum(ref.FullName()), filedesc.PlaceholderMessage(ref.FullName()), nil
} else if err == protoregistry.NotFound {
return 0, nil, nil, errors.New("%q not found", ref.FullName())
} else if err != nil {
return 0, nil, nil, err
}
switch d := d.(type) {
case protoreflect.EnumDescriptor:
return protoreflect.EnumKind, d, nil, nil
case protoreflect.MessageDescriptor:
return protoreflect.MessageKind, nil, d, nil
default:
return 0, nil, nil, errors.New("unknown kind")
}
default:
if ref != "" {
return 0, nil, nil, errors.New("target name cannot be specified for %v", k)
}
if !k.IsValid() {
return 0, nil, nil, errors.New("invalid kind: %d", k)
}
return k, nil, nil, nil
}
}
// findDescriptor finds the descriptor by name,
// which may be a relative name within some scope.
//
// Suppose the scope was "fizz.buzz" and the reference was "Foo.Bar",
// then the following full names are searched:
// * fizz.buzz.Foo.Bar
// * fizz.Foo.Bar
// * Foo.Bar
func (r *resolver) findDescriptor(scope protoreflect.FullName, ref partialName) (protoreflect.Descriptor, error) {
if !ref.IsValid() {
return nil, errors.New("invalid name reference: %q", ref)
}
if ref.IsFull() {
scope, ref = "", ref[1:]
}
var foundButNotImported protoreflect.Descriptor
for {
// Derive the full name to search.
s := protoreflect.FullName(ref)
if scope != "" {
s = scope + "." + s
}
// Check the current file for the descriptor.
if d, ok := r.local[s]; ok {
return d, nil
}
// Check the remote registry for the descriptor.
d, err := r.remote.FindDescriptorByName(s)
if err == nil {
// Only allow descriptors covered by one of the imports.
if r.imports[d.ParentFile().Path()] {
return d, nil
}
foundButNotImported = d
} else if err != protoregistry.NotFound {
return nil, errors.Wrap(err, "%q", s)
}
// Continue on at a higher level of scoping.
if scope == "" {
if d := foundButNotImported; d != nil {
return nil, errors.New("resolved %q, but %q is not imported", d.FullName(), d.ParentFile().Path())
}
return nil, protoregistry.NotFound
}
scope = scope.Parent()
}
}
func (r *resolver) findEnumDescriptor(scope protoreflect.FullName, ref partialName, isWeak bool) (protoreflect.EnumDescriptor, error) {
d, err := r.findDescriptor(scope, ref)
if err == protoregistry.NotFound && (r.allowUnresolvable || isWeak) {
return filedesc.PlaceholderEnum(ref.FullName()), nil
} else if err == protoregistry.NotFound {
return nil, errors.New("%q not found", ref.FullName())
} else if err != nil {
return nil, err
}
ed, ok := d.(protoreflect.EnumDescriptor)
if !ok {
return nil, errors.New("resolved %q, but it is not an enum", d.FullName())
}
return ed, nil
}
func (r *resolver) findMessageDescriptor(scope protoreflect.FullName, ref partialName, isWeak bool) (protoreflect.MessageDescriptor, error) {
d, err := r.findDescriptor(scope, ref)
if err == protoregistry.NotFound && (r.allowUnresolvable || isWeak) {
return filedesc.PlaceholderMessage(ref.FullName()), nil
} else if err == protoregistry.NotFound {
return nil, errors.New("%q not found", ref.FullName())
} else if err != nil {
return nil, err
}
md, ok := d.(protoreflect.MessageDescriptor)
if !ok {
return nil, errors.New("resolved %q, but it is not an message", d.FullName())
}
return md, nil
}
// partialName is the partial name. A leading dot means that the name is full,
// otherwise the name is relative to some current scope.
// See google.protobuf.FieldDescriptorProto.type_name.
type partialName string
func (s partialName) IsFull() bool {
return len(s) > 0 && s[0] == '.'
}
func (s partialName) IsValid() bool {
if s.IsFull() {
return protoreflect.FullName(s[1:]).IsValid()
}
return protoreflect.FullName(s).IsValid()
}
const unknownPrefix = "*."
// FullName converts the partial name to a full name on a best-effort basis.
// If relative, it creates an invalid full name, using a "*." prefix
// to indicate that the start of the full name is unknown.
func (s partialName) FullName() protoreflect.FullName {
if s.IsFull() {
return protoreflect.FullName(s[1:])
}
return protoreflect.FullName(unknownPrefix + s)
}
func unmarshalDefault(s string, fd protoreflect.FieldDescriptor, allowUnresolvable bool) (protoreflect.Value, protoreflect.EnumValueDescriptor, error) {
var evs protoreflect.EnumValueDescriptors
if fd.Enum() != nil {
evs = fd.Enum().Values()
}
v, ev, err := defval.Unmarshal(s, fd.Kind(), evs, defval.Descriptor)
if err != nil && allowUnresolvable && evs != nil && protoreflect.Name(s).IsValid() {
v = protoreflect.ValueOfEnum(0)
if evs.Len() > 0 {
v = protoreflect.ValueOfEnum(evs.Get(0).Number())
}
ev = filedesc.PlaceholderEnumValue(fd.Enum().FullName().Parent().Append(protoreflect.Name(s)))
} else if err != nil {
return v, ev, err
}
if fd.Syntax() == protoreflect.Proto3 {
return v, ev, errors.New("cannot be specified under proto3 semantics")
}
if fd.Kind() == protoreflect.MessageKind || fd.Kind() == protoreflect.GroupKind || fd.Cardinality() == protoreflect.Repeated {
return v, ev, errors.New("cannot be specified on composite types")
}
return v, ev, nil
}

View File

@ -0,0 +1,371 @@
// Copyright 2019 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 protodesc
import (
"strings"
"unicode"
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/internal/errors"
"google.golang.org/protobuf/internal/filedesc"
"google.golang.org/protobuf/internal/flags"
"google.golang.org/protobuf/internal/genid"
"google.golang.org/protobuf/internal/strs"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
)
func validateEnumDeclarations(es []filedesc.Enum, eds []*descriptorpb.EnumDescriptorProto) error {
for i, ed := range eds {
e := &es[i]
if err := e.L2.ReservedNames.CheckValid(); err != nil {
return errors.New("enum %q reserved names has %v", e.FullName(), err)
}
if err := e.L2.ReservedRanges.CheckValid(); err != nil {
return errors.New("enum %q reserved ranges has %v", e.FullName(), err)
}
if len(ed.GetValue()) == 0 {
return errors.New("enum %q must contain at least one value declaration", e.FullName())
}
allowAlias := ed.GetOptions().GetAllowAlias()
foundAlias := false
for i := 0; i < e.Values().Len(); i++ {
v1 := e.Values().Get(i)
if v2 := e.Values().ByNumber(v1.Number()); v1 != v2 {
foundAlias = true
if !allowAlias {
return errors.New("enum %q has conflicting non-aliased values on number %d: %q with %q", e.FullName(), v1.Number(), v1.Name(), v2.Name())
}
}
}
if allowAlias && !foundAlias {
return errors.New("enum %q allows aliases, but none were found", e.FullName())
}
if e.Syntax() == protoreflect.Proto3 {
if v := e.Values().Get(0); v.Number() != 0 {
return errors.New("enum %q using proto3 semantics must have zero number for the first value", v.FullName())
}
// Verify that value names in proto3 do not conflict if the
// case-insensitive prefix is removed.
// See protoc v3.8.0: src/google/protobuf/descriptor.cc:4991-5055
names := map[string]protoreflect.EnumValueDescriptor{}
prefix := strings.Replace(strings.ToLower(string(e.Name())), "_", "", -1)
for i := 0; i < e.Values().Len(); i++ {
v1 := e.Values().Get(i)
s := strs.EnumValueName(strs.TrimEnumPrefix(string(v1.Name()), prefix))
if v2, ok := names[s]; ok && v1.Number() != v2.Number() {
return errors.New("enum %q using proto3 semantics has conflict: %q with %q", e.FullName(), v1.Name(), v2.Name())
}
names[s] = v1
}
}
for j, vd := range ed.GetValue() {
v := &e.L2.Values.List[j]
if vd.Number == nil {
return errors.New("enum value %q must have a specified number", v.FullName())
}
if e.L2.ReservedNames.Has(v.Name()) {
return errors.New("enum value %q must not use reserved name", v.FullName())
}
if e.L2.ReservedRanges.Has(v.Number()) {
return errors.New("enum value %q must not use reserved number %d", v.FullName(), v.Number())
}
}
}
return nil
}
func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.DescriptorProto) error {
for i, md := range mds {
m := &ms[i]
// Handle the message descriptor itself.
isMessageSet := md.GetOptions().GetMessageSetWireFormat()
if err := m.L2.ReservedNames.CheckValid(); err != nil {
return errors.New("message %q reserved names has %v", m.FullName(), err)
}
if err := m.L2.ReservedRanges.CheckValid(isMessageSet); err != nil {
return errors.New("message %q reserved ranges has %v", m.FullName(), err)
}
if err := m.L2.ExtensionRanges.CheckValid(isMessageSet); err != nil {
return errors.New("message %q extension ranges has %v", m.FullName(), err)
}
if err := (*filedesc.FieldRanges).CheckOverlap(&m.L2.ReservedRanges, &m.L2.ExtensionRanges); err != nil {
return errors.New("message %q reserved and extension ranges has %v", m.FullName(), err)
}
for i := 0; i < m.Fields().Len(); i++ {
f1 := m.Fields().Get(i)
if f2 := m.Fields().ByNumber(f1.Number()); f1 != f2 {
return errors.New("message %q has conflicting fields: %q with %q", m.FullName(), f1.Name(), f2.Name())
}
}
if isMessageSet && !flags.ProtoLegacy {
return errors.New("message %q is a MessageSet, which is a legacy proto1 feature that is no longer supported", m.FullName())
}
if isMessageSet && (m.Syntax() != protoreflect.Proto2 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) {
return errors.New("message %q is an invalid proto1 MessageSet", m.FullName())
}
if m.Syntax() == protoreflect.Proto3 {
if m.ExtensionRanges().Len() > 0 {
return errors.New("message %q using proto3 semantics cannot have extension ranges", m.FullName())
}
// Verify that field names in proto3 do not conflict if lowercased
// with all underscores removed.
// See protoc v3.8.0: src/google/protobuf/descriptor.cc:5830-5847
names := map[string]protoreflect.FieldDescriptor{}
for i := 0; i < m.Fields().Len(); i++ {
f1 := m.Fields().Get(i)
s := strings.Replace(strings.ToLower(string(f1.Name())), "_", "", -1)
if f2, ok := names[s]; ok {
return errors.New("message %q using proto3 semantics has conflict: %q with %q", m.FullName(), f1.Name(), f2.Name())
}
names[s] = f1
}
}
for j, fd := range md.GetField() {
f := &m.L2.Fields.List[j]
if m.L2.ReservedNames.Has(f.Name()) {
return errors.New("message field %q must not use reserved name", f.FullName())
}
if !f.Number().IsValid() {
return errors.New("message field %q has an invalid number: %d", f.FullName(), f.Number())
}
if !f.Cardinality().IsValid() {
return errors.New("message field %q has an invalid cardinality: %d", f.FullName(), f.Cardinality())
}
if m.L2.ReservedRanges.Has(f.Number()) {
return errors.New("message field %q must not use reserved number %d", f.FullName(), f.Number())
}
if m.L2.ExtensionRanges.Has(f.Number()) {
return errors.New("message field %q with number %d in extension range", f.FullName(), f.Number())
}
if fd.Extendee != nil {
return errors.New("message field %q may not have extendee: %q", f.FullName(), fd.GetExtendee())
}
if f.L1.IsProto3Optional {
if f.Syntax() != protoreflect.Proto3 {
return errors.New("message field %q under proto3 optional semantics must be specified in the proto3 syntax", f.FullName())
}
if f.Cardinality() != protoreflect.Optional {
return errors.New("message field %q under proto3 optional semantics must have optional cardinality", f.FullName())
}
if f.ContainingOneof() != nil && f.ContainingOneof().Fields().Len() != 1 {
return errors.New("message field %q under proto3 optional semantics must be within a single element oneof", f.FullName())
}
}
if f.IsWeak() && !flags.ProtoLegacy {
return errors.New("message field %q is a weak field, which is a legacy proto1 feature that is no longer supported", f.FullName())
}
if f.IsWeak() && (f.Syntax() != protoreflect.Proto2 || !isOptionalMessage(f) || f.ContainingOneof() != nil) {
return errors.New("message field %q may only be weak for an optional message", f.FullName())
}
if f.IsPacked() && !isPackable(f) {
return errors.New("message field %q is not packable", f.FullName())
}
if err := checkValidGroup(f); err != nil {
return errors.New("message field %q is an invalid group: %v", f.FullName(), err)
}
if err := checkValidMap(f); err != nil {
return errors.New("message field %q is an invalid map: %v", f.FullName(), err)
}
if f.Syntax() == protoreflect.Proto3 {
if f.Cardinality() == protoreflect.Required {
return errors.New("message field %q using proto3 semantics cannot be required", f.FullName())
}
if f.Enum() != nil && !f.Enum().IsPlaceholder() && f.Enum().Syntax() != protoreflect.Proto3 {
return errors.New("message field %q using proto3 semantics may only depend on a proto3 enum", f.FullName())
}
}
}
seenSynthetic := false // synthetic oneofs for proto3 optional must come after real oneofs
for j := range md.GetOneofDecl() {
o := &m.L2.Oneofs.List[j]
if o.Fields().Len() == 0 {
return errors.New("message oneof %q must contain at least one field declaration", o.FullName())
}
if n := o.Fields().Len(); n-1 != (o.Fields().Get(n-1).Index() - o.Fields().Get(0).Index()) {
return errors.New("message oneof %q must have consecutively declared fields", o.FullName())
}
if o.IsSynthetic() {
seenSynthetic = true
continue
}
if !o.IsSynthetic() && seenSynthetic {
return errors.New("message oneof %q must be declared before synthetic oneofs", o.FullName())
}
for i := 0; i < o.Fields().Len(); i++ {
f := o.Fields().Get(i)
if f.Cardinality() != protoreflect.Optional {
return errors.New("message field %q belongs in a oneof and must be optional", f.FullName())
}
if f.IsWeak() {
return errors.New("message field %q belongs in a oneof and must not be a weak reference", f.FullName())
}
}
}
if err := validateEnumDeclarations(m.L1.Enums.List, md.GetEnumType()); err != nil {
return err
}
if err := validateMessageDeclarations(m.L1.Messages.List, md.GetNestedType()); err != nil {
return err
}
if err := validateExtensionDeclarations(m.L1.Extensions.List, md.GetExtension()); err != nil {
return err
}
}
return nil
}
func validateExtensionDeclarations(xs []filedesc.Extension, xds []*descriptorpb.FieldDescriptorProto) error {
for i, xd := range xds {
x := &xs[i]
// NOTE: Avoid using the IsValid method since extensions to MessageSet
// may have a field number higher than normal. This check only verifies
// that the number is not negative or reserved. We check again later
// if we know that the extendee is definitely not a MessageSet.
if n := x.Number(); n < 0 || (protowire.FirstReservedNumber <= n && n <= protowire.LastReservedNumber) {
return errors.New("extension field %q has an invalid number: %d", x.FullName(), x.Number())
}
if !x.Cardinality().IsValid() || x.Cardinality() == protoreflect.Required {
return errors.New("extension field %q has an invalid cardinality: %d", x.FullName(), x.Cardinality())
}
if xd.JsonName != nil {
if xd.GetJsonName() != strs.JSONCamelCase(string(x.Name())) {
return errors.New("extension field %q may not have an explicitly set JSON name: %q", x.FullName(), xd.GetJsonName())
}
}
if xd.OneofIndex != nil {
return errors.New("extension field %q may not be part of a oneof", x.FullName())
}
if md := x.ContainingMessage(); !md.IsPlaceholder() {
if !md.ExtensionRanges().Has(x.Number()) {
return errors.New("extension field %q extends %q with non-extension field number: %d", x.FullName(), md.FullName(), x.Number())
}
isMessageSet := md.Options().(*descriptorpb.MessageOptions).GetMessageSetWireFormat()
if isMessageSet && !isOptionalMessage(x) {
return errors.New("extension field %q extends MessageSet and must be an optional message", x.FullName())
}
if !isMessageSet && !x.Number().IsValid() {
return errors.New("extension field %q has an invalid number: %d", x.FullName(), x.Number())
}
}
if xd.GetOptions().GetWeak() {
return errors.New("extension field %q cannot be a weak reference", x.FullName())
}
if x.IsPacked() && !isPackable(x) {
return errors.New("extension field %q is not packable", x.FullName())
}
if err := checkValidGroup(x); err != nil {
return errors.New("extension field %q is an invalid group: %v", x.FullName(), err)
}
if md := x.Message(); md != nil && md.IsMapEntry() {
return errors.New("extension field %q cannot be a map entry", x.FullName())
}
if x.Syntax() == protoreflect.Proto3 {
switch x.ContainingMessage().FullName() {
case (*descriptorpb.FileOptions)(nil).ProtoReflect().Descriptor().FullName():
case (*descriptorpb.EnumOptions)(nil).ProtoReflect().Descriptor().FullName():
case (*descriptorpb.EnumValueOptions)(nil).ProtoReflect().Descriptor().FullName():
case (*descriptorpb.MessageOptions)(nil).ProtoReflect().Descriptor().FullName():
case (*descriptorpb.FieldOptions)(nil).ProtoReflect().Descriptor().FullName():
case (*descriptorpb.OneofOptions)(nil).ProtoReflect().Descriptor().FullName():
case (*descriptorpb.ExtensionRangeOptions)(nil).ProtoReflect().Descriptor().FullName():
case (*descriptorpb.ServiceOptions)(nil).ProtoReflect().Descriptor().FullName():
case (*descriptorpb.MethodOptions)(nil).ProtoReflect().Descriptor().FullName():
default:
return errors.New("extension field %q cannot be declared in proto3 unless extended descriptor options", x.FullName())
}
}
}
return nil
}
// isOptionalMessage reports whether this is an optional message.
// If the kind is unknown, it is assumed to be a message.
func isOptionalMessage(fd protoreflect.FieldDescriptor) bool {
return (fd.Kind() == 0 || fd.Kind() == protoreflect.MessageKind) && fd.Cardinality() == protoreflect.Optional
}
// isPackable checks whether the pack option can be specified.
func isPackable(fd protoreflect.FieldDescriptor) bool {
switch fd.Kind() {
case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind:
return false
}
return fd.IsList()
}
// checkValidGroup reports whether fd is a valid group according to the same
// rules that protoc imposes.
func checkValidGroup(fd protoreflect.FieldDescriptor) error {
md := fd.Message()
switch {
case fd.Kind() != protoreflect.GroupKind:
return nil
case fd.Syntax() != protoreflect.Proto2:
return errors.New("invalid under proto2 semantics")
case md == nil || md.IsPlaceholder():
return errors.New("message must be resolvable")
case fd.FullName().Parent() != md.FullName().Parent():
return errors.New("message and field must be declared in the same scope")
case !unicode.IsUpper(rune(md.Name()[0])):
return errors.New("message name must start with an uppercase")
case fd.Name() != protoreflect.Name(strings.ToLower(string(md.Name()))):
return errors.New("field name must be lowercased form of the message name")
}
return nil
}
// checkValidMap checks whether the field is a valid map according to the same
// rules that protoc imposes.
// See protoc v3.8.0: src/google/protobuf/descriptor.cc:6045-6115
func checkValidMap(fd protoreflect.FieldDescriptor) error {
md := fd.Message()
switch {
case md == nil || !md.IsMapEntry():
return nil
case fd.FullName().Parent() != md.FullName().Parent():
return errors.New("message and field must be declared in the same scope")
case md.Name() != protoreflect.Name(strs.MapEntryName(string(fd.Name()))):
return errors.New("incorrect implicit map entry name")
case fd.Cardinality() != protoreflect.Repeated:
return errors.New("field must be repeated")
case md.Fields().Len() != 2:
return errors.New("message must have exactly two fields")
case md.ExtensionRanges().Len() > 0:
return errors.New("message must not have any extension ranges")
case md.Enums().Len()+md.Messages().Len()+md.Extensions().Len() > 0:
return errors.New("message must not have any nested declarations")
}
kf := md.Fields().Get(0)
vf := md.Fields().Get(1)
switch {
case kf.Name() != genid.MapEntry_Key_field_name || kf.Number() != genid.MapEntry_Key_field_number || kf.Cardinality() != protoreflect.Optional || kf.ContainingOneof() != nil || kf.HasDefault():
return errors.New("invalid key field")
case vf.Name() != genid.MapEntry_Value_field_name || vf.Number() != genid.MapEntry_Value_field_number || vf.Cardinality() != protoreflect.Optional || vf.ContainingOneof() != nil || vf.HasDefault():
return errors.New("invalid value field")
}
switch kf.Kind() {
case protoreflect.BoolKind: // bool
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: // int32
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: // int64
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: // uint32
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: // uint64
case protoreflect.StringKind: // string
default:
return errors.New("invalid key kind: %v", kf.Kind())
}
if e := vf.Enum(); e != nil && e.Values().Len() > 0 && e.Values().Get(0).Number() != 0 {
return errors.New("map enum value must have zero number for the first value")
}
return nil
}

View File

@ -0,0 +1,242 @@
// Copyright 2019 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 protodesc
import (
"fmt"
"strings"
"google.golang.org/protobuf/internal/encoding/defval"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
)
// ToFileDescriptorProto copies a protoreflect.FileDescriptor into a
// google.protobuf.FileDescriptorProto message.
func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileDescriptorProto {
p := &descriptorpb.FileDescriptorProto{
Name: proto.String(file.Path()),
Package: proto.String(string(file.Package())),
Options: proto.Clone(file.Options()).(*descriptorpb.FileOptions),
}
for i, imports := 0, file.Imports(); i < imports.Len(); i++ {
imp := imports.Get(i)
p.Dependency = append(p.Dependency, imp.Path())
if imp.IsPublic {
p.PublicDependency = append(p.PublicDependency, int32(i))
}
if imp.IsWeak {
p.WeakDependency = append(p.WeakDependency, int32(i))
}
}
for i, locs := 0, file.SourceLocations(); i < locs.Len(); i++ {
loc := locs.Get(i)
l := &descriptorpb.SourceCodeInfo_Location{}
l.Path = append(l.Path, loc.Path...)
if loc.StartLine == loc.EndLine {
l.Span = []int32{int32(loc.StartLine), int32(loc.StartColumn), int32(loc.EndColumn)}
} else {
l.Span = []int32{int32(loc.StartLine), int32(loc.StartColumn), int32(loc.EndLine), int32(loc.EndColumn)}
}
l.LeadingDetachedComments = append([]string(nil), loc.LeadingDetachedComments...)
if loc.LeadingComments != "" {
l.LeadingComments = proto.String(loc.LeadingComments)
}
if loc.TrailingComments != "" {
l.TrailingComments = proto.String(loc.TrailingComments)
}
if p.SourceCodeInfo == nil {
p.SourceCodeInfo = &descriptorpb.SourceCodeInfo{}
}
p.SourceCodeInfo.Location = append(p.SourceCodeInfo.Location, l)
}
for i, messages := 0, file.Messages(); i < messages.Len(); i++ {
p.MessageType = append(p.MessageType, ToDescriptorProto(messages.Get(i)))
}
for i, enums := 0, file.Enums(); i < enums.Len(); i++ {
p.EnumType = append(p.EnumType, ToEnumDescriptorProto(enums.Get(i)))
}
for i, services := 0, file.Services(); i < services.Len(); i++ {
p.Service = append(p.Service, ToServiceDescriptorProto(services.Get(i)))
}
for i, exts := 0, file.Extensions(); i < exts.Len(); i++ {
p.Extension = append(p.Extension, ToFieldDescriptorProto(exts.Get(i)))
}
if syntax := file.Syntax(); syntax != protoreflect.Proto2 {
p.Syntax = proto.String(file.Syntax().String())
}
return p
}
// ToDescriptorProto copies a protoreflect.MessageDescriptor into a
// google.protobuf.DescriptorProto message.
func ToDescriptorProto(message protoreflect.MessageDescriptor) *descriptorpb.DescriptorProto {
p := &descriptorpb.DescriptorProto{
Name: proto.String(string(message.Name())),
Options: proto.Clone(message.Options()).(*descriptorpb.MessageOptions),
}
for i, fields := 0, message.Fields(); i < fields.Len(); i++ {
p.Field = append(p.Field, ToFieldDescriptorProto(fields.Get(i)))
}
for i, exts := 0, message.Extensions(); i < exts.Len(); i++ {
p.Extension = append(p.Extension, ToFieldDescriptorProto(exts.Get(i)))
}
for i, messages := 0, message.Messages(); i < messages.Len(); i++ {
p.NestedType = append(p.NestedType, ToDescriptorProto(messages.Get(i)))
}
for i, enums := 0, message.Enums(); i < enums.Len(); i++ {
p.EnumType = append(p.EnumType, ToEnumDescriptorProto(enums.Get(i)))
}
for i, xranges := 0, message.ExtensionRanges(); i < xranges.Len(); i++ {
xrange := xranges.Get(i)
p.ExtensionRange = append(p.ExtensionRange, &descriptorpb.DescriptorProto_ExtensionRange{
Start: proto.Int32(int32(xrange[0])),
End: proto.Int32(int32(xrange[1])),
Options: proto.Clone(message.ExtensionRangeOptions(i)).(*descriptorpb.ExtensionRangeOptions),
})
}
for i, oneofs := 0, message.Oneofs(); i < oneofs.Len(); i++ {
p.OneofDecl = append(p.OneofDecl, ToOneofDescriptorProto(oneofs.Get(i)))
}
for i, ranges := 0, message.ReservedRanges(); i < ranges.Len(); i++ {
rrange := ranges.Get(i)
p.ReservedRange = append(p.ReservedRange, &descriptorpb.DescriptorProto_ReservedRange{
Start: proto.Int32(int32(rrange[0])),
End: proto.Int32(int32(rrange[1])),
})
}
for i, names := 0, message.ReservedNames(); i < names.Len(); i++ {
p.ReservedName = append(p.ReservedName, string(names.Get(i)))
}
return p
}
// ToFieldDescriptorProto copies a protoreflect.FieldDescriptor into a
// google.protobuf.FieldDescriptorProto message.
func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.FieldDescriptorProto {
p := &descriptorpb.FieldDescriptorProto{
Name: proto.String(string(field.Name())),
Number: proto.Int32(int32(field.Number())),
Label: descriptorpb.FieldDescriptorProto_Label(field.Cardinality()).Enum(),
Options: proto.Clone(field.Options()).(*descriptorpb.FieldOptions),
}
if field.IsExtension() {
p.Extendee = fullNameOf(field.ContainingMessage())
}
if field.Kind().IsValid() {
p.Type = descriptorpb.FieldDescriptorProto_Type(field.Kind()).Enum()
}
if field.Enum() != nil {
p.TypeName = fullNameOf(field.Enum())
}
if field.Message() != nil {
p.TypeName = fullNameOf(field.Message())
}
if field.HasJSONName() {
p.JsonName = proto.String(field.JSONName())
}
if field.Syntax() == protoreflect.Proto3 && field.HasOptionalKeyword() {
p.Proto3Optional = proto.Bool(true)
}
if field.HasDefault() {
def, err := defval.Marshal(field.Default(), field.DefaultEnumValue(), field.Kind(), defval.Descriptor)
if err != nil && field.DefaultEnumValue() != nil {
def = string(field.DefaultEnumValue().Name()) // occurs for unresolved enum values
} else if err != nil {
panic(fmt.Sprintf("%v: %v", field.FullName(), err))
}
p.DefaultValue = proto.String(def)
}
if oneof := field.ContainingOneof(); oneof != nil {
p.OneofIndex = proto.Int32(int32(oneof.Index()))
}
return p
}
// ToOneofDescriptorProto copies a protoreflect.OneofDescriptor into a
// google.protobuf.OneofDescriptorProto message.
func ToOneofDescriptorProto(oneof protoreflect.OneofDescriptor) *descriptorpb.OneofDescriptorProto {
return &descriptorpb.OneofDescriptorProto{
Name: proto.String(string(oneof.Name())),
Options: proto.Clone(oneof.Options()).(*descriptorpb.OneofOptions),
}
}
// ToEnumDescriptorProto copies a protoreflect.EnumDescriptor into a
// google.protobuf.EnumDescriptorProto message.
func ToEnumDescriptorProto(enum protoreflect.EnumDescriptor) *descriptorpb.EnumDescriptorProto {
p := &descriptorpb.EnumDescriptorProto{
Name: proto.String(string(enum.Name())),
Options: proto.Clone(enum.Options()).(*descriptorpb.EnumOptions),
}
for i, values := 0, enum.Values(); i < values.Len(); i++ {
p.Value = append(p.Value, ToEnumValueDescriptorProto(values.Get(i)))
}
for i, ranges := 0, enum.ReservedRanges(); i < ranges.Len(); i++ {
rrange := ranges.Get(i)
p.ReservedRange = append(p.ReservedRange, &descriptorpb.EnumDescriptorProto_EnumReservedRange{
Start: proto.Int32(int32(rrange[0])),
End: proto.Int32(int32(rrange[1])),
})
}
for i, names := 0, enum.ReservedNames(); i < names.Len(); i++ {
p.ReservedName = append(p.ReservedName, string(names.Get(i)))
}
return p
}
// ToEnumValueDescriptorProto copies a protoreflect.EnumValueDescriptor into a
// google.protobuf.EnumValueDescriptorProto message.
func ToEnumValueDescriptorProto(value protoreflect.EnumValueDescriptor) *descriptorpb.EnumValueDescriptorProto {
return &descriptorpb.EnumValueDescriptorProto{
Name: proto.String(string(value.Name())),
Number: proto.Int32(int32(value.Number())),
Options: proto.Clone(value.Options()).(*descriptorpb.EnumValueOptions),
}
}
// ToServiceDescriptorProto copies a protoreflect.ServiceDescriptor into a
// google.protobuf.ServiceDescriptorProto message.
func ToServiceDescriptorProto(service protoreflect.ServiceDescriptor) *descriptorpb.ServiceDescriptorProto {
p := &descriptorpb.ServiceDescriptorProto{
Name: proto.String(string(service.Name())),
Options: proto.Clone(service.Options()).(*descriptorpb.ServiceOptions),
}
for i, methods := 0, service.Methods(); i < methods.Len(); i++ {
p.Method = append(p.Method, ToMethodDescriptorProto(methods.Get(i)))
}
return p
}
// ToMethodDescriptorProto copies a protoreflect.MethodDescriptor into a
// google.protobuf.MethodDescriptorProto message.
func ToMethodDescriptorProto(method protoreflect.MethodDescriptor) *descriptorpb.MethodDescriptorProto {
p := &descriptorpb.MethodDescriptorProto{
Name: proto.String(string(method.Name())),
InputType: fullNameOf(method.Input()),
OutputType: fullNameOf(method.Output()),
Options: proto.Clone(method.Options()).(*descriptorpb.MethodOptions),
}
if method.IsStreamingClient() {
p.ClientStreaming = proto.Bool(true)
}
if method.IsStreamingServer() {
p.ServerStreaming = proto.Bool(true)
}
return p
}
func fullNameOf(d protoreflect.Descriptor) *string {
if d == nil {
return nil
}
if strings.HasPrefix(string(d.FullName()), unknownPrefix) {
return proto.String(string(d.FullName()[len(unknownPrefix):]))
}
return proto.String("." + string(d.FullName()))
}

View File

@ -0,0 +1,77 @@
// Copyright 2020 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 protoreflect
import (
"google.golang.org/protobuf/internal/pragma"
)
// The following types are used by the fast-path Message.ProtoMethods method.
//
// To avoid polluting the public protoreflect API with types used only by
// low-level implementations, the canonical definitions of these types are
// in the runtime/protoiface package. The definitions here and in protoiface
// must be kept in sync.
type (
methods = struct {
pragma.NoUnkeyedLiterals
Flags supportFlags
Size func(sizeInput) sizeOutput
Marshal func(marshalInput) (marshalOutput, error)
Unmarshal func(unmarshalInput) (unmarshalOutput, error)
Merge func(mergeInput) mergeOutput
CheckInitialized func(checkInitializedInput) (checkInitializedOutput, error)
}
supportFlags = uint64
sizeInput = struct {
pragma.NoUnkeyedLiterals
Message Message
Flags uint8
}
sizeOutput = struct {
pragma.NoUnkeyedLiterals
Size int
}
marshalInput = struct {
pragma.NoUnkeyedLiterals
Message Message
Buf []byte
Flags uint8
}
marshalOutput = struct {
pragma.NoUnkeyedLiterals
Buf []byte
}
unmarshalInput = struct {
pragma.NoUnkeyedLiterals
Message Message
Buf []byte
Flags uint8
Resolver interface {
FindExtensionByName(field FullName) (ExtensionType, error)
FindExtensionByNumber(message FullName, field FieldNumber) (ExtensionType, error)
}
}
unmarshalOutput = struct {
pragma.NoUnkeyedLiterals
Flags uint8
}
mergeInput = struct {
pragma.NoUnkeyedLiterals
Source Message
Destination Message
}
mergeOutput = struct {
pragma.NoUnkeyedLiterals
Flags uint8
}
checkInitializedInput = struct {
pragma.NoUnkeyedLiterals
Message Message
}
checkInitializedOutput = struct {
pragma.NoUnkeyedLiterals
}
)

View File

@ -0,0 +1,504 @@
// 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 protoreflect provides interfaces to dynamically manipulate messages.
//
// This package includes type descriptors which describe the structure of types
// defined in proto source files and value interfaces which provide the
// ability to examine and manipulate the contents of messages.
//
//
// Protocol Buffer Descriptors
//
// Protobuf descriptors (e.g., EnumDescriptor or MessageDescriptor)
// are immutable objects that represent protobuf type information.
// They are wrappers around the messages declared in descriptor.proto.
// Protobuf descriptors alone lack any information regarding Go types.
//
// Enums and messages generated by this module implement Enum and ProtoMessage,
// where the Descriptor and ProtoReflect.Descriptor accessors respectively
// return the protobuf descriptor for the values.
//
// The protobuf descriptor interfaces are not meant to be implemented by
// user code since they might need to be extended in the future to support
// additions to the protobuf language.
// The "google.golang.org/protobuf/reflect/protodesc" package converts between
// google.protobuf.DescriptorProto messages and protobuf descriptors.
//
//
// Go Type Descriptors
//
// A type descriptor (e.g., EnumType or MessageType) is a constructor for
// a concrete Go type that represents the associated protobuf descriptor.
// There is commonly a one-to-one relationship between protobuf descriptors and
// Go type descriptors, but it can potentially be a one-to-many relationship.
//
// Enums and messages generated by this module implement Enum and ProtoMessage,
// where the Type and ProtoReflect.Type accessors respectively
// return the protobuf descriptor for the values.
//
// The "google.golang.org/protobuf/types/dynamicpb" package can be used to
// create Go type descriptors from protobuf descriptors.
//
//
// Value Interfaces
//
// The Enum and Message interfaces provide a reflective view over an
// enum or message instance. For enums, it provides the ability to retrieve
// the enum value number for any concrete enum type. For messages, it provides
// the ability to access or manipulate fields of the message.
//
// To convert a proto.Message to a protoreflect.Message, use the
// former's ProtoReflect method. Since the ProtoReflect method is new to the
// v2 message interface, it may not be present on older message implementations.
// The "github.com/golang/protobuf/proto".MessageReflect function can be used
// to obtain a reflective view on older messages.
//
//
// Relationships
//
// The following diagrams demonstrate the relationships between
// various types declared in this package.
//
//
// ┌───────────────────────────────────┐
// V │
// ┌────────────── New(n) ─────────────┐ │
// │ │ │
// │ ┌──── Descriptor() ──┐ │ ┌── Number() ──┐ │
// │ │ V V │ V │
// ╔════════════╗ ╔════════════════╗ ╔════════╗ ╔════════════╗
// ║ EnumType ║ ║ EnumDescriptor ║ ║ Enum ║ ║ EnumNumber ║
// ╚════════════╝ ╚════════════════╝ ╚════════╝ ╚════════════╝
// Λ Λ │ │
// │ └─── Descriptor() ──┘ │
// │ │
// └────────────────── Type() ───────┘
//
// • An EnumType describes a concrete Go enum type.
// It has an EnumDescriptor and can construct an Enum instance.
//
// • An EnumDescriptor describes an abstract protobuf enum type.
//
// • An Enum is a concrete enum instance. Generated enums implement Enum.
//
//
// ┌──────────────── New() ─────────────────┐
// │ │
// │ ┌─── Descriptor() ─────┐ │ ┌── Interface() ───┐
// │ │ V V │ V
// ╔═════════════╗ ╔═══════════════════╗ ╔═════════╗ ╔══════════════╗
// ║ MessageType ║ ║ MessageDescriptor ║ ║ Message ║ ║ ProtoMessage ║
// ╚═════════════╝ ╚═══════════════════╝ ╚═════════╝ ╚══════════════╝
// Λ Λ │ │ Λ │
// │ └──── Descriptor() ────┘ │ └─ ProtoReflect() ─┘
// │ │
// └─────────────────── Type() ─────────┘
//
// • A MessageType describes a concrete Go message type.
// It has a MessageDescriptor and can construct a Message instance.
//
// • A MessageDescriptor describes an abstract protobuf message type.
//
// • A Message is a concrete message instance. Generated messages implement
// ProtoMessage, which can convert to/from a Message.
//
//
// ┌── TypeDescriptor() ──┐ ┌───── Descriptor() ─────┐
// │ V │ V
// ╔═══════════════╗ ╔═════════════════════════╗ ╔═════════════════════╗
// ║ ExtensionType ║ ║ ExtensionTypeDescriptor ║ ║ ExtensionDescriptor ║
// ╚═══════════════╝ ╚═════════════════════════╝ ╚═════════════════════╝
// Λ │ │ Λ │ Λ
// └─────── Type() ───────┘ │ └─── may implement ────┘ │
// │ │
// └────── implements ────────┘
//
// • An ExtensionType describes a concrete Go implementation of an extension.
// It has an ExtensionTypeDescriptor and can convert to/from
// abstract Values and Go values.
//
// • An ExtensionTypeDescriptor is an ExtensionDescriptor
// which also has an ExtensionType.
//
// • An ExtensionDescriptor describes an abstract protobuf extension field and
// may not always be an ExtensionTypeDescriptor.
package protoreflect
import (
"fmt"
"strings"
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/internal/pragma"
)
type doNotImplement pragma.DoNotImplement
// ProtoMessage is the top-level interface that all proto messages implement.
// This is declared in the protoreflect package to avoid a cyclic dependency;
// use the proto.Message type instead, which aliases this type.
type ProtoMessage interface{ ProtoReflect() Message }
// Syntax is the language version of the proto file.
type Syntax syntax
type syntax int8 // keep exact type opaque as the int type may change
const (
Proto2 Syntax = 2
Proto3 Syntax = 3
)
// IsValid reports whether the syntax is valid.
func (s Syntax) IsValid() bool {
switch s {
case Proto2, Proto3:
return true
default:
return false
}
}
// String returns s as a proto source identifier (e.g., "proto2").
func (s Syntax) String() string {
switch s {
case Proto2:
return "proto2"
case Proto3:
return "proto3"
default:
return fmt.Sprintf("<unknown:%d>", s)
}
}
// GoString returns s as a Go source identifier (e.g., "Proto2").
func (s Syntax) GoString() string {
switch s {
case Proto2:
return "Proto2"
case Proto3:
return "Proto3"
default:
return fmt.Sprintf("Syntax(%d)", s)
}
}
// Cardinality determines whether a field is optional, required, or repeated.
type Cardinality cardinality
type cardinality int8 // keep exact type opaque as the int type may change
// Constants as defined by the google.protobuf.Cardinality enumeration.
const (
Optional Cardinality = 1 // appears zero or one times
Required Cardinality = 2 // appears exactly one time; invalid with Proto3
Repeated Cardinality = 3 // appears zero or more times
)
// IsValid reports whether the cardinality is valid.
func (c Cardinality) IsValid() bool {
switch c {
case Optional, Required, Repeated:
return true
default:
return false
}
}
// String returns c as a proto source identifier (e.g., "optional").
func (c Cardinality) String() string {
switch c {
case Optional:
return "optional"
case Required:
return "required"
case Repeated:
return "repeated"
default:
return fmt.Sprintf("<unknown:%d>", c)
}
}
// GoString returns c as a Go source identifier (e.g., "Optional").
func (c Cardinality) GoString() string {
switch c {
case Optional:
return "Optional"
case Required:
return "Required"
case Repeated:
return "Repeated"
default:
return fmt.Sprintf("Cardinality(%d)", c)
}
}
// Kind indicates the basic proto kind of a field.
type Kind kind
type kind int8 // keep exact type opaque as the int type may change
// Constants as defined by the google.protobuf.Field.Kind enumeration.
const (
BoolKind Kind = 8
EnumKind Kind = 14
Int32Kind Kind = 5
Sint32Kind Kind = 17
Uint32Kind Kind = 13
Int64Kind Kind = 3
Sint64Kind Kind = 18
Uint64Kind Kind = 4
Sfixed32Kind Kind = 15
Fixed32Kind Kind = 7
FloatKind Kind = 2
Sfixed64Kind Kind = 16
Fixed64Kind Kind = 6
DoubleKind Kind = 1
StringKind Kind = 9
BytesKind Kind = 12
MessageKind Kind = 11
GroupKind Kind = 10
)
// IsValid reports whether the kind is valid.
func (k Kind) IsValid() bool {
switch k {
case BoolKind, EnumKind,
Int32Kind, Sint32Kind, Uint32Kind,
Int64Kind, Sint64Kind, Uint64Kind,
Sfixed32Kind, Fixed32Kind, FloatKind,
Sfixed64Kind, Fixed64Kind, DoubleKind,
StringKind, BytesKind, MessageKind, GroupKind:
return true
default:
return false
}
}
// String returns k as a proto source identifier (e.g., "bool").
func (k Kind) String() string {
switch k {
case BoolKind:
return "bool"
case EnumKind:
return "enum"
case Int32Kind:
return "int32"
case Sint32Kind:
return "sint32"
case Uint32Kind:
return "uint32"
case Int64Kind:
return "int64"
case Sint64Kind:
return "sint64"
case Uint64Kind:
return "uint64"
case Sfixed32Kind:
return "sfixed32"
case Fixed32Kind:
return "fixed32"
case FloatKind:
return "float"
case Sfixed64Kind:
return "sfixed64"
case Fixed64Kind:
return "fixed64"
case DoubleKind:
return "double"
case StringKind:
return "string"
case BytesKind:
return "bytes"
case MessageKind:
return "message"
case GroupKind:
return "group"
default:
return fmt.Sprintf("<unknown:%d>", k)
}
}
// GoString returns k as a Go source identifier (e.g., "BoolKind").
func (k Kind) GoString() string {
switch k {
case BoolKind:
return "BoolKind"
case EnumKind:
return "EnumKind"
case Int32Kind:
return "Int32Kind"
case Sint32Kind:
return "Sint32Kind"
case Uint32Kind:
return "Uint32Kind"
case Int64Kind:
return "Int64Kind"
case Sint64Kind:
return "Sint64Kind"
case Uint64Kind:
return "Uint64Kind"
case Sfixed32Kind:
return "Sfixed32Kind"
case Fixed32Kind:
return "Fixed32Kind"
case FloatKind:
return "FloatKind"
case Sfixed64Kind:
return "Sfixed64Kind"
case Fixed64Kind:
return "Fixed64Kind"
case DoubleKind:
return "DoubleKind"
case StringKind:
return "StringKind"
case BytesKind:
return "BytesKind"
case MessageKind:
return "MessageKind"
case GroupKind:
return "GroupKind"
default:
return fmt.Sprintf("Kind(%d)", k)
}
}
// FieldNumber is the field number in a message.
type FieldNumber = protowire.Number
// FieldNumbers represent a list of field numbers.
type FieldNumbers interface {
// Len reports the number of fields in the list.
Len() int
// Get returns the ith field number. It panics if out of bounds.
Get(i int) FieldNumber
// Has reports whether n is within the list of fields.
Has(n FieldNumber) bool
doNotImplement
}
// FieldRanges represent a list of field number ranges.
type FieldRanges interface {
// Len reports the number of ranges in the list.
Len() int
// Get returns the ith range. It panics if out of bounds.
Get(i int) [2]FieldNumber // start inclusive; end exclusive
// Has reports whether n is within any of the ranges.
Has(n FieldNumber) bool
doNotImplement
}
// EnumNumber is the numeric value for an enum.
type EnumNumber int32
// EnumRanges represent a list of enum number ranges.
type EnumRanges interface {
// Len reports the number of ranges in the list.
Len() int
// Get returns the ith range. It panics if out of bounds.
Get(i int) [2]EnumNumber // start inclusive; end inclusive
// Has reports whether n is within any of the ranges.
Has(n EnumNumber) bool
doNotImplement
}
// Name is the short name for a proto declaration. This is not the name
// as used in Go source code, which might not be identical to the proto name.
type Name string // e.g., "Kind"
// IsValid reports whether s is a syntactically valid name.
// An empty name is invalid.
func (s Name) IsValid() bool {
return consumeIdent(string(s)) == len(s)
}
// Names represent a list of names.
type Names interface {
// Len reports the number of names in the list.
Len() int
// Get returns the ith name. It panics if out of bounds.
Get(i int) Name
// Has reports whether s matches any names in the list.
Has(s Name) bool
doNotImplement
}
// FullName is a qualified name that uniquely identifies a proto declaration.
// A qualified name is the concatenation of the proto package along with the
// fully-declared name (i.e., name of parent preceding the name of the child),
// with a '.' delimiter placed between each Name.
//
// This should not have any leading or trailing dots.
type FullName string // e.g., "google.protobuf.Field.Kind"
// IsValid reports whether s is a syntactically valid full name.
// An empty full name is invalid.
func (s FullName) IsValid() bool {
i := consumeIdent(string(s))
if i < 0 {
return false
}
for len(s) > i {
if s[i] != '.' {
return false
}
i++
n := consumeIdent(string(s[i:]))
if n < 0 {
return false
}
i += n
}
return true
}
func consumeIdent(s string) (i int) {
if len(s) == 0 || !isLetter(s[i]) {
return -1
}
i++
for len(s) > i && isLetterDigit(s[i]) {
i++
}
return i
}
func isLetter(c byte) bool {
return c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
}
func isLetterDigit(c byte) bool {
return isLetter(c) || ('0' <= c && c <= '9')
}
// Name returns the short name, which is the last identifier segment.
// A single segment FullName is the Name itself.
func (n FullName) Name() Name {
if i := strings.LastIndexByte(string(n), '.'); i >= 0 {
return Name(n[i+1:])
}
return Name(n)
}
// Parent returns the full name with the trailing identifier removed.
// A single segment FullName has no parent.
func (n FullName) Parent() FullName {
if i := strings.LastIndexByte(string(n), '.'); i >= 0 {
return n[:i]
}
return ""
}
// Append returns the qualified name appended with the provided short name.
//
// Invariant: n == n.Parent().Append(n.Name()) // assuming n is valid
func (n FullName) Append(s Name) FullName {
if n == "" {
return FullName(s)
}
return n + "." + FullName(s)
}

View File

@ -0,0 +1,52 @@
// Copyright 2019 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 protoreflect
// SourceLocations is a list of source locations.
type SourceLocations interface {
// Len reports the number of source locations in the proto file.
Len() int
// Get returns the ith SourceLocation. It panics if out of bounds.
Get(int) SourceLocation
doNotImplement
// TODO: Add ByPath and ByDescriptor helper methods.
}
// SourceLocation describes a source location and
// corresponds with the google.protobuf.SourceCodeInfo.Location message.
type SourceLocation struct {
// Path is the path to the declaration from the root file descriptor.
// The contents of this slice must not be mutated.
Path SourcePath
// StartLine and StartColumn are the zero-indexed starting location
// in the source file for the declaration.
StartLine, StartColumn int
// EndLine and EndColumn are the zero-indexed ending location
// in the source file for the declaration.
// In the descriptor.proto, the end line may be omitted if it is identical
// to the start line. Here, it is always populated.
EndLine, EndColumn int
// LeadingDetachedComments are the leading detached comments
// for the declaration. The contents of this slice must not be mutated.
LeadingDetachedComments []string
// LeadingComments is the leading attached comment for the declaration.
LeadingComments string
// TrailingComments is the trailing attached comment for the declaration.
TrailingComments string
}
// SourcePath identifies part of a file descriptor for a source location.
// The SourcePath is a sequence of either field numbers or indexes into
// a repeated field that form a path starting from the root file descriptor.
//
// See google.protobuf.SourceCodeInfo.Location.path.
type SourcePath []int32
// TODO: Add SourcePath.String method to pretty-print the path. For example:
// ".message_type[6].nested_type[15].field[3]"

View File

@ -0,0 +1,631 @@
// 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 protoreflect
// Descriptor provides a set of accessors that are common to every descriptor.
// Each descriptor type wraps the equivalent google.protobuf.XXXDescriptorProto,
// but provides efficient lookup and immutability.
//
// Each descriptor is comparable. Equality implies that the two types are
// exactly identical. However, it is possible for the same semantically
// identical proto type to be represented by multiple type descriptors.
//
// For example, suppose we have t1 and t2 which are both MessageDescriptors.
// If t1 == t2, then the types are definitely equal and all accessors return
// the same information. However, if t1 != t2, then it is still possible that
// they still represent the same proto type (e.g., t1.FullName == t2.FullName).
// This can occur if a descriptor type is created dynamically, or multiple
// versions of the same proto type are accidentally linked into the Go binary.
type Descriptor interface {
// ParentFile returns the parent file descriptor that this descriptor
// is declared within. The parent file for the file descriptor is itself.
//
// Support for this functionality is optional and may return nil.
ParentFile() FileDescriptor
// Parent returns the parent containing this descriptor declaration.
// The following shows the mapping from child type to possible parent types:
//
// ╔═════════════════════╤═══════════════════════════════════╗
// ║ Child type │ Possible parent types ║
// ╠═════════════════════╪═══════════════════════════════════╣
// ║ FileDescriptor │ nil ║
// ║ MessageDescriptor │ FileDescriptor, MessageDescriptor ║
// ║ FieldDescriptor │ FileDescriptor, MessageDescriptor ║
// ║ OneofDescriptor │ MessageDescriptor ║
// ║ EnumDescriptor │ FileDescriptor, MessageDescriptor ║
// ║ EnumValueDescriptor │ EnumDescriptor ║
// ║ ServiceDescriptor │ FileDescriptor ║
// ║ MethodDescriptor │ ServiceDescriptor ║
// ╚═════════════════════╧═══════════════════════════════════╝
//
// Support for this functionality is optional and may return nil.
Parent() Descriptor
// Index returns the index of this descriptor within its parent.
// It returns 0 if the descriptor does not have a parent or if the parent
// is unknown.
Index() int
// Syntax is the protobuf syntax.
Syntax() Syntax // e.g., Proto2 or Proto3
// Name is the short name of the declaration (i.e., FullName.Name).
Name() Name // e.g., "Any"
// FullName is the fully-qualified name of the declaration.
//
// The FullName is a concatenation of the full name of the type that this
// type is declared within and the declaration name. For example,
// field "foo_field" in message "proto.package.MyMessage" is
// uniquely identified as "proto.package.MyMessage.foo_field".
// Enum values are an exception to the rule (see EnumValueDescriptor).
FullName() FullName // e.g., "google.protobuf.Any"
// IsPlaceholder reports whether type information is missing since a
// dependency is not resolved, in which case only name information is known.
//
// Placeholder types may only be returned by the following accessors
// as a result of unresolved dependencies or weak imports:
//
// ╔═══════════════════════════════════╤═════════════════════╗
// ║ Accessor │ Descriptor ║
// ╠═══════════════════════════════════╪═════════════════════╣
// ║ FileImports.FileDescriptor │ FileDescriptor ║
// ║ FieldDescriptor.Enum │ EnumDescriptor ║
// ║ FieldDescriptor.Message │ MessageDescriptor ║
// ║ FieldDescriptor.DefaultEnumValue │ EnumValueDescriptor ║
// ║ FieldDescriptor.ContainingMessage │ MessageDescriptor ║
// ║ MethodDescriptor.Input │ MessageDescriptor ║
// ║ MethodDescriptor.Output │ MessageDescriptor ║
// ╚═══════════════════════════════════╧═════════════════════╝
//
// If true, only Name and FullName are valid.
// For FileDescriptor, the Path is also valid.
IsPlaceholder() bool
// Options returns the descriptor options. The caller must not modify
// the returned value.
//
// To avoid a dependency cycle, this function returns a proto.Message value.
// The proto message type returned for each descriptor type is as follows:
// ╔═════════════════════╤══════════════════════════════════════════╗
// ║ Go type │ Protobuf message type ║
// ╠═════════════════════╪══════════════════════════════════════════╣
// ║ FileDescriptor │ google.protobuf.FileOptions ║
// ║ EnumDescriptor │ google.protobuf.EnumOptions ║
// ║ EnumValueDescriptor │ google.protobuf.EnumValueOptions ║
// ║ MessageDescriptor │ google.protobuf.MessageOptions ║
// ║ FieldDescriptor │ google.protobuf.FieldOptions ║
// ║ OneofDescriptor │ google.protobuf.OneofOptions ║
// ║ ServiceDescriptor │ google.protobuf.ServiceOptions ║
// ║ MethodDescriptor │ google.protobuf.MethodOptions ║
// ╚═════════════════════╧══════════════════════════════════════════╝
//
// This method returns a typed nil-pointer if no options are present.
// The caller must import the descriptorpb package to use this.
Options() ProtoMessage
doNotImplement
}
// FileDescriptor describes the types in a complete proto file and
// corresponds with the google.protobuf.FileDescriptorProto message.
//
// Top-level declarations:
// EnumDescriptor, MessageDescriptor, FieldDescriptor, and/or ServiceDescriptor.
type FileDescriptor interface {
Descriptor // Descriptor.FullName is identical to Package
// Path returns the file name, relative to the source tree root.
Path() string // e.g., "path/to/file.proto"
// Package returns the protobuf package namespace.
Package() FullName // e.g., "google.protobuf"
// Imports is a list of imported proto files.
Imports() FileImports
// Enums is a list of the top-level enum declarations.
Enums() EnumDescriptors
// Messages is a list of the top-level message declarations.
Messages() MessageDescriptors
// Extensions is a list of the top-level extension declarations.
Extensions() ExtensionDescriptors
// Services is a list of the top-level service declarations.
Services() ServiceDescriptors
// SourceLocations is a list of source locations.
SourceLocations() SourceLocations
isFileDescriptor
}
type isFileDescriptor interface{ ProtoType(FileDescriptor) }
// FileImports is a list of file imports.
type FileImports interface {
// Len reports the number of files imported by this proto file.
Len() int
// Get returns the ith FileImport. It panics if out of bounds.
Get(i int) FileImport
doNotImplement
}
// FileImport is the declaration for a proto file import.
type FileImport struct {
// FileDescriptor is the file type for the given import.
// It is a placeholder descriptor if IsWeak is set or if a dependency has
// not been regenerated to implement the new reflection APIs.
FileDescriptor
// IsPublic reports whether this is a public import, which causes this file
// to alias declarations within the imported file. The intended use cases
// for this feature is the ability to move proto files without breaking
// existing dependencies.
//
// The current file and the imported file must be within proto package.
IsPublic bool
// IsWeak reports whether this is a weak import, which does not impose
// a direct dependency on the target file.
//
// Weak imports are a legacy proto1 feature. Equivalent behavior is
// achieved using proto2 extension fields or proto3 Any messages.
IsWeak bool
}
// MessageDescriptor describes a message and
// corresponds with the google.protobuf.DescriptorProto message.
//
// Nested declarations:
// FieldDescriptor, OneofDescriptor, FieldDescriptor, EnumDescriptor,
// and/or MessageDescriptor.
type MessageDescriptor interface {
Descriptor
// IsMapEntry indicates that this is an auto-generated message type to
// represent the entry type for a map field.
//
// Map entry messages have only two fields:
// • a "key" field with a field number of 1
// • a "value" field with a field number of 2
// The key and value types are determined by these two fields.
//
// If IsMapEntry is true, it implies that FieldDescriptor.IsMap is true
// for some field with this message type.
IsMapEntry() bool
// Fields is a list of nested field declarations.
Fields() FieldDescriptors
// Oneofs is a list of nested oneof declarations.
Oneofs() OneofDescriptors
// ReservedNames is a list of reserved field names.
ReservedNames() Names
// ReservedRanges is a list of reserved ranges of field numbers.
ReservedRanges() FieldRanges
// RequiredNumbers is a list of required field numbers.
// In Proto3, it is always an empty list.
RequiredNumbers() FieldNumbers
// ExtensionRanges is the field ranges used for extension fields.
// In Proto3, it is always an empty ranges.
ExtensionRanges() FieldRanges
// ExtensionRangeOptions returns the ith extension range options.
//
// To avoid a dependency cycle, this method returns a proto.Message value,
// which always contains a google.protobuf.ExtensionRangeOptions message.
// This method returns a typed nil-pointer if no options are present.
// The caller must import the descriptorpb package to use this.
ExtensionRangeOptions(i int) ProtoMessage
// Enums is a list of nested enum declarations.
Enums() EnumDescriptors
// Messages is a list of nested message declarations.
Messages() MessageDescriptors
// Extensions is a list of nested extension declarations.
Extensions() ExtensionDescriptors
isMessageDescriptor
}
type isMessageDescriptor interface{ ProtoType(MessageDescriptor) }
// MessageType encapsulates a MessageDescriptor with a concrete Go implementation.
type MessageType interface {
// New returns a newly allocated empty message.
New() Message
// Zero returns an empty, read-only message.
Zero() Message
// Descriptor returns the message descriptor.
//
// Invariant: t.Descriptor() == t.New().Descriptor()
Descriptor() MessageDescriptor
}
// MessageDescriptors is a list of message declarations.
type MessageDescriptors interface {
// Len reports the number of messages.
Len() int
// Get returns the ith MessageDescriptor. It panics if out of bounds.
Get(i int) MessageDescriptor
// ByName returns the MessageDescriptor for a message named s.
// It returns nil if not found.
ByName(s Name) MessageDescriptor
doNotImplement
}
// FieldDescriptor describes a field within a message and
// corresponds with the google.protobuf.FieldDescriptorProto message.
//
// It is used for both normal fields defined within the parent message
// (e.g., MessageDescriptor.Fields) and fields that extend some remote message
// (e.g., FileDescriptor.Extensions or MessageDescriptor.Extensions).
type FieldDescriptor interface {
Descriptor
// Number reports the unique number for this field.
Number() FieldNumber
// Cardinality reports the cardinality for this field.
Cardinality() Cardinality
// Kind reports the basic kind for this field.
Kind() Kind
// HasJSONName reports whether this field has an explicitly set JSON name.
HasJSONName() bool
// JSONName reports the name used for JSON serialization.
// It is usually the camel-cased form of the field name.
JSONName() string
// HasPresence reports whether the field distinguishes between unpopulated
// and default values.
HasPresence() bool
// IsExtension reports whether this is an extension field. If false,
// then Parent and ContainingMessage refer to the same message.
// Otherwise, ContainingMessage and Parent likely differ.
IsExtension() bool
// HasOptionalKeyword reports whether the "optional" keyword was explicitly
// specified in the source .proto file.
HasOptionalKeyword() bool
// IsWeak reports whether this is a weak field, which does not impose a
// direct dependency on the target type.
// If true, then Message returns a placeholder type.
IsWeak() bool
// IsPacked reports whether repeated primitive numeric kinds should be
// serialized using a packed encoding.
// If true, then it implies Cardinality is Repeated.
IsPacked() bool
// IsList reports whether this field represents a list,
// where the value type for the associated field is a List.
// It is equivalent to checking whether Cardinality is Repeated and
// that IsMap reports false.
IsList() bool
// IsMap reports whether this field represents a map,
// where the value type for the associated field is a Map.
// It is equivalent to checking whether Cardinality is Repeated,
// that the Kind is MessageKind, and that Message.IsMapEntry reports true.
IsMap() bool
// MapKey returns the field descriptor for the key in the map entry.
// It returns nil if IsMap reports false.
MapKey() FieldDescriptor
// MapValue returns the field descriptor for the value in the map entry.
// It returns nil if IsMap reports false.
MapValue() FieldDescriptor
// HasDefault reports whether this field has a default value.
HasDefault() bool
// Default returns the default value for scalar fields.
// For proto2, it is the default value as specified in the proto file,
// or the zero value if unspecified.
// For proto3, it is always the zero value of the scalar.
// The Value type is determined by the Kind.
Default() Value
// DefaultEnumValue returns the enum value descriptor for the default value
// of an enum field, and is nil for any other kind of field.
DefaultEnumValue() EnumValueDescriptor
// ContainingOneof is the containing oneof that this field belongs to,
// and is nil if this field is not part of a oneof.
ContainingOneof() OneofDescriptor
// ContainingMessage is the containing message that this field belongs to.
// For extension fields, this may not necessarily be the parent message
// that the field is declared within.
ContainingMessage() MessageDescriptor
// Enum is the enum descriptor if Kind is EnumKind.
// It returns nil for any other Kind.
Enum() EnumDescriptor
// Message is the message descriptor if Kind is
// MessageKind or GroupKind. It returns nil for any other Kind.
Message() MessageDescriptor
isFieldDescriptor
}
type isFieldDescriptor interface{ ProtoType(FieldDescriptor) }
// FieldDescriptors is a list of field declarations.
type FieldDescriptors interface {
// Len reports the number of fields.
Len() int
// Get returns the ith FieldDescriptor. It panics if out of bounds.
Get(i int) FieldDescriptor
// ByName returns the FieldDescriptor for a field named s.
// It returns nil if not found.
ByName(s Name) FieldDescriptor
// ByJSONName returns the FieldDescriptor for a field with s as the JSON name.
// It returns nil if not found.
ByJSONName(s string) FieldDescriptor
// ByNumber returns the FieldDescriptor for a field numbered n.
// It returns nil if not found.
ByNumber(n FieldNumber) FieldDescriptor
doNotImplement
}
// OneofDescriptor describes a oneof field set within a given message and
// corresponds with the google.protobuf.OneofDescriptorProto message.
type OneofDescriptor interface {
Descriptor
// IsSynthetic reports whether this is a synthetic oneof created to support
// proto3 optional semantics. If true, Fields contains exactly one field
// with HasOptionalKeyword specified.
IsSynthetic() bool
// Fields is a list of fields belonging to this oneof.
Fields() FieldDescriptors
isOneofDescriptor
}
type isOneofDescriptor interface{ ProtoType(OneofDescriptor) }
// OneofDescriptors is a list of oneof declarations.
type OneofDescriptors interface {
// Len reports the number of oneof fields.
Len() int
// Get returns the ith OneofDescriptor. It panics if out of bounds.
Get(i int) OneofDescriptor
// ByName returns the OneofDescriptor for a oneof named s.
// It returns nil if not found.
ByName(s Name) OneofDescriptor
doNotImplement
}
// ExtensionDescriptor is an alias of FieldDescriptor for documentation.
type ExtensionDescriptor = FieldDescriptor
// ExtensionTypeDescriptor is an ExtensionDescriptor with an associated ExtensionType.
type ExtensionTypeDescriptor interface {
ExtensionDescriptor
// Type returns the associated ExtensionType.
Type() ExtensionType
// Descriptor returns the plain ExtensionDescriptor without the
// associated ExtensionType.
Descriptor() ExtensionDescriptor
}
// ExtensionDescriptors is a list of field declarations.
type ExtensionDescriptors interface {
// Len reports the number of fields.
Len() int
// Get returns the ith ExtensionDescriptor. It panics if out of bounds.
Get(i int) ExtensionDescriptor
// ByName returns the ExtensionDescriptor for a field named s.
// It returns nil if not found.
ByName(s Name) ExtensionDescriptor
doNotImplement
}
// ExtensionType encapsulates an ExtensionDescriptor with a concrete
// Go implementation. The nested field descriptor must be for a extension field.
//
// While a normal field is a member of the parent message that it is declared
// within (see Descriptor.Parent), an extension field is a member of some other
// target message (see ExtensionDescriptor.Extendee) and may have no
// relationship with the parent. However, the full name of an extension field is
// relative to the parent that it is declared within.
//
// For example:
// syntax = "proto2";
// package example;
// message FooMessage {
// extensions 100 to max;
// }
// message BarMessage {
// extends FooMessage { optional BarMessage bar_field = 100; }
// }
//
// Field "bar_field" is an extension of FooMessage, but its full name is
// "example.BarMessage.bar_field" instead of "example.FooMessage.bar_field".
type ExtensionType interface {
// New returns a new value for the field.
// For scalars, this returns the default value in native Go form.
New() Value
// Zero returns a new value for the field.
// For scalars, this returns the default value in native Go form.
// For composite types, this returns an empty, read-only message, list, or map.
Zero() Value
// TypeDescriptor returns the extension type descriptor.
TypeDescriptor() ExtensionTypeDescriptor
// ValueOf wraps the input and returns it as a Value.
// ValueOf panics if the input value is invalid or not the appropriate type.
//
// ValueOf is more extensive than protoreflect.ValueOf for a given field's
// value as it has more type information available.
ValueOf(interface{}) Value
// InterfaceOf completely unwraps the Value to the underlying Go type.
// InterfaceOf panics if the input is nil or does not represent the
// appropriate underlying Go type. For composite types, it panics if the
// value is not mutable.
//
// InterfaceOf is able to unwrap the Value further than Value.Interface
// as it has more type information available.
InterfaceOf(Value) interface{}
// IsValidValue reports whether the Value is valid to assign to the field.
IsValidValue(Value) bool
// IsValidInterface reports whether the input is valid to assign to the field.
IsValidInterface(interface{}) bool
}
// EnumDescriptor describes an enum and
// corresponds with the google.protobuf.EnumDescriptorProto message.
//
// Nested declarations:
// EnumValueDescriptor.
type EnumDescriptor interface {
Descriptor
// Values is a list of nested enum value declarations.
Values() EnumValueDescriptors
// ReservedNames is a list of reserved enum names.
ReservedNames() Names
// ReservedRanges is a list of reserved ranges of enum numbers.
ReservedRanges() EnumRanges
isEnumDescriptor
}
type isEnumDescriptor interface{ ProtoType(EnumDescriptor) }
// EnumType encapsulates an EnumDescriptor with a concrete Go implementation.
type EnumType interface {
// New returns an instance of this enum type with its value set to n.
New(n EnumNumber) Enum
// Descriptor returns the enum descriptor.
//
// Invariant: t.Descriptor() == t.New(0).Descriptor()
Descriptor() EnumDescriptor
}
// EnumDescriptors is a list of enum declarations.
type EnumDescriptors interface {
// Len reports the number of enum types.
Len() int
// Get returns the ith EnumDescriptor. It panics if out of bounds.
Get(i int) EnumDescriptor
// ByName returns the EnumDescriptor for an enum named s.
// It returns nil if not found.
ByName(s Name) EnumDescriptor
doNotImplement
}
// EnumValueDescriptor describes an enum value and
// corresponds with the google.protobuf.EnumValueDescriptorProto message.
//
// All other proto declarations are in the namespace of the parent.
// However, enum values do not follow this rule and are within the namespace
// of the parent's parent (i.e., they are a sibling of the containing enum).
// Thus, a value named "FOO_VALUE" declared within an enum uniquely identified
// as "proto.package.MyEnum" has a full name of "proto.package.FOO_VALUE".
type EnumValueDescriptor interface {
Descriptor
// Number returns the enum value as an integer.
Number() EnumNumber
isEnumValueDescriptor
}
type isEnumValueDescriptor interface{ ProtoType(EnumValueDescriptor) }
// EnumValueDescriptors is a list of enum value declarations.
type EnumValueDescriptors interface {
// Len reports the number of enum values.
Len() int
// Get returns the ith EnumValueDescriptor. It panics if out of bounds.
Get(i int) EnumValueDescriptor
// ByName returns the EnumValueDescriptor for the enum value named s.
// It returns nil if not found.
ByName(s Name) EnumValueDescriptor
// ByNumber returns the EnumValueDescriptor for the enum value numbered n.
// If multiple have the same number, the first one defined is returned
// It returns nil if not found.
ByNumber(n EnumNumber) EnumValueDescriptor
doNotImplement
}
// ServiceDescriptor describes a service and
// corresponds with the google.protobuf.ServiceDescriptorProto message.
//
// Nested declarations: MethodDescriptor.
type ServiceDescriptor interface {
Descriptor
// Methods is a list of nested message declarations.
Methods() MethodDescriptors
isServiceDescriptor
}
type isServiceDescriptor interface{ ProtoType(ServiceDescriptor) }
// ServiceDescriptors is a list of service declarations.
type ServiceDescriptors interface {
// Len reports the number of services.
Len() int
// Get returns the ith ServiceDescriptor. It panics if out of bounds.
Get(i int) ServiceDescriptor
// ByName returns the ServiceDescriptor for a service named s.
// It returns nil if not found.
ByName(s Name) ServiceDescriptor
doNotImplement
}
// MethodDescriptor describes a method and
// corresponds with the google.protobuf.MethodDescriptorProto message.
type MethodDescriptor interface {
Descriptor
// Input is the input message descriptor.
Input() MessageDescriptor
// Output is the output message descriptor.
Output() MessageDescriptor
// IsStreamingClient reports whether the client streams multiple messages.
IsStreamingClient() bool
// IsStreamingServer reports whether the server streams multiple messages.
IsStreamingServer() bool
isMethodDescriptor
}
type isMethodDescriptor interface{ ProtoType(MethodDescriptor) }
// MethodDescriptors is a list of method declarations.
type MethodDescriptors interface {
// Len reports the number of methods.
Len() int
// Get returns the ith MethodDescriptor. It panics if out of bounds.
Get(i int) MethodDescriptor
// ByName returns the MethodDescriptor for a service method named s.
// It returns nil if not found.
ByName(s Name) MethodDescriptor
doNotImplement
}

View File

@ -0,0 +1,285 @@
// 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 protoreflect
import "google.golang.org/protobuf/encoding/protowire"
// Enum is a reflection interface for a concrete enum value,
// which provides type information and a getter for the enum number.
// Enum does not provide a mutable API since enums are commonly backed by
// Go constants, which are not addressable.
type Enum interface {
// Descriptor returns enum descriptor, which contains only the protobuf
// type information for the enum.
Descriptor() EnumDescriptor
// Type returns the enum type, which encapsulates both Go and protobuf
// type information. If the Go type information is not needed,
// it is recommended that the enum descriptor be used instead.
Type() EnumType
// Number returns the enum value as an integer.
Number() EnumNumber
}
// Message is a reflective interface for a concrete message value,
// encapsulating both type and value information for the message.
//
// Accessor/mutators for individual fields are keyed by FieldDescriptor.
// For non-extension fields, the descriptor must exactly match the
// field known by the parent message.
// For extension fields, the descriptor must implement ExtensionTypeDescriptor,
// extend the parent message (i.e., have the same message FullName), and
// be within the parent's extension range.
//
// Each field Value can be a scalar or a composite type (Message, List, or Map).
// See Value for the Go types associated with a FieldDescriptor.
// Providing a Value that is invalid or of an incorrect type panics.
type Message interface {
// Descriptor returns message descriptor, which contains only the protobuf
// type information for the message.
Descriptor() MessageDescriptor
// Type returns the message type, which encapsulates both Go and protobuf
// type information. If the Go type information is not needed,
// it is recommended that the message descriptor be used instead.
Type() MessageType
// New returns a newly allocated and mutable empty message.
New() Message
// Interface unwraps the message reflection interface and
// returns the underlying ProtoMessage interface.
Interface() ProtoMessage
// Range iterates over every populated field in an undefined order,
// calling f for each field descriptor and value encountered.
// Range returns immediately if f returns false.
// While iterating, mutating operations may only be performed
// on the current field descriptor.
Range(f func(FieldDescriptor, Value) bool)
// Has reports whether a field is populated.
//
// Some fields have the property of nullability where it is possible to
// distinguish between the default value of a field and whether the field
// was explicitly populated with the default value. Singular message fields,
// member fields of a oneof, and proto2 scalar fields are nullable. Such
// fields are populated only if explicitly set.
//
// In other cases (aside from the nullable cases above),
// a proto3 scalar field is populated if it contains a non-zero value, and
// a repeated field is populated if it is non-empty.
Has(FieldDescriptor) bool
// Clear clears the field such that a subsequent Has call reports false.
//
// Clearing an extension field clears both the extension type and value
// associated with the given field number.
//
// Clear is a mutating operation and unsafe for concurrent use.
Clear(FieldDescriptor)
// Get retrieves the value for a field.
//
// For unpopulated scalars, it returns the default value, where
// the default value of a bytes scalar is guaranteed to be a copy.
// For unpopulated composite types, it returns an empty, read-only view
// of the value; to obtain a mutable reference, use Mutable.
Get(FieldDescriptor) Value
// Set stores the value for a field.
//
// For a field belonging to a oneof, it implicitly clears any other field
// that may be currently set within the same oneof.
// For extension fields, it implicitly stores the provided ExtensionType.
// When setting a composite type, it is unspecified whether the stored value
// aliases the source's memory in any way. If the composite value is an
// empty, read-only value, then it panics.
//
// Set is a mutating operation and unsafe for concurrent use.
Set(FieldDescriptor, Value)
// Mutable returns a mutable reference to a composite type.
//
// If the field is unpopulated, it may allocate a composite value.
// For a field belonging to a oneof, it implicitly clears any other field
// that may be currently set within the same oneof.
// For extension fields, it implicitly stores the provided ExtensionType
// if not already stored.
// It panics if the field does not contain a composite type.
//
// Mutable is a mutating operation and unsafe for concurrent use.
Mutable(FieldDescriptor) Value
// NewField returns a new value that is assignable to the field
// for the given descriptor. For scalars, this returns the default value.
// For lists, maps, and messages, this returns a new, empty, mutable value.
NewField(FieldDescriptor) Value
// WhichOneof reports which field within the oneof is populated,
// returning nil if none are populated.
// It panics if the oneof descriptor does not belong to this message.
WhichOneof(OneofDescriptor) FieldDescriptor
// GetUnknown retrieves the entire list of unknown fields.
// The caller may only mutate the contents of the RawFields
// if the mutated bytes are stored back into the message with SetUnknown.
GetUnknown() RawFields
// SetUnknown stores an entire list of unknown fields.
// The raw fields must be syntactically valid according to the wire format.
// An implementation may panic if this is not the case.
// Once stored, the caller must not mutate the content of the RawFields.
// An empty RawFields may be passed to clear the fields.
//
// SetUnknown is a mutating operation and unsafe for concurrent use.
SetUnknown(RawFields)
// IsValid reports whether the message is valid.
//
// An invalid message is an empty, read-only value.
//
// An invalid message often corresponds to a nil pointer of the concrete
// message type, but the details are implementation dependent.
// Validity is not part of the protobuf data model, and may not
// be preserved in marshaling or other operations.
IsValid() bool
// ProtoMethods returns optional fast-path implementions of various operations.
// This method may return nil.
//
// The returned methods type is identical to
// "google.golang.org/protobuf/runtime/protoiface".Methods.
// Consult the protoiface package documentation for details.
ProtoMethods() *methods
}
// RawFields is the raw bytes for an ordered sequence of fields.
// Each field contains both the tag (representing field number and wire type),
// and also the wire data itself.
type RawFields []byte
// IsValid reports whether b is syntactically correct wire format.
func (b RawFields) IsValid() bool {
for len(b) > 0 {
_, _, n := protowire.ConsumeField(b)
if n < 0 {
return false
}
b = b[n:]
}
return true
}
// List is a zero-indexed, ordered list.
// The element Value type is determined by FieldDescriptor.Kind.
// Providing a Value that is invalid or of an incorrect type panics.
type List interface {
// Len reports the number of entries in the List.
// Get, Set, and Truncate panic with out of bound indexes.
Len() int
// Get retrieves the value at the given index.
// It never returns an invalid value.
Get(int) Value
// Set stores a value for the given index.
// When setting a composite type, it is unspecified whether the set
// value aliases the source's memory in any way.
//
// Set is a mutating operation and unsafe for concurrent use.
Set(int, Value)
// Append appends the provided value to the end of the list.
// When appending a composite type, it is unspecified whether the appended
// value aliases the source's memory in any way.
//
// Append is a mutating operation and unsafe for concurrent use.
Append(Value)
// AppendMutable appends a new, empty, mutable message value to the end
// of the list and returns it.
// It panics if the list does not contain a message type.
AppendMutable() Value
// Truncate truncates the list to a smaller length.
//
// Truncate is a mutating operation and unsafe for concurrent use.
Truncate(int)
// NewElement returns a new value for a list element.
// For enums, this returns the first enum value.
// For other scalars, this returns the zero value.
// For messages, this returns a new, empty, mutable value.
NewElement() Value
// IsValid reports whether the list is valid.
//
// An invalid list is an empty, read-only value.
//
// Validity is not part of the protobuf data model, and may not
// be preserved in marshaling or other operations.
IsValid() bool
}
// Map is an unordered, associative map.
// The entry MapKey type is determined by FieldDescriptor.MapKey.Kind.
// The entry Value type is determined by FieldDescriptor.MapValue.Kind.
// Providing a MapKey or Value that is invalid or of an incorrect type panics.
type Map interface {
// Len reports the number of elements in the map.
Len() int
// Range iterates over every map entry in an undefined order,
// calling f for each key and value encountered.
// Range calls f Len times unless f returns false, which stops iteration.
// While iterating, mutating operations may only be performed
// on the current map key.
Range(f func(MapKey, Value) bool)
// Has reports whether an entry with the given key is in the map.
Has(MapKey) bool
// Clear clears the entry associated with they given key.
// The operation does nothing if there is no entry associated with the key.
//
// Clear is a mutating operation and unsafe for concurrent use.
Clear(MapKey)
// Get retrieves the value for an entry with the given key.
// It returns an invalid value for non-existent entries.
Get(MapKey) Value
// Set stores the value for an entry with the given key.
// It panics when given a key or value that is invalid or the wrong type.
// When setting a composite type, it is unspecified whether the set
// value aliases the source's memory in any way.
//
// Set is a mutating operation and unsafe for concurrent use.
Set(MapKey, Value)
// Mutable retrieves a mutable reference to the entry for the given key.
// If no entry exists for the key, it creates a new, empty, mutable value
// and stores it as the entry for the key.
// It panics if the map value is not a message.
Mutable(MapKey) Value
// NewValue returns a new value assignable as a map value.
// For enums, this returns the first enum value.
// For other scalars, this returns the zero value.
// For messages, this returns a new, empty, mutable value.
NewValue() Value
// IsValid reports whether the map is valid.
//
// An invalid map is an empty, read-only value.
//
// An invalid message often corresponds to a nil Go map value,
// but the details are implementation dependent.
// Validity is not part of the protobuf data model, and may not
// be preserved in marshaling or other operations.
IsValid() bool
}

View File

@ -0,0 +1,59 @@
// 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.
// +build purego appengine
package protoreflect
import "google.golang.org/protobuf/internal/pragma"
type valueType int
const (
nilType valueType = iota
boolType
int32Type
int64Type
uint32Type
uint64Type
float32Type
float64Type
stringType
bytesType
enumType
ifaceType
)
// value is a union where only one type can be represented at a time.
// This uses a distinct field for each type. This is type safe in Go, but
// occupies more memory than necessary (72B).
type value struct {
pragma.DoNotCompare // 0B
typ valueType // 8B
num uint64 // 8B
str string // 16B
bin []byte // 24B
iface interface{} // 16B
}
func valueOfString(v string) Value {
return Value{typ: stringType, str: v}
}
func valueOfBytes(v []byte) Value {
return Value{typ: bytesType, bin: v}
}
func valueOfIface(v interface{}) Value {
return Value{typ: ifaceType, iface: v}
}
func (v Value) getString() string {
return v.str
}
func (v Value) getBytes() []byte {
return v.bin
}
func (v Value) getIface() interface{} {
return v.iface
}

View File

@ -0,0 +1,411 @@
// 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 protoreflect
import (
"fmt"
"math"
)
// Value is a union where only one Go type may be set at a time.
// The Value is used to represent all possible values a field may take.
// The following shows which Go type is used to represent each proto Kind:
//
// ╔════════════╤═════════════════════════════════════╗
// ║ Go type │ Protobuf kind ║
// ╠════════════╪═════════════════════════════════════╣
// ║ bool │ BoolKind ║
// ║ int32 │ Int32Kind, Sint32Kind, Sfixed32Kind ║
// ║ int64 │ Int64Kind, Sint64Kind, Sfixed64Kind ║
// ║ uint32 │ Uint32Kind, Fixed32Kind ║
// ║ uint64 │ Uint64Kind, Fixed64Kind ║
// ║ float32 │ FloatKind ║
// ║ float64 │ DoubleKind ║
// ║ string │ StringKind ║
// ║ []byte │ BytesKind ║
// ║ EnumNumber │ EnumKind ║
// ║ Message │ MessageKind, GroupKind ║
// ╚════════════╧═════════════════════════════════════╝
//
// Multiple protobuf Kinds may be represented by a single Go type if the type
// can losslessly represent the information for the proto kind. For example,
// Int64Kind, Sint64Kind, and Sfixed64Kind are all represented by int64,
// but use different integer encoding methods.
//
// The List or Map types are used if the field cardinality is repeated.
// A field is a List if FieldDescriptor.IsList reports true.
// A field is a Map if FieldDescriptor.IsMap reports true.
//
// Converting to/from a Value and a concrete Go value panics on type mismatch.
// For example, ValueOf("hello").Int() panics because this attempts to
// retrieve an int64 from a string.
type Value value
// The protoreflect API uses a custom Value union type instead of interface{}
// to keep the future open for performance optimizations. Using an interface{}
// always incurs an allocation for primitives (e.g., int64) since it needs to
// be boxed on the heap (as interfaces can only contain pointers natively).
// Instead, we represent the Value union as a flat struct that internally keeps
// track of which type is set. Using unsafe, the Value union can be reduced
// down to 24B, which is identical in size to a slice.
//
// The latest compiler (Go1.11) currently suffers from some limitations:
// • With inlining, the compiler should be able to statically prove that
// only one of these switch cases are taken and inline one specific case.
// See https://golang.org/issue/22310.
// ValueOf returns a Value initialized with the concrete value stored in v.
// This panics if the type does not match one of the allowed types in the
// Value union.
func ValueOf(v interface{}) Value {
switch v := v.(type) {
case nil:
return Value{}
case bool:
return ValueOfBool(v)
case int32:
return ValueOfInt32(v)
case int64:
return ValueOfInt64(v)
case uint32:
return ValueOfUint32(v)
case uint64:
return ValueOfUint64(v)
case float32:
return ValueOfFloat32(v)
case float64:
return ValueOfFloat64(v)
case string:
return ValueOfString(v)
case []byte:
return ValueOfBytes(v)
case EnumNumber:
return ValueOfEnum(v)
case Message, List, Map:
return valueOfIface(v)
case ProtoMessage:
panic(fmt.Sprintf("invalid proto.Message(%T) type, expected a protoreflect.Message type", v))
default:
panic(fmt.Sprintf("invalid type: %T", v))
}
}
// ValueOfBool returns a new boolean value.
func ValueOfBool(v bool) Value {
if v {
return Value{typ: boolType, num: 1}
} else {
return Value{typ: boolType, num: 0}
}
}
// ValueOfInt32 returns a new int32 value.
func ValueOfInt32(v int32) Value {
return Value{typ: int32Type, num: uint64(v)}
}
// ValueOfInt64 returns a new int64 value.
func ValueOfInt64(v int64) Value {
return Value{typ: int64Type, num: uint64(v)}
}
// ValueOfUint32 returns a new uint32 value.
func ValueOfUint32(v uint32) Value {
return Value{typ: uint32Type, num: uint64(v)}
}
// ValueOfUint64 returns a new uint64 value.
func ValueOfUint64(v uint64) Value {
return Value{typ: uint64Type, num: v}
}
// ValueOfFloat32 returns a new float32 value.
func ValueOfFloat32(v float32) Value {
return Value{typ: float32Type, num: uint64(math.Float64bits(float64(v)))}
}
// ValueOfFloat64 returns a new float64 value.
func ValueOfFloat64(v float64) Value {
return Value{typ: float64Type, num: uint64(math.Float64bits(float64(v)))}
}
// ValueOfString returns a new string value.
func ValueOfString(v string) Value {
return valueOfString(v)
}
// ValueOfBytes returns a new bytes value.
func ValueOfBytes(v []byte) Value {
return valueOfBytes(v[:len(v):len(v)])
}
// ValueOfEnum returns a new enum value.
func ValueOfEnum(v EnumNumber) Value {
return Value{typ: enumType, num: uint64(v)}
}
// ValueOfMessage returns a new Message value.
func ValueOfMessage(v Message) Value {
return valueOfIface(v)
}
// ValueOfList returns a new List value.
func ValueOfList(v List) Value {
return valueOfIface(v)
}
// ValueOfMap returns a new Map value.
func ValueOfMap(v Map) Value {
return valueOfIface(v)
}
// IsValid reports whether v is populated with a value.
func (v Value) IsValid() bool {
return v.typ != nilType
}
// Interface returns v as an interface{}.
//
// Invariant: v == ValueOf(v).Interface()
func (v Value) Interface() interface{} {
switch v.typ {
case nilType:
return nil
case boolType:
return v.Bool()
case int32Type:
return int32(v.Int())
case int64Type:
return int64(v.Int())
case uint32Type:
return uint32(v.Uint())
case uint64Type:
return uint64(v.Uint())
case float32Type:
return float32(v.Float())
case float64Type:
return float64(v.Float())
case stringType:
return v.String()
case bytesType:
return v.Bytes()
case enumType:
return v.Enum()
default:
return v.getIface()
}
}
func (v Value) typeName() string {
switch v.typ {
case nilType:
return "nil"
case boolType:
return "bool"
case int32Type:
return "int32"
case int64Type:
return "int64"
case uint32Type:
return "uint32"
case uint64Type:
return "uint64"
case float32Type:
return "float32"
case float64Type:
return "float64"
case stringType:
return "string"
case bytesType:
return "bytes"
case enumType:
return "enum"
default:
switch v := v.getIface().(type) {
case Message:
return "message"
case List:
return "list"
case Map:
return "map"
default:
return fmt.Sprintf("<unknown: %T>", v)
}
}
}
func (v Value) panicMessage(what string) string {
return fmt.Sprintf("type mismatch: cannot convert %v to %s", v.typeName(), what)
}
// Bool returns v as a bool and panics if the type is not a bool.
func (v Value) Bool() bool {
switch v.typ {
case boolType:
return v.num > 0
default:
panic(v.panicMessage("bool"))
}
}
// Int returns v as a int64 and panics if the type is not a int32 or int64.
func (v Value) Int() int64 {
switch v.typ {
case int32Type, int64Type:
return int64(v.num)
default:
panic(v.panicMessage("int"))
}
}
// Uint returns v as a uint64 and panics if the type is not a uint32 or uint64.
func (v Value) Uint() uint64 {
switch v.typ {
case uint32Type, uint64Type:
return uint64(v.num)
default:
panic(v.panicMessage("uint"))
}
}
// Float returns v as a float64 and panics if the type is not a float32 or float64.
func (v Value) Float() float64 {
switch v.typ {
case float32Type, float64Type:
return math.Float64frombits(uint64(v.num))
default:
panic(v.panicMessage("float"))
}
}
// String returns v as a string. Since this method implements fmt.Stringer,
// this returns the formatted string value for any non-string type.
func (v Value) String() string {
switch v.typ {
case stringType:
return v.getString()
default:
return fmt.Sprint(v.Interface())
}
}
// Bytes returns v as a []byte and panics if the type is not a []byte.
func (v Value) Bytes() []byte {
switch v.typ {
case bytesType:
return v.getBytes()
default:
panic(v.panicMessage("bytes"))
}
}
// Enum returns v as a EnumNumber and panics if the type is not a EnumNumber.
func (v Value) Enum() EnumNumber {
switch v.typ {
case enumType:
return EnumNumber(v.num)
default:
panic(v.panicMessage("enum"))
}
}
// Message returns v as a Message and panics if the type is not a Message.
func (v Value) Message() Message {
switch vi := v.getIface().(type) {
case Message:
return vi
default:
panic(v.panicMessage("message"))
}
}
// List returns v as a List and panics if the type is not a List.
func (v Value) List() List {
switch vi := v.getIface().(type) {
case List:
return vi
default:
panic(v.panicMessage("list"))
}
}
// Map returns v as a Map and panics if the type is not a Map.
func (v Value) Map() Map {
switch vi := v.getIface().(type) {
case Map:
return vi
default:
panic(v.panicMessage("map"))
}
}
// MapKey returns v as a MapKey and panics for invalid MapKey types.
func (v Value) MapKey() MapKey {
switch v.typ {
case boolType, int32Type, int64Type, uint32Type, uint64Type, stringType:
return MapKey(v)
default:
panic(v.panicMessage("map key"))
}
}
// MapKey is used to index maps, where the Go type of the MapKey must match
// the specified key Kind (see MessageDescriptor.IsMapEntry).
// The following shows what Go type is used to represent each proto Kind:
//
// ╔═════════╤═════════════════════════════════════╗
// ║ Go type │ Protobuf kind ║
// ╠═════════╪═════════════════════════════════════╣
// ║ bool │ BoolKind ║
// ║ int32 │ Int32Kind, Sint32Kind, Sfixed32Kind ║
// ║ int64 │ Int64Kind, Sint64Kind, Sfixed64Kind ║
// ║ uint32 │ Uint32Kind, Fixed32Kind ║
// ║ uint64 │ Uint64Kind, Fixed64Kind ║
// ║ string │ StringKind ║
// ╚═════════╧═════════════════════════════════════╝
//
// A MapKey is constructed and accessed through a Value:
// k := ValueOf("hash").MapKey() // convert string to MapKey
// s := k.String() // convert MapKey to string
//
// The MapKey is a strict subset of valid types used in Value;
// converting a Value to a MapKey with an invalid type panics.
type MapKey value
// IsValid reports whether k is populated with a value.
func (k MapKey) IsValid() bool {
return Value(k).IsValid()
}
// Interface returns k as an interface{}.
func (k MapKey) Interface() interface{} {
return Value(k).Interface()
}
// Bool returns k as a bool and panics if the type is not a bool.
func (k MapKey) Bool() bool {
return Value(k).Bool()
}
// Int returns k as a int64 and panics if the type is not a int32 or int64.
func (k MapKey) Int() int64 {
return Value(k).Int()
}
// Uint returns k as a uint64 and panics if the type is not a uint32 or uint64.
func (k MapKey) Uint() uint64 {
return Value(k).Uint()
}
// String returns k as a string. Since this method implements fmt.Stringer,
// this returns the formatted string value for any non-string type.
func (k MapKey) String() string {
return Value(k).String()
}
// Value returns k as a Value.
func (k MapKey) Value() Value {
return Value(k)
}

View File

@ -0,0 +1,98 @@
// 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.
// +build !purego,!appengine
package protoreflect
import (
"unsafe"
"google.golang.org/protobuf/internal/pragma"
)
type (
stringHeader struct {
Data unsafe.Pointer
Len int
}
sliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
ifaceHeader struct {
Type unsafe.Pointer
Data unsafe.Pointer
}
)
var (
nilType = typeOf(nil)
boolType = typeOf(*new(bool))
int32Type = typeOf(*new(int32))
int64Type = typeOf(*new(int64))
uint32Type = typeOf(*new(uint32))
uint64Type = typeOf(*new(uint64))
float32Type = typeOf(*new(float32))
float64Type = typeOf(*new(float64))
stringType = typeOf(*new(string))
bytesType = typeOf(*new([]byte))
enumType = typeOf(*new(EnumNumber))
)
// typeOf returns a pointer to the Go type information.
// The pointer is comparable and equal if and only if the types are identical.
func typeOf(t interface{}) unsafe.Pointer {
return (*ifaceHeader)(unsafe.Pointer(&t)).Type
}
// value is a union where only one type can be represented at a time.
// The struct is 24B large on 64-bit systems and requires the minimum storage
// necessary to represent each possible type.
//
// The Go GC needs to be able to scan variables containing pointers.
// As such, pointers and non-pointers cannot be intermixed.
type value struct {
pragma.DoNotCompare // 0B
// typ stores the type of the value as a pointer to the Go type.
typ unsafe.Pointer // 8B
// ptr stores the data pointer for a String, Bytes, or interface value.
ptr unsafe.Pointer // 8B
// num stores a Bool, Int32, Int64, Uint32, Uint64, Float32, Float64, or
// Enum value as a raw uint64.
//
// It is also used to store the length of a String or Bytes value;
// the capacity is ignored.
num uint64 // 8B
}
func valueOfString(v string) Value {
p := (*stringHeader)(unsafe.Pointer(&v))
return Value{typ: stringType, ptr: p.Data, num: uint64(len(v))}
}
func valueOfBytes(v []byte) Value {
p := (*sliceHeader)(unsafe.Pointer(&v))
return Value{typ: bytesType, ptr: p.Data, num: uint64(len(v))}
}
func valueOfIface(v interface{}) Value {
p := (*ifaceHeader)(unsafe.Pointer(&v))
return Value{typ: p.Type, ptr: p.Data}
}
func (v Value) getString() (x string) {
*(*stringHeader)(unsafe.Pointer(&x)) = stringHeader{Data: v.ptr, Len: int(v.num)}
return x
}
func (v Value) getBytes() (x []byte) {
*(*sliceHeader)(unsafe.Pointer(&x)) = sliceHeader{Data: v.ptr, Len: int(v.num), Cap: int(v.num)}
return x
}
func (v Value) getIface() (x interface{}) {
*(*ifaceHeader)(unsafe.Pointer(&x)) = ifaceHeader{Type: v.typ, Data: v.ptr}
return x
}

View File

@ -0,0 +1,800 @@
// 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.
//
// 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"
"log"
"strings"
"sync"
"google.golang.org/protobuf/internal/errors"
"google.golang.org/protobuf/reflect/protoreflect"
)
// 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"+
"A future release will panic on registration conflicts. See:\n"+
"https://developers.google.com/protocol-buffers/docs/reference/go/faq#namespace-conflict\n"+
"\n", err)
return true
}
var globalMutex sync.RWMutex
// GlobalFiles is a global registry of file descriptors.
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.
//
// Since registry lookup can happen in the critical performance path, resolvers
// must return this exact error value, not an error wrapping it.
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 {
// The map of descsByName contains:
// EnumDescriptor
// EnumValueDescriptor
// MessageDescriptor
// ExtensionDescriptor
// ServiceDescriptor
// *packageDescriptor
//
// Note that files are stored as a slice, since a package may contain
// 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{}
filesByPath map[string]protoreflect.FileDescriptor
}
type packageDescriptor struct {
files []protoreflect.FileDescriptor
}
// RegisterFile registers the provided file descriptor.
//
// If any descriptor within the file conflicts with the descriptor of any
// previously registered file (e.g., two enums with the same full name),
// then the file is not registered and an error is returned.
//
// It is permitted for multiple files to have the same file path.
func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error {
if r == GlobalFiles {
globalMutex.Lock()
defer globalMutex.Unlock()
}
if r.descsByName == nil {
r.descsByName = map[protoreflect.FullName]interface{}{
"": &packageDescriptor{},
}
r.filesByPath = make(map[string]protoreflect.FileDescriptor)
}
path := file.Path()
if prev := r.filesByPath[path]; prev != nil {
// TODO: Remove this after some soak-in period after moving these types.
var prevPath string
const prevModule = "google.golang.org/genproto"
const prevVersion = "cb27e3aa (May 26th, 2020)"
switch path {
case "google/protobuf/field_mask.proto":
prevPath = prevModule + "/protobuf/field_mask"
case "google/protobuf/api.proto":
prevPath = prevModule + "/protobuf/api"
case "google/protobuf/type.proto":
prevPath = prevModule + "/protobuf/ptype"
case "google/protobuf/source_context.proto":
prevPath = prevModule + "/protobuf/source_context"
}
if r == GlobalFiles && prevPath != "" {
pkgName := strings.TrimSuffix(strings.TrimPrefix(path, "google/protobuf/"), ".proto")
pkgName = strings.Replace(pkgName, "_", "", -1) + "pb"
currPath := "google.golang.org/protobuf/types/known/" + pkgName
panic(fmt.Sprintf(""+
"duplicate registration of %q\n"+
"\n"+
"The generated definition for this file has moved:\n"+
"\tfrom: %q\n"+
"\tto: %q\n"+
"A dependency on the %q module must\n"+
"be at version %v or higher.\n"+
"\n"+
"Upgrade the dependency by running:\n"+
"\tgo get -u %v\n",
path, prevPath, currPath, prevModule, prevVersion, prevPath))
}
err := errors.New("file %q is already registered", file.Path())
err = amendErrorWithCaller(err, prev, file)
if r == GlobalFiles && ignoreConflict(file, err) {
err = nil
}
return err
}
for name := file.Package(); name != ""; name = name.Parent() {
switch prev := r.descsByName[name]; prev.(type) {
case nil, *packageDescriptor:
default:
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) {
err = nil
}
return err
}
}
var err error
var hasConflict bool
rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) {
if prev := r.descsByName[d.FullName()]; prev != nil {
hasConflict = true
err = errors.New("file %q has a name conflict over %v", file.Path(), d.FullName())
err = amendErrorWithCaller(err, prev, file)
if r == GlobalFiles && ignoreConflict(d, err) {
err = nil
}
}
})
if hasConflict {
return err
}
for name := file.Package(); name != ""; name = name.Parent() {
if r.descsByName[name] == nil {
r.descsByName[name] = &packageDescriptor{}
}
}
p := r.descsByName[file.Package()].(*packageDescriptor)
p.files = append(p.files, file)
rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) {
r.descsByName[d.FullName()] = d
})
r.filesByPath[path] = file
return nil
}
// FindDescriptorByName looks up a descriptor by the full name.
//
// This returns (nil, NotFound) if not found.
func (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) {
if r == nil {
return nil, NotFound
}
if r == GlobalFiles {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
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
}
// 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
}
if r == GlobalFiles {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
if fd, ok := r.filesByPath[path]; ok {
return fd, nil
}
return nil, NotFound
}
// NumFiles reports the number of registered files.
func (r *Files) NumFiles() int {
if r == nil {
return 0
}
if r == GlobalFiles {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
return len(r.filesByPath)
}
// RangeFiles iterates over all registered files while f returns true.
// The iteration order is undefined.
func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) {
if r == nil {
return
}
if r == GlobalFiles {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
for _, file := range r.filesByPath {
if !f(file) {
return
}
}
}
// NumFilesByPackage reports the number of registered files in a proto package.
func (r *Files) NumFilesByPackage(name protoreflect.FullName) int {
if r == nil {
return 0
}
if r == GlobalFiles {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
p, ok := r.descsByName[name].(*packageDescriptor)
if !ok {
return 0
}
return len(p.files)
}
// RangeFilesByPackage iterates over all registered files in a given proto package
// while f returns true. The iteration order is undefined.
func (r *Files) RangeFilesByPackage(name protoreflect.FullName, f func(protoreflect.FileDescriptor) bool) {
if r == nil {
return
}
if r == GlobalFiles {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
p, ok := r.descsByName[name].(*packageDescriptor)
if !ok {
return
}
for _, file := range p.files {
if !f(file) {
return
}
}
}
// 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))
}
}
mds := fd.Messages()
for i := mds.Len() - 1; i >= 0; i-- {
f(mds.Get(i))
}
xds := fd.Extensions()
for i := xds.Len() - 1; i >= 0; i-- {
f(xds.Get(i))
}
sds := fd.Services()
for i := sds.Len() - 1; i >= 0; i-- {
f(sds.Get(i))
}
}
// 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"
//
// 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.
//
// 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.
//
// 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.
//
// This returns (nil, NotFound) if not found.
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}
var (
_ MessageTypeResolver = (*Types)(nil)
_ ExtensionTypeResolver = (*Types)(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 {
typesByName typesByName
extensionsByMessage extensionsByMessage
numEnums int
numMessages int
numExtensions int
}
type (
typesByName map[protoreflect.FullName]interface{}
extensionsByMessage map[protoreflect.FullName]extensionsByNumber
extensionsByNumber map[protoreflect.FieldNumber]protoreflect.ExtensionType
)
// 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 {
// Under rare circumstances getting the descriptor might recursively
// examine the registry, so fetch it before locking.
md := mt.Descriptor()
if r == GlobalTypes {
globalMutex.Lock()
defer globalMutex.Unlock()
}
if err := r.register("message", md, mt); err != nil {
return err
}
r.numMessages++
return nil
}
// 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 {
// Under rare circumstances getting the descriptor might recursively
// examine the registry, so fetch it before locking.
ed := et.Descriptor()
if r == GlobalTypes {
globalMutex.Lock()
defer globalMutex.Unlock()
}
if err := r.register("enum", ed, 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 {
// Under rare circumstances getting the descriptor might recursively
// examine the registry, so fetch it before locking.
//
// A known case where this can happen: Fetching the TypeDescriptor for a
// legacy ExtensionDesc can consult the global registry.
xd := xt.TypeDescriptor()
if r == GlobalTypes {
globalMutex.Lock()
defer globalMutex.Unlock()
}
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
}
}
if err := r.register("extension", xd, 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
}
// 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
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
if v := r.typesByName[enum]; 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 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 documentation on google.protobuf.Any.type_url for the URL format.
//
// This returns (nil, NotFound) if not found.
func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error) {
if r == nil {
return nil, NotFound
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
message := protoreflect.FullName(url)
if i := strings.LastIndexByte(url, '/'); i >= 0 {
message = message[i+len("/"):]
}
if v := r.typesByName[message]; 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 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 returns (nil, NotFound) if not found.
func (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
if r == nil {
return nil, NotFound
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
if v := r.typesByName[field]; 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 nil, NotFound
}
// 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
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
if xt, ok := r.extensionsByMessage[message][field]; ok {
return xt, nil
}
return nil, NotFound
}
// NumEnums reports the number of registered enums.
func (r *Types) NumEnums() int {
if r == nil {
return 0
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
return r.numEnums
}
// RangeEnums iterates over all registered enums while f returns true.
// Iteration order is undefined.
func (r *Types) RangeEnums(f func(protoreflect.EnumType) bool) {
if r == nil {
return
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
for _, typ := range r.typesByName {
if et, ok := typ.(protoreflect.EnumType); ok {
if !f(et) {
return
}
}
}
}
// NumMessages reports the number of registered messages.
func (r *Types) NumMessages() int {
if r == nil {
return 0
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
return r.numMessages
}
// RangeMessages iterates over all registered messages while f returns true.
// Iteration order is undefined.
func (r *Types) RangeMessages(f func(protoreflect.MessageType) bool) {
if r == nil {
return
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
for _, typ := range r.typesByName {
if mt, ok := typ.(protoreflect.MessageType); ok {
if !f(mt) {
return
}
}
}
}
// NumExtensions reports the number of registered extensions.
func (r *Types) NumExtensions() int {
if r == nil {
return 0
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
return r.numExtensions
}
// RangeExtensions iterates over all registered extensions while f returns true.
// Iteration order is undefined.
func (r *Types) RangeExtensions(f func(protoreflect.ExtensionType) bool) {
if r == nil {
return
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
for _, typ := range r.typesByName {
if xt, ok := typ.(protoreflect.ExtensionType); ok {
if !f(xt) {
return
}
}
}
}
// 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
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
return len(r.extensionsByMessage[message])
}
// RangeExtensionsByMessage iterates over all registered extensions filtered
// by a given message type while f returns true. Iteration order is undefined.
func (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) {
if r == nil {
return
}
if r == GlobalTypes {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
for _, xt := range r.extensionsByMessage[message] {
if !f(xt) {
return
}
}
}
func typeName(t interface{}) 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)
}
}
func amendErrorWithCaller(err error, prev, curr interface{}) error {
prevPkg := goPackage(prev)
currPkg := goPackage(curr)
if prevPkg == "" || currPkg == "" || prevPkg == currPkg {
return err
}
return errors.New("%s\n\tpreviously from: %q\n\tcurrently from: %q", err, prevPkg, currPkg)
}
func goPackage(v interface{}) string {
switch d := v.(type) {
case protoreflect.EnumType:
v = d.Descriptor()
case protoreflect.MessageType:
v = d.Descriptor()
case protoreflect.ExtensionType:
v = d.TypeDescriptor()
}
if d, ok := v.(protoreflect.Descriptor); ok {
v = d.ParentFile()
}
if d, ok := v.(interface{ GoPackagePath() string }); ok {
return d.GoPackagePath()
}
return ""
}