[protobuf] reflect/protoreflect: initial commit

266 views
Skip to first unread message

Joe Tsai (Gerrit)

unread,
Aug 10, 2018, 3:59:53 PM8/10/18
to Joe Tsai, Joe Tsai, goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Damien Neil, Herbie Ong, Chris Manghane, golang-co...@googlegroups.com

Joe Tsai merged this change.

View Change

Approvals: Damien Neil: Looks good to me, approved
reflect/protoreflect: initial commit

Package protoreflect provides APIs for programatically interacting with messages
according to the protobuf type system.

Change-Id: I11fa3874e4b3f94771514c69efb2ae8bb812d7fa
Reviewed-on: https://go-review.googlesource.com/127823
Reviewed-by: Damien Neil <dn...@google.com>
---
M reflect/protoreflect/proto.go
A reflect/protoreflect/proto_test.go
A reflect/protoreflect/type.go
A reflect/protoreflect/value.go
A reflect/protoreflect/value_pure.go
A reflect/protoreflect/value_test.go
A reflect/protoreflect/value_union.go
A reflect/protoreflect/value_unsafe.go
M test.bash
9 files changed, 1,916 insertions(+), 4 deletions(-)

diff --git a/reflect/protoreflect/proto.go b/reflect/protoreflect/proto.go
index 06be22e..6ac1759 100644
--- a/reflect/protoreflect/proto.go
+++ b/reflect/protoreflect/proto.go
@@ -2,21 +2,281 @@
// 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.
+//
+// Every Go type that represents a protocol buffer type must implement the
+// proto.Message or proto.Enum interface, which has a ProtoReflect method that
+// returns a protoreflect.Message or protoreflect.Enum.
+// These interfaces provide programs with the ability to manipulate a
+// message value or to explore the protobuf type descriptor.
+//
+// The defined interfaces can be categorized as either a type descriptor
+// or a value interface.
+//
+// Type Descriptors
+//
+// The type descriptors (e.g., MessageDescriptor or EnumDescriptor)
+// are immutable objects that represent protobuf type information.
+// They are wrappers around the messages declared in descriptor.proto.
+//
+// Value Interfaces
+//
+// The value is a reflective interface (e.g., Message) for a message instance.
+// The Message interface provides the ability to manipulate the fields of a
+// message using getters and setters.
package protoreflect

import (
"regexp"
+ "strings"
+
+ "google.golang.org/proto/internal/encoding/wire"
+ "google.golang.org/proto/internal/pragma"
)

-// TODO: This is a stub while the full implementation is under review.
-// See https://golang.org/cl/127823.
+type doNotImplement pragma.DoNotImplement

-type Name string
+// 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 }
+
+// ProtoEnum is the top-level interface that all proto enums implement.
+// This is declared in the protoreflect package to avoid a cyclic dependency;
+// use the proto.Enum type instead, which aliases this type.
+type ProtoEnum interface{ ProtoReflect() Enum }
+
+// 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
+ }
+}
+func (s Syntax) String() string {
+ switch s {
+ case Proto2:
+ return "proto2"
+ case Proto3:
+ return "proto3"
+ default:
+ return "<unknown>"
+ }
+}
+
+// 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
+ }
+}
+func (c Cardinality) String() string {
+ switch c {
+ case Optional:
+ return "optional"
+ case Required:
+ return "required"
+ case Repeated:
+ return "repeated"
+ default:
+ return "<unknown>"
+ }
+}
+
+// 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
+ }
+}
+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 "<unknown>"
+ }
+}
+
+// FieldNumber is the field number in a message.
+type FieldNumber = wire.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

var (
- regexName = regexp.MustCompile(`^[_a-zA-Z][_a-zA-Z0-9]*$`)
+ regexName = regexp.MustCompile(`^[_a-zA-Z][_a-zA-Z0-9]*$`)
+ regexFullName = regexp.MustCompile(`^[_a-zA-Z][_a-zA-Z0-9]*(\.[_a-zA-Z][_a-zA-Z0-9]*)*$`)
)

+// 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 n is a syntactically valid name.
+// An empty name is invalid.
func (n Name) IsValid() bool {
return regexName.MatchString(string(n))
}
+
+// 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 n is a syntactically valid full name.
+// An empty full name is invalid.
+func (n FullName) IsValid() bool {
+ return regexFullName.MatchString(string(n))
+}
+
+// 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)
+}
diff --git a/reflect/protoreflect/proto_test.go b/reflect/protoreflect/proto_test.go
new file mode 100644
index 0000000..a62a004
--- /dev/null
+++ b/reflect/protoreflect/proto_test.go
@@ -0,0 +1,74 @@
+// 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 "testing"
+
+func TestNameIsValid(t *testing.T) {
+ tests := []struct {
+ in Name
+ want bool
+ }{
+ {"", false},
+ {"a", true},
+ {".", false},
+ {"_", true}, // odd, but permitted by protoc
+ {".foo", false},
+ {"foo.", false},
+ {"foo", true},
+ {"one1_two2_three3", true},
+ {"1one", false},
+ }
+
+ for _, tt := range tests {
+ if got := tt.in.IsValid(); got != tt.want {
+ t.Errorf("Name(%q).IsValid() = %v, want %v", tt.in, got, tt.want)
+ }
+ }
+}
+
+func TestFullNameIsValid(t *testing.T) {
+ tests := []struct {
+ in FullName
+ want bool
+ }{
+ {"", false},
+ {"a", true},
+ {"a.b", true},
+ {"a.b.c", true},
+ {".", false},
+ {"_._._", true}, // odd, but permitted by protoc
+ {".foo", false},
+ {"foo.", false},
+ {"foo", true},
+ {"one1_two2_three3", true},
+ {"one1.two2.three3", true},
+ {".one1.two2.three3", false},
+ {"one1.two2.three3.", false},
+ {"foo.1one", false},
+ }
+
+ for _, tt := range tests {
+ if got := tt.in.IsValid(); got != tt.want {
+ t.Errorf("Name(%q).IsValid() = %v, want %v", tt.in, got, tt.want)
+ }
+ }
+}
+
+func TestNameAppend(t *testing.T) {
+ tests := []FullName{
+ "",
+ "a",
+ "a.b",
+ "a.b.c",
+ "one1.two2.three3",
+ }
+
+ for _, tt := range tests {
+ if got := tt.Parent().Append(tt.Name()); got != tt {
+ t.Errorf("FullName.Parent().Append(FullName.Name()) = %q, want %q", got, tt)
+ }
+ }
+}
diff --git a/reflect/protoreflect/type.go b/reflect/protoreflect/type.go
new file mode 100644
index 0000000..d46f402
--- /dev/null
+++ b/reflect/protoreflect/type.go
@@ -0,0 +1,638 @@
+// 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 "reflect"
+
+// TODO: Rename GoNew as New for MessageType, EnumType, and ExtensionType?
+
+// TODO: For all ByX methods (e.g., ByName, ByJSONName, ByNumber, etc),
+// should they use a (v, ok) signature for the return value?
+
+// 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 linked into the Go binary.
+type Descriptor interface {
+ // 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, false).
+ Parent() (Descriptor, bool)
+
+ // 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 weak imports:
+ //
+ // +-----------------------------------+-------------------+
+ // | Accessor | Descriptor |
+ // +-----------------------------------+-------------------+
+ // | FileImports.FileDescriptor | FileDescriptor |
+ // | FieldDescriptor.MessageDescriptor | MessageDescriptor |
+ // +-----------------------------------+-------------------+
+ //
+ // If true, only Name and FullName are valid.
+ // For FileDescriptor, the Path and Package are also valid.
+ IsPlaceholder() bool
+
+ // DescriptorProto returns a reflective interface to the underlying
+ // google.protobuf.XXXDescriptorProto message. The returned Message is
+ // considered frozen and mutating operations must not be performed
+ // on the message or any nested values retrieved by methods on the message.
+ //
+ // The proto message type for each type is as follows:
+ // +---------------------+------------------------------------------+
+ // | Go type | Proto message type |
+ // +---------------------+------------------------------------------+
+ // | FileDescriptor | google.protobuf.FileDescriptorProto |
+ // | MessageDescriptor | google.protobuf.DescriptorProto |
+ // | FieldDescriptor | google.protobuf.FieldDescriptorProto |
+ // | OneofDescriptor | google.protobuf.OneofDescriptorProto |
+ // | EnumDescriptor | google.protobuf.EnumDescriptorProto |
+ // | EnumValueDescriptor | google.protobuf.EnumValueDescriptorProto |
+ // | ServiceDescriptor | google.protobuf.ServiceDescriptorProto |
+ // | MethodDescriptor | google.protobuf.MethodDescriptorProto |
+ // +---------------------+------------------------------------------+
+ //
+ // Support for this functionality is optional and may return (nil, false).
+ DescriptorProto() (Message, bool)
+
+ // DescriptorOptions is a helper for accessing the proto options specified
+ // on any of the descriptor types. It is functionally equivalent to
+ // accessing the "options" field in the descriptor and providing a set of
+ // efficient lookup methods.
+ //
+ // Support for this functionality is optional and may return (nil, false).
+ DescriptorOptions() (DescriptorOptions, bool)
+
+ doNotImplement
+}
+
+// DescriptorOptions is a wrapper around proto options.
+//
+// The proto message type for each descriptor type is as follows:
+// +---------------------+----------------------------------+
+// | Go type | Proto message type |
+// +---------------------+----------------------------------+
+// | FileDescriptor | google.protobuf.FileOptions |
+// | MessageDescriptor | google.protobuf.MessageOptions |
+// | FieldDescriptor | google.protobuf.FieldOptions |
+// | OneofDescriptor | google.protobuf.OneofOptions |
+// | EnumDescriptor | google.protobuf.EnumOptions |
+// | EnumValueDescriptor | google.protobuf.EnumValueOptions |
+// | ServiceDescriptor | google.protobuf.ServiceOptions |
+// | MethodDescriptor | google.protobuf.MethodOptions |
+// +---------------------+----------------------------------+
+//
+// The values returned by Get, ByName, and ByNumber are considered frozen and
+// mutating operations must not be performed on values returned by them.
+type DescriptorOptions interface {
+ // Len reports the total number of option fields,
+ // including both fields declared in the options proto and extensions, and
+ // including fields that are declared but not set in the proto file.
+ Len() int
+
+ // Get returns the ith field. It panics if out of bounds.
+ // The FieldDescriptor is guaranteed to be non-nil, while the Value
+ // may be Null if the proto file did not specify this option.
+ Get(i int) (FieldDescriptor, Value)
+
+ // ByName looks a field up by full name and
+ // returns (nil, Null) if not found.
+ //
+ // As a special-case, non-extension fields in the options type can be
+ // directly accessed by the field name alone (e.g., "map_entry" may be used
+ // instead of "google.protobuf.MessageOptions.map_entry").
+ ByName(s FullName) (FieldDescriptor, Value)
+
+ // ByNumber looks a field up by the field number and
+ // returns (nil, Null) if not found.
+ ByNumber(n FieldNumber) (FieldDescriptor, Value)
+
+ doNotImplement
+}
+
+// FileDescriptor describes the types in a complete proto file and
+// corresponds with the google.protobuf.FileDescriptorProto message.
+//
+// Top-level declarations:
+// MessageDescriptor, EnumDescriptor, FieldDescriptor, and/or ServiceDescriptor.
+type FileDescriptor interface {
+ Descriptor // Descriptor.FullName is identical to Package
+
+ // Path returns the file name, relative to root of source tree.
+ 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
+
+ // Messages is a list of the top-level message declarations.
+ Messages() MessageDescriptors
+ // Enums is a list of the top-level enum declarations.
+ Enums() EnumDescriptors
+ // Extensions is a list of the top-level extension declarations.
+ Extensions() ExtensionDescriptors
+ // Services is a list of the top-level service declarations.
+ Services() ServiceDescriptors
+
+ 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 {
+ // TODO: For FileDescriptor to be resolved, we must assume that there is
+ // a way to obtain the FileDescriptor for some dependency.
+ // How does this work for old dependencies that have not been regenerated
+ // and lack a FileDescriptor in a variable we can reference?
+ //
+ // A similar problem exists for FieldDescriptor.MessageDescriptor and
+ // FieldDescriptor.EnumDescriptor where a reference to external types
+ // is only possible if we assume that the dependency has
+ // reflection support available.
+ //
+ // Is it reasonable to assume that your dependencies have to be regenerated
+ // to have proto reflection support before you can depend on them?
+ //
+ // The decision here also affects Descriptor.IsPlaceholder
+
+ // FileDescriptor is the file type for the given import.
+ // It is a placeholder type if IsWeak is set.
+ 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, MessageDescriptor,
+// and/or EnumDescriptor.
+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
+
+ // 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
+
+ // Messages is a list of nested message declarations.
+ Messages() MessageDescriptors
+ // Enums is a list of nested enum declarations.
+ Enums() EnumDescriptors
+ // Extensions is a list of nested extension declarations.
+ Extensions() ExtensionDescriptors
+
+ isMessageDescriptor
+}
+type isMessageDescriptor interface{ ProtoType(MessageDescriptor) }
+
+// MessageType extends a MessageDescriptor with Go specific type information.
+type MessageType interface {
+ MessageDescriptor
+
+ // GoNew returns a newly allocated empty message.
+ GoNew() ProtoMessage
+
+ // GoType returns the Go type of the allocated message.
+ //
+ // Invariant: t.GoType() == reflect.TypeOf(t.GoNew())
+ GoType() reflect.Type
+}
+
+// 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
+
+ // JSONName reports the name used for JSON serialization.
+ // It is usually the camel-cased form of the field name.
+ JSONName() string
+
+ // IsPacked reports whether repeated primitive numeric kinds should be
+ // serialized using a packed encoding.
+ // If true, then it implies Cardinality is Repeated.
+ IsPacked() bool
+
+ // IsMap reports whether this field represents a map.
+ // The value type for the associated field is a Map instead of a Vector.
+ //
+ // If true, it implies that Kind is MessageKind, Cardinality is Repeated,
+ // and MessageDescriptor.IsMapEntry is true.
+ IsMap() bool
+
+ // IsWeak reports whether this is a weak field, which does not impose a
+ // direct dependency on the target type.
+ // If true, then MessageDescriptor returns a placeholder type.
+ IsWeak() 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
+
+ // OneofType is the containing oneof that this field belongs to,
+ // and is nil if this field is not part of a oneof.
+ OneofType() OneofDescriptor
+
+ // ExtendedType returns a MessageDescriptor for the extended message
+ // that this extension field belongs in.
+ // It returns nil if this field is not an extension.
+ ExtendedType() MessageDescriptor
+
+ // MessageType is the message type if Kind is MessageKind or GroupKind.
+ // It returns nil for any other Kind.
+ MessageType() MessageDescriptor
+
+ // EnumType is the enum type if Kind is EnumKind.
+ // It returns nil for any other Kind.
+ EnumType() EnumDescriptor
+
+ 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
+
+ // 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
+
+// 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 extends a ExtensionDescriptor with Go type information.
+// The embedded 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.ExtendedType) 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 {
+ ExtensionDescriptor
+
+ // GoNew returns a new value for the field.
+ GoNew() interface{}
+
+ // GoType returns the Go type of the field value.
+ //
+ // The type is currently determined automatically (although custom Go types
+ // may be supported in the future). The type is T for scalars and
+ // *[]T for vectors. Maps are not valid in extension fields.
+ // The type T is determined as follows:
+ //
+ // +------------+-------------------------------------+
+ // | 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 |
+ // | E | EnumKind |
+ // | M | MessageKind, GroupKind |
+ // +------------+-------------------------------------+
+ //
+ // The type E is the concrete enum type (see EnumDescriptor.GoType).
+ // The type M is the concrete message type (see MessageDescriptor.GoType).
+ //
+ // Invariants:
+ // t.GoType() == reflect.TypeOf(t.GoNew())
+ // t.GoType() == reflect.TypeOf(t.InterfaceOf(t.ValueOf(t.GoNew())))
+ GoType() reflect.Type
+
+ // TODO: How do we reconcile GoType with the existing extension API,
+ // which returns *T for scalars (causing unnecessary aliasing),
+ // and []T for vectors (causing insufficient aliasing)?
+
+ // ValueOf wraps the input and returns it as a Value.
+ // ValueOf panics if the input value is 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 does not represent the appropriate
+ // underlying Go type.
+ //
+ // InterfaceOf is able to unwrap the Value further than Value.Interface
+ // as it has more type information available.
+ InterfaceOf(Value) interface{}
+}
+
+// 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
+
+ isEnumDescriptor
+}
+type isEnumDescriptor interface{ ProtoType(EnumDescriptor) }
+
+// EnumType extends a EnumDescriptor with Go specific type information.
+type EnumType interface {
+ EnumDescriptor
+
+ // GoNew returns an instance of this enum type with its value set to n.
+ GoNew(n EnumNumber) ProtoEnum
+
+ // GoType returns the Go type of the enum value.
+ //
+ // Invariants: t.GoType() == reflect.TypeOf(t.GoNew(0))
+ GoType() reflect.Type
+}
+
+// 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
+
+ // InputType is the input message type.
+ InputType() MessageDescriptor
+ // OutputType is the output message type.
+ OutputType() 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
+}
diff --git a/reflect/protoreflect/value.go b/reflect/protoreflect/value.go
new file mode 100644
index 0000000..94fe3f5
--- /dev/null
+++ b/reflect/protoreflect/value.go
@@ -0,0 +1,383 @@
+// 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/proto/internal/encoding/wire"
+
+// TODO: Document the behavior of each Range operation when a mutation
+// occurs while ranging. Also document the order.
+
+// 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 {
+ Type() EnumType
+
+ // Number returns the enum value as an integer.
+ Number() EnumNumber
+}
+
+// Message is a reflection interface to a concrete message value,
+// which provides type information and getters/setters for individual fields.
+//
+// Concrete types may implement interfaces defined in proto/protoiface,
+// which provide specialized, performant implementations of high-level
+// operations such as Marshal and Unmarshal.
+type Message interface {
+ Type() MessageType
+
+ // KnownFields returns an interface to access/mutate known fields.
+ KnownFields() KnownFields
+
+ // UnknownFields returns an interface to access/mutate unknown fields.
+ UnknownFields() UnknownFields
+
+ // Interface unwraps the message reflection interface and
+ // returns the underlying proto.Message interface.
+ Interface() ProtoMessage
+
+ // ProtoMutable is a marker method to implement the Mutable interface.
+ ProtoMutable()
+}
+
+// KnownFields provides accessor and mutator methods for known fields.
+//
+// Each field Value can either be a scalar, Message, Vector, or Map.
+// The field is a Vector or Map if FieldDescriptor.Cardinality is Repeated and
+// a Map if and only if FieldDescriptor.IsMap is true. The scalar type or
+// underlying repeated element type is determined by the FieldDescriptor.Kind.
+// See Value for a list of Go types associated with each Kind.
+//
+// Some fields have the property of nullability where it is possible to
+// distinguish between the zero value of a field and whether the field was
+// explicitly populated with the zero value. Only scalars in proto2,
+// members of a oneof field, and singular messages are nullable.
+// In the presence of unset fields, KnownFields.Get does not return defaults;
+// use the corresponding FieldDescriptor.DefaultValue for that information.
+//
+// Field extensions are handled as known fields once the extension type has been
+// registered with KnownFields.ExtensionTypes.
+//
+// List, Len, Get, Range, and ExtensionTypes are safe for concurrent access.
+type KnownFields interface {
+ // List returns a new, unordered list of all fields that are populated.
+ // A nullable field is populated only if explicitly set.
+ // A scalar field in proto3 is populated if it contains a non-zero value.
+ // A repeated field is populated only if it is non-empty.
+ List() []FieldNumber
+
+ // Len reports the number of fields that are populated.
+ //
+ // Invariant: f.Len() == len(f.List())
+ Len() int
+
+ // TODO: Should Get return FieldDescriptor.Default if unpopulated instead of
+ // returning the Null variable? If so, we loose the ability to represent
+ // nullability in Get and Set calls and also need to add Has and Clear.
+
+ // Get retrieves the value for field with the given field number.
+ // It returns Null for non-existent or nulled fields.
+ Get(FieldNumber) Value
+
+ // TODO: Document memory aliasing behavior when a field is cleared?
+ // For example, if Mutable is called later, can it reuse memory?
+
+ // Set stores the value for a field with the given field number.
+ // Setting a field belonging to a oneof implicitly clears any other field
+ // that may be currently set by the same oneof.
+ // Null may be used to explicitly clear a field containing a proto2 scalar,
+ // a member of oneof, or a singular message.
+ //
+ // When setting a composite type, it is unspecified whether the set
+ // value aliases the source's memory in any way.
+ //
+ // It panics if the field number does not correspond with a known field
+ // in MessageDescriptor.Fields or an extension field in ExtensionTypes.
+ Set(FieldNumber, Value)
+
+ // Mutable returns a reference value for a field with a given field number.
+ // If the field is unset, Mutable implicitly initializes the field with
+ // a zero value instance of the Go type for that field.
+ //
+ // The returned Mutable reference is never nil, and is only valid until the
+ // next Set or Mutable call.
+ //
+ // It panics if FieldNumber does not correspond with a known field
+ // in MessageDescriptor.Fields or an extension field in ExtensionTypes.
+ Mutable(FieldNumber) Mutable
+
+ // Range calls f sequentially for each known field that is populated.
+ // If f returns false, range stops the iteration.
+ Range(f func(FieldNumber, Value) bool)
+
+ // ExtensionTypes are extension field types that are known by this
+ // specific message instance.
+ ExtensionTypes() ExtensionFieldTypes
+}
+
+// UnknownFields are a list of unknown or unparsed fields and may contain
+// field numbers corresponding with defined fields or extension fields.
+// The ordering of fields is maintained for fields of the same field number.
+// However, the relative ordering of fields with different field numbers
+// is undefined.
+//
+// List, Len, Get, and Range are safe for concurrent access.
+type UnknownFields interface {
+ // List returns a new, unordered list of all fields that are set.
+ List() []FieldNumber
+
+ // Len reports the number of fields that are set.
+ //
+ // Invariant: f.Len() == len(f.List())
+ Len() int
+
+ // Get retrieves the raw bytes of fields with the given field number.
+ // It returns an empty RawFields if there are no set fields.
+ //
+ // The caller must not mutate the content of the retrieved RawFields.
+ Get(FieldNumber) RawFields
+
+ // Set stores the raw bytes of fields with the given field number.
+ // The RawFields must be valid and correspond with the given field number;
+ // an implementation may panic if the fields are invalid.
+ // An empty RawFields may be passed to clear the fields.
+ //
+ // The caller must not mutate the content of the RawFields being stored.
+ Set(FieldNumber, RawFields)
+
+ // Range calls f sequentially for each unknown field that is populated.
+ // If f returns false, range stops the iteration.
+ Range(f func(FieldNumber, RawFields) bool)
+
+ // TODO: Should IsSupported be renamed as ReadOnly?
+ // TODO: Should IsSupported panic on Set instead of silently ignore?
+
+ // IsSupported reports whether this message supports unknown fields.
+ // If false, UnknownFields ignores all Set operations.
+ IsSupported() bool
+}
+
+// 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.
+//
+// Once stored, the content of a RawFields must be treated as immutable.
+// (e.g., raw[:len(raw)] is immutable, but raw[len(raw):cap(raw)] is mutable).
+// Thus, appending to RawFields (with valid wire data) is permitted.
+type RawFields []byte
+
+// IsValid reports whether RawFields is syntactically correct wire format.
+func (b RawFields) IsValid() bool {
+ for len(b) > 0 {
+ _, _, n := wire.ConsumeField(b)
+ if n < 0 {
+ return false
+ }
+ b = b[n:]
+ }
+ return true
+}
+
+// ExtensionFieldTypes are the extension field types that this message instance
+// has been extended with.
+//
+// List, Len, Get, and Range are safe for concurrent access.
+type ExtensionFieldTypes interface {
+ // List returns a new, unordered list of known extension field types.
+ List() []ExtensionType
+
+ // Len reports the number of field extensions.
+ //
+ // Invariant: f.Len() == len(f.List())
+ Len() int
+
+ // Register stores an ExtensionType.
+ // The ExtensionType.ExtendedType must match the containing message type
+ // and the field number must be within the valid extension ranges
+ // (see MessageDescriptor.ExtensionRanges).
+ // It panics if the extension has already been registered (i.e.,
+ // a conflict by number or by full name).
+ Register(ExtensionType)
+
+ // Remove removes the ExtensionType.
+ // It panics if a value for this extension field is still populated.
+ Remove(ExtensionType)
+
+ // ByNumber looks up an extension by field number.
+ // It returns nil if not found.
+ ByNumber(FieldNumber) ExtensionType
+
+ // ByName looks up an extension field by full name.
+ // It returns nil if not found.
+ ByName(FullName) ExtensionType
+
+ // Range calls f sequentially for each registered extension field type.
+ // If f returns false, range stops the iteration.
+ Range(f func(ExtensionType) bool)
+}
+
+// Vector is an ordered list. Every element is always considered populated
+// (i.e., Get never provides and Set never accepts Null).
+// The element Value type is determined by the associated FieldDescriptor.Kind
+// and cannot be a Map or Vector.
+//
+// Len and Get are safe for concurrent access.
+type Vector interface {
+ // Len reports the number of entries in the Vector.
+ // Get, Set, Mutable, and Truncate panic with out of bound indexes.
+ Len() int
+
+ // Get retrieves the value at the given index.
+ 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.
+ //
+ // It panics if the value is Null.
+ Set(int, Value)
+
+ // Append appends the provided value to the end of the vector.
+ //
+ // When appending a composite type, it is unspecified whether the appended
+ // value aliases the source's memory in any way.
+ //
+ // It panics if the value is Null.
+ Append(Value)
+
+ // Mutable returns a Mutable reference for the element with a given index.
+ //
+ // The returned reference is never nil, and is only valid until the
+ // next Set, Mutable, Append, MutableAppend, or Truncate call.
+ Mutable(int) Mutable
+
+ // MutableAppend appends a new element and returns a mutable reference.
+ //
+ // The returned reference is never nil, and is only valid until the
+ // next Set, Mutable, Append, MutableAppend, or Truncate call.
+ MutableAppend() Mutable
+
+ // TODO: Should truncate accept two indexes similar to slicing?M
+
+ // Truncate truncates the vector to a smaller length.
+ Truncate(int)
+
+ // ProtoMutable is a marker method to implement the Mutable interface.
+ ProtoMutable()
+}
+
+// Map is an unordered, associative map. Only elements within the map
+// is considered populated. The entry Value type is determined by the associated
+// FieldDescripto.Kind and cannot be a Map or Vector.
+//
+// List, Len, Get, and Range are safe for concurrent access.
+type Map interface {
+ // List returns an unordered list of keys for all entries in the map.
+ List() []MapKey
+
+ // Len reports the number of elements in the map.
+ //
+ // Invariant: f.Len() == len(f.List())
+ Len() int
+
+ // Get retrieves the value for an entry with the given key.
+ Get(MapKey) Value
+
+ // Set stores the value for an entry with the given key.
+ //
+ // When setting a composite type, it is unspecified whether the set
+ // value aliases the source's memory in any way.
+ //
+ // It panics if either the key or value are Null.
+ Set(MapKey, Value)
+
+ // Mutable returns a Mutable reference for the element with a given key,
+ // allocating a new entry if necessary.
+ //
+ // The returned Mutable reference is never nil, and is only valid until the
+ // next Set or Mutable call.
+ Mutable(MapKey) Mutable
+
+ // Range calls f sequentially for each key and value present in the map.
+ // If f returns false, range stops the iteration.
+ Range(f func(MapKey, Value) bool)
+
+ // ProtoMutable is a marker method to implement the Mutable interface.
+ ProtoMutable()
+}
+
+// Mutable is a mutable reference, where mutate operations also affect
+// the backing store. Possible Mutable types: Vector, Map, or Message.
+type Mutable interface{ ProtoMutable() }
+
+// 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 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 |
+// | float32 | FloatKind |
+// | float64 | DoubleKind |
+// | string | StringKind |
+// | []byte | BytesKind |
+// | EnumNumber | EnumKind |
+// +------------+-------------------------------------+
+// | Message | MessageKind, GroupKind |
+// | Vector | |
+// | Map | |
+// +------------+-------------------------------------+
+//
+// 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 all represent int64,
+// but use different integer encoding methods.
+//
+// The Vector or Map types are used if the FieldDescriptor.Cardinality of the
+// corresponding field is Repeated and a Map if and only if
+// FieldDescriptor.IsMap is 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
+
+// Null is an unpopulated Value.
+//
+// Since Value is incomparable, call Value.IsNull instead to test whether
+// a Value is empty.
+//
+// It is equivalent to Value{} or ValueOf(nil).
+var Null Value
+
+// 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
diff --git a/reflect/protoreflect/value_pure.go b/reflect/protoreflect/value_pure.go
new file mode 100644
index 0000000..d9ecc9b
--- /dev/null
+++ b/reflect/protoreflect/value_pure.go
@@ -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
+
+package protoreflect
+
+import "google.golang.org/proto/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
+}
diff --git a/reflect/protoreflect/value_test.go b/reflect/protoreflect/value_test.go
new file mode 100644
index 0000000..69d9966
--- /dev/null
+++ b/reflect/protoreflect/value_test.go
@@ -0,0 +1,148 @@
+// 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 (
+ "bytes"
+ "math"
+ "reflect"
+ "testing"
+)
+
+func TestValue(t *testing.T) {
+ fakeMessage := new(struct{ Message })
+ fakeVector := new(struct{ Vector })
+ fakeMap := new(struct{ Map })
+
+ tests := []struct {
+ in Value
+ want interface{}
+ }{
+ {in: Null},
+ {in: Value{}},
+ {in: ValueOf(nil)},
+ {in: ValueOf(true), want: true},
+ {in: ValueOf(int32(math.MaxInt32)), want: int32(math.MaxInt32)},
+ {in: ValueOf(int64(math.MaxInt64)), want: int64(math.MaxInt64)},
+ {in: ValueOf(uint32(math.MaxUint32)), want: uint32(math.MaxUint32)},
+ {in: ValueOf(uint64(math.MaxUint64)), want: uint64(math.MaxUint64)},
+ {in: ValueOf(float32(math.MaxFloat32)), want: float32(math.MaxFloat32)},
+ {in: ValueOf(float64(math.MaxFloat64)), want: float64(math.MaxFloat64)},
+ {in: ValueOf(string("hello")), want: string("hello")},
+ {in: ValueOf([]byte("hello")), want: []byte("hello")},
+ {in: ValueOf(fakeMessage), want: fakeMessage},
+ {in: ValueOf(fakeVector), want: fakeVector},
+ {in: ValueOf(fakeMap), want: fakeMap},
+ }
+
+ for _, tt := range tests {
+ got := tt.in.Interface()
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("Value(%v).Interface() = %v, want %v", tt.in, got, tt.want)
+ }
+
+ if got := tt.in.IsNull(); got != (tt.want == nil) {
+ t.Errorf("Value(%v).IsNull() = %v, want %v", tt.in, got, tt.want == nil)
+ }
+ switch want := tt.want.(type) {
+ case int32:
+ if got := tt.in.Int(); got != int64(want) {
+ t.Errorf("Value(%v).Int() = %v, want %v", tt.in, got, tt.want)
+ }
+ case int64:
+ if got := tt.in.Int(); got != int64(want) {
+ t.Errorf("Value(%v).Int() = %v, want %v", tt.in, got, tt.want)
+ }
+ case uint32:
+ if got := tt.in.Uint(); got != uint64(want) {
+ t.Errorf("Value(%v).Uint() = %v, want %v", tt.in, got, tt.want)
+ }
+ case uint64:
+ if got := tt.in.Uint(); got != uint64(want) {
+ t.Errorf("Value(%v).Uint() = %v, want %v", tt.in, got, tt.want)
+ }
+ case float32:
+ if got := tt.in.Float(); got != float64(want) {
+ t.Errorf("Value(%v).Float() = %v, want %v", tt.in, got, tt.want)
+ }
+ case float64:
+ if got := tt.in.Float(); got != float64(want) {
+ t.Errorf("Value(%v).Float() = %v, want %v", tt.in, got, tt.want)
+ }
+ case string:
+ if got := tt.in.String(); got != string(want) {
+ t.Errorf("Value(%v).String() = %v, want %v", tt.in, got, tt.want)
+ }
+ case []byte:
+ if got := tt.in.Bytes(); !bytes.Equal(got, want) {
+ t.Errorf("Value(%v).Bytes() = %v, want %v", tt.in, got, tt.want)
+ }
+ case EnumNumber:
+ if got := tt.in.Enum(); got != want {
+ t.Errorf("Value(%v).Enum() = %v, want %v", tt.in, got, tt.want)
+ }
+ case Message:
+ if got := tt.in.Message(); got != want {
+ t.Errorf("Value(%v).Message() = %v, want %v", tt.in, got, tt.want)
+ }
+ case Map:
+ if got := tt.in.Map(); got != want {
+ t.Errorf("Value(%v).Map() = %v, want %v", tt.in, got, tt.want)
+ }
+ case Vector:
+ if got := tt.in.Vector(); got != want {
+ t.Errorf("Value(%v).Vector() = %v, want %v", tt.in, got, tt.want)
+ }
+ }
+ }
+}
+
+func BenchmarkValue(b *testing.B) {
+ const testdata = "The quick brown fox jumped over the lazy dog."
+ var sink1 string
+ var sink2 Value
+ var sink3 interface{}
+
+ // Baseline measures the time to store a string into a native variable.
+ b.Run("Baseline", func(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ sink1 = testdata[:len(testdata)%(i+1)]
+ }
+ })
+
+ // Inline measures the time to store a string into a Value,
+ // assuming that the compiler could inline the ValueOf function call.
+ b.Run("Inline", func(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ sink2 = valueOfString(testdata[:len(testdata)%(i+1)])
+ }
+ })
+
+ // Value measures the time to store a string into a Value using the general
+ // ValueOf function call. This should be identical to Inline.
+ //
+ // NOTE: As of Go1.11, this is not as efficient as Inline due to the lack
+ // of some compiler optimizations:
+ // https://golang.org/issue/22310
+ // https://golang.org/issue/25189
+ b.Run("Value", func(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ sink2 = ValueOf(string(testdata[:len(testdata)%(i+1)]))
+ }
+ })
+
+ // Interface measures the time to store a string into an interface.
+ b.Run("Interface", func(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ sink3 = string(testdata[:len(testdata)%(i+1)])
+ }
+ })
+
+ _, _, _ = sink1, sink2, sink3
+}
diff --git a/reflect/protoreflect/value_union.go b/reflect/protoreflect/value_union.go
new file mode 100644
index 0000000..ba82d31
--- /dev/null
+++ b/reflect/protoreflect/value_union.go
@@ -0,0 +1,251 @@
+// 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"
+ "reflect"
+)
+
+// 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.
+//
+// After calling ValueOf on a []byte, the slice must no longer be mutated.
+func ValueOf(v interface{}) Value {
+ switch v := v.(type) {
+ case nil:
+ return Value{}
+ case bool:
+ if v {
+ return Value{typ: boolType, num: 1}
+ } else {
+ return Value{typ: boolType, num: 0}
+ }
+ case int32:
+ return Value{typ: int32Type, num: uint64(v)}
+ case int64:
+ return Value{typ: int64Type, num: uint64(v)}
+ case uint32:
+ return Value{typ: uint32Type, num: uint64(v)}
+ case uint64:
+ return Value{typ: uint64Type, num: uint64(v)}
+ case float32:
+ return Value{typ: float32Type, num: uint64(math.Float64bits(float64(v)))}
+ case float64:
+ return Value{typ: float64Type, num: uint64(math.Float64bits(float64(v)))}
+ case string:
+ return valueOfString(v)
+ case []byte:
+ return valueOfBytes(v[:len(v):len(v)])
+ case EnumNumber:
+ return Value{typ: enumType, num: uint64(v)}
+ case Message, Vector, Map:
+ return valueOfIface(v)
+ default:
+ // TODO: Special case ProtoEnum, ProtoMessage, *[]T, and *map[K]V?
+ // Note: this would violate the documented invariant in Interface.
+ panic(fmt.Sprintf("invalid type: %v", reflect.TypeOf(v)))
+ }
+}
+
+// IsNull reports whether v is empty (has no value).
+func (v Value) IsNull() bool {
+ return v.typ == nilType
+}
+
+// Interface returns v as an interface{}.
+// Returned []byte values must not be mutated.
+//
+// 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()
+ }
+}
+
+// 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("proto: value type mismatch")
+ }
+}
+
+// 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("proto: value type mismatch")
+ }
+}
+
+// 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("proto: value type mismatch")
+ }
+}
+
+// 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("proto: value type mismatch")
+ }
+}
+
+// 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.
+// The returned slice must not be mutated.
+func (v Value) Bytes() []byte {
+ switch v.typ {
+ case bytesType:
+ return v.getBytes()
+ default:
+ panic("proto: value type mismatch")
+ }
+}
+
+// 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("proto: value type mismatch")
+ }
+}
+
+// Message returns v as a Message and panics if the type is not a Message.
+func (v Value) Message() Message {
+ switch v := v.getIface().(type) {
+ case Message:
+ return v
+ default:
+ panic("proto: value type mismatch")
+ }
+}
+
+// Vector returns v as a Vector and panics if the type is not a Vector.
+func (v Value) Vector() Vector {
+ switch v := v.getIface().(type) {
+ case Vector:
+ return v
+ default:
+ panic("proto: value type mismatch")
+ }
+}
+
+// Map returns v as a Map and panics if the type is not a Map.
+func (v Value) Map() Map {
+ switch v := v.getIface().(type) {
+ case Map:
+ return v
+ default:
+ panic("proto: value type mismatch")
+ }
+}
+
+// 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)
+ }
+ panic("proto: invalid map key type")
+}
+
+// IsNull reports whether v is empty (has no value).
+func (k MapKey) IsNull() bool {
+ return Value(k).IsNull()
+}
+
+// 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)
+}
diff --git a/reflect/protoreflect/value_unsafe.go b/reflect/protoreflect/value_unsafe.go
new file mode 100644
index 0000000..b315420
--- /dev/null
+++ b/reflect/protoreflect/value_unsafe.go
@@ -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
+
+package protoreflect
+
+import (
+ "unsafe"
+
+ "google.golang.org/proto/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
+}
diff --git a/test.bash b/test.bash
index 99c8a7f..c010f94 100755
--- a/test.bash
+++ b/test.bash
@@ -77,6 +77,7 @@

go build ./...
go test -race ./...
+ go test -race -tags purego ./...
go test -race -tags proto1_legacy ./...
done


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

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: I11fa3874e4b3f94771514c69efb2ae8bb812d7fa
Gerrit-Change-Number: 127823
Gerrit-PatchSet: 10
Gerrit-Owner: Joe Tsai <joe...@digital-static.net>
Gerrit-Reviewer: Chris Manghane <cm...@golang.org>
Gerrit-Reviewer: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Herbie Ong <her...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-MessageType: merged
Reply all
Reply to author
Forward
0 new messages