reflect/prototype: initial commit

The prototype package provides constructors to create protobuf types that
implement the interfaces defined in the protoreflect package.

High-level API:
	func NewFile(t *File) (protoreflect.FileDescriptor, error)
	type File struct{ ... }
	type Message struct{ ... }
	type Field struct{ ... }
	type Oneof struct{ ... }
	type Enum struct{ ... }
	type EnumValue struct{ ... }
	type Extension struct{ ... }
	type Service struct{ ... }
	type Method struct{ ... }

	func NewEnum(t *StandaloneEnum) (protoreflect.EnumDescriptor, error)
	func NewMessage(t *StandaloneMessage) (protoreflect.MessageDescriptor, error)
	func NewExtension(t *StandaloneExtension) (protoreflect.ExtensionDescriptor, error)
	type StandaloneEnum struct{ ... }
	type StandaloneMessage struct{ ... }
	type StandaloneExtension struct{ ... }

	func PlaceholderFile(path string, pkg protoreflect.FullName) protoreflect.FileDescriptor
	func PlaceholderEnum(name protoreflect.FullName) protoreflect.EnumDescriptor
	func PlaceholderMessage(name protoreflect.FullName) protoreflect.MessageDescriptor

This CL is missing some features that are to be added later:
* The stringer methods are not implemented, providing no way to print the
descriptors in a humanly readable manner.
* There is no support for proto options or retrieving the raw descriptor.
* There are constructors for Go specific types (e.g., protoreflect.MessageType).

We drop go1.9 support since we use strings.Builder.
We switch to go.11rc1 to obtain some bug fixes for modules.

Change-Id: Ieac9a2530afc81e5a5bb9ab5816804372f652b18
Reviewed-on: https://go-review.googlesource.com/129057
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Herbie Ong <herbie@google.com>
This commit is contained in:
Joe Tsai 2018-08-10 13:58:07 -07:00 committed by Joe Tsai
parent dbc9a12dbd
commit bfda014ecd
14 changed files with 2395 additions and 3 deletions

View File

@ -0,0 +1,244 @@
// 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.
//go:generate go run . -execute
package main
import (
"flag"
"go/format"
"io/ioutil"
"os"
"os/exec"
"path"
"regexp"
"strconv"
"strings"
"text/template"
"unicode"
)
var run = flag.Bool("execute", false, "Write generated files to destination.")
func main() {
flag.Parse()
chdirRoot()
writeSource("reflect/prototype/protofile_list_gen.go", generateListTypes())
}
// chdirRoot changes the working directory to the repository root.
func chdirRoot() {
out, err := exec.Command("git", "rev-parse", "--show-toplevel").CombinedOutput()
if err != nil {
panic(err)
}
if err := os.Chdir(strings.TrimSuffix(string(out), "\n")); err != nil {
panic(err)
}
}
// Expr is a single line Go expression.
type Expr string
type DescriptorType string
const (
MessageDesc DescriptorType = "Message"
FieldDesc DescriptorType = "Field"
OneofDesc DescriptorType = "Oneof"
ExtensionDesc DescriptorType = "Extension"
EnumDesc DescriptorType = "Enum"
EnumValueDesc DescriptorType = "EnumValue"
ServiceDesc DescriptorType = "Service"
MethodDesc DescriptorType = "Method"
)
func (d DescriptorType) Expr() Expr {
return "protoreflect." + Expr(d) + "Descriptor"
}
func (d DescriptorType) NumberExpr() Expr {
switch d {
case FieldDesc:
return "protoreflect.FieldNumber"
case EnumValueDesc:
return "protoreflect.EnumNumber"
default:
return ""
}
}
func generateListTypes() string {
// TODO: If Go2 has generics, replace this with a single container type.
return mustExecute(listTypesTemplate, []DescriptorType{
MessageDesc, FieldDesc, OneofDesc, ExtensionDesc, EnumDesc, EnumValueDesc, ServiceDesc, MethodDesc,
})
}
var listTypesTemplate = template.Must(template.New("").Funcs(template.FuncMap{
"unexport": func(t DescriptorType) Expr {
return Expr(string(unicode.ToLower(rune(t[0]))) + string(t[1:]))
},
}).Parse(`
{{- range .}}
{{$nameList := (printf "%ss" (unexport .))}} {{/* e.g., "messages" */}}
{{$nameListMeta := (printf "%ssMeta" (unexport .))}} {{/* e.g., "messagesMeta" */}}
{{$nameMeta := (printf "%sMeta" (unexport .))}} {{/* e.g., "messageMeta" */}}
{{$nameDesc := (printf "%sDesc" (unexport .))}} {{/* e.g., "messageDesc" */}}
type {{$nameListMeta}} struct {
once sync.Once
typs []{{.}}
nameOnce sync.Once
byName map[protoreflect.Name]*{{.}}
{{- if (eq . "Field")}}
jsonOnce sync.Once
byJSON map[string]*{{.}}
{{- end}}
{{- if .NumberExpr}}
numOnce sync.Once
byNum map[{{.NumberExpr}}]*{{.}}
{{- end}}
}
type {{$nameList}} {{$nameListMeta}}
func (p *{{$nameListMeta}}) lazyInit(parent protoreflect.Descriptor, ts []{{.}}) *{{$nameList}} {
p.once.Do(func() {
nb := nameBuilderPool.Get().(*nameBuilder)
defer nameBuilderPool.Put(nb)
metas := make([]{{$nameMeta}}, len(ts))
for i := range ts {
t := &ts[i]
if t.{{$nameMeta}} != nil {
panic("already initialized")
}
t.{{$nameMeta}} = &metas[i]
t.inheritedMeta.init(nb, parent, t.Name, {{printf "%v" (eq . "EnumValue")}})
}
p.typs = ts
})
return (*{{$nameList}})(p)
}
func (p *{{$nameList}}) Len() int { return len(p.typs) }
func (p *{{$nameList}}) Get(i int) {{.Expr}} { return {{$nameDesc}}{&p.typs[i]} }
func (p *{{$nameList}}) ByName(s protoreflect.Name) {{.Expr}} {
p.nameOnce.Do(func() {
if len(p.typs) > 0 {
p.byName = make(map[protoreflect.Name]*{{.}}, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
p.byName[t.Name] = t
}
}
})
t := p.byName[s]
if t == nil {
return nil
}
return {{$nameDesc}}{t}
}
{{- if (eq . "Field")}}
func (p *{{$nameList}}) ByJSONName(s string) {{.Expr}} {
p.jsonOnce.Do(func() {
if len(p.typs) > 0 {
p.byJSON = make(map[string]*{{.}}, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
s := {{$nameDesc}}{t}.JSONName()
if _, ok := p.byJSON[s]; !ok {
p.byJSON[s] = t
}
}
}
})
t := p.byJSON[s]
if t == nil {
return nil
}
return {{$nameDesc}}{t}
}
{{- end}}
{{- if .NumberExpr}}
func (p *{{$nameList}}) ByNumber(n {{.NumberExpr}}) {{.Expr}} {
p.numOnce.Do(func() {
if len(p.typs) > 0 {
p.byNum = make(map[{{.NumberExpr}}]*{{.}}, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
if _, ok := p.byNum[t.Number]; !ok {
p.byNum[t.Number] = t
}
}
}
})
t := p.byNum[n]
if t == nil {
return nil
}
return {{$nameDesc}}{t}
}
{{- end}}
func (p *{{$nameList}}) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *{{$nameList}}) ProtoInternal(pragma.DoNotImplement) {}
{{- end}}
`))
func mustExecute(t *template.Template, data interface{}) string {
var sb strings.Builder
if err := t.Execute(&sb, data); err != nil {
panic(err)
}
return sb.String()
}
func writeSource(file, src string) {
// Crude but effective way to detect used imports.
var imports []string
for _, pkg := range []string{
"fmt",
"sync",
"",
"google.golang.org/proto/internal/pragma",
"google.golang.org/proto/reflect/protoreflect",
} {
if pkg == "" {
imports = append(imports, "") // blank line between stdlib and proto packages
} else if regexp.MustCompile(`[^\pL_0-9]` + path.Base(pkg) + `\.`).MatchString(src) {
imports = append(imports, strconv.Quote(pkg))
}
}
s := strings.Join([]string{
"// 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.",
"",
"// Code generated by generate-types. DO NOT EDIT.",
"",
"package " + path.Base(path.Dir(path.Join("proto", file))),
"",
"import (" + strings.Join(imports, "\n") + ")",
"",
src,
}, "\n")
b, err := format.Source([]byte(s))
if err != nil {
panic(err)
}
if *run {
if err := ioutil.WriteFile(file, b, 0664); err != nil {
panic(err)
}
} else {
if err := ioutil.WriteFile(file+".tmp", b, 0664); err != nil {
panic(err)
}
defer os.Remove(file + ".tmp")
cmd := exec.Command("diff", file, file+".tmp", "-u")
cmd.Stdout = os.Stdout
cmd.Run()
}
}

View File

@ -0,0 +1,29 @@
// 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 prototype
import (
pref "google.golang.org/proto/reflect/protoreflect"
)
// TODO: This cannot be implemented without proto.Unmarshal.
type descriptorFileMeta struct{}
func (p *descriptorFileMeta) lazyInit(t fileDesc) (pref.Message, bool) {
return nil, false
}
type descriptorSubMeta struct{}
func (p *descriptorSubMeta) lazyInit(t pref.Descriptor) (pref.Message, bool) {
return nil, false
}
type descriptorOptionsMeta struct{}
func (p *descriptorOptionsMeta) lazyInit(t pref.Descriptor) (pref.DescriptorOptions, bool) {
return nil, false
}

View File

@ -0,0 +1,32 @@
// 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 prototype
import "google.golang.org/proto/reflect/protoreflect"
// PlaceholderFile returns a placeholder protoreflect.FileType where
// only the Path and Package accessors are valid.
func PlaceholderFile(path string, pkg protoreflect.FullName) protoreflect.FileDescriptor {
// TODO: Is Package needed for placeholders?
return placeholderFile{path, placeholderName(pkg)}
}
// PlaceholderMessage returns a placeholder protoreflect.MessageType
// where only the Name and FullName accessors are valid.
//
// A placeholder can be used within File literals when referencing a message
// that is declared within that file.
func PlaceholderMessage(name protoreflect.FullName) protoreflect.MessageDescriptor {
return placeholderMessage{placeholderName(name)}
}
// PlaceholderEnum returns a placeholder protoreflect.EnumType
// where only the Name and FullName accessors are valid.
//
// A placeholder can be used within File literals when referencing an enum
// that is declared within that file.
func PlaceholderEnum(name protoreflect.FullName) protoreflect.EnumDescriptor {
return placeholderEnum{placeholderName(name)}
}

View File

@ -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 prototype
import (
"fmt"
"google.golang.org/proto/internal/pragma"
pref "google.golang.org/proto/reflect/protoreflect"
)
var (
emptyFiles fileImports
emptyMessages messages
emptyFields fields
emptyOneofs oneofs
emptyNumbers numbers
emptyRanges ranges
emptyEnums enums
emptyEnumValues enumValues
emptyExtensions extensions
emptyServices services
)
type placeholderName pref.FullName
func (t placeholderName) Parent() (pref.Descriptor, bool) { return nil, false }
func (t placeholderName) Syntax() pref.Syntax { return 0 }
func (t placeholderName) Name() pref.Name { return pref.FullName(t).Name() }
func (t placeholderName) FullName() pref.FullName { return pref.FullName(t) }
func (t placeholderName) IsPlaceholder() bool { return true }
func (t placeholderName) DescriptorProto() (pref.Message, bool) { return nil, false }
func (t placeholderName) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
func (t placeholderName) ProtoInternal(pragma.DoNotImplement) {}
type placeholderFile struct {
path string
placeholderName
}
func (t placeholderFile) Path() string { return t.path }
func (t placeholderFile) Package() pref.FullName { return t.FullName() }
func (t placeholderFile) Imports() pref.FileImports { return &emptyFiles }
func (t placeholderFile) Messages() pref.MessageDescriptors { return &emptyMessages }
func (t placeholderFile) Enums() pref.EnumDescriptors { return &emptyEnums }
func (t placeholderFile) Extensions() pref.ExtensionDescriptors { return &emptyExtensions }
func (t placeholderFile) Services() pref.ServiceDescriptors { return &emptyServices }
func (t placeholderFile) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t placeholderFile) ProtoType(pref.FileDescriptor) {}
type placeholderMessage struct {
placeholderName
}
func (t placeholderMessage) IsMapEntry() bool { return false }
func (t placeholderMessage) Fields() pref.FieldDescriptors { return &emptyFields }
func (t placeholderMessage) Oneofs() pref.OneofDescriptors { return &emptyOneofs }
func (t placeholderMessage) RequiredNumbers() pref.FieldNumbers { return &emptyNumbers }
func (t placeholderMessage) ExtensionRanges() pref.FieldRanges { return &emptyRanges }
func (t placeholderMessage) Messages() pref.MessageDescriptors { return &emptyMessages }
func (t placeholderMessage) Enums() pref.EnumDescriptors { return &emptyEnums }
func (t placeholderMessage) Extensions() pref.ExtensionDescriptors { return &emptyExtensions }
func (t placeholderMessage) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t placeholderMessage) ProtoType(pref.MessageDescriptor) {}
type placeholderEnum struct {
placeholderName
}
func (t placeholderEnum) Values() pref.EnumValueDescriptors { return &emptyEnumValues }
func (t placeholderEnum) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t placeholderEnum) ProtoType(pref.EnumDescriptor) {}

View File

@ -0,0 +1,161 @@
// 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 prototype provides builders to construct protobuf types that
// implement the interfaces defined in the protoreflect package.
//
// Protobuf types can either be constructed as standalone types
// (e.g., StandaloneMessage), or together as a batch of types in a single
// proto file (e.g., File). When creating standalone types, additional
// information must be provided such as the full type name and the proto syntax.
// When creating an entire file, the syntax and full name is derived from
// the parent type.
package prototype
import (
"google.golang.org/proto/reflect/protoreflect"
)
// Every struct has a "meta" struct embedded within it as a pointer.
// The meta type provides additional data structures for efficient lookup on
// certain methods (e.g., ByName) or derived information that can be
// derived from the parent (e.g., FullName). The meta type is lazily allocated
// and initialized. This architectural approach keeps the literal representation
// smaller, which then keeps the generated code size smaller.
// TODO: Support initializing File from a google.protobuf.FileDescriptor?
// TODO: Instead of a top-down construction approach where internal references
// to message types use placeholder types, we could add a Reference method
// on Message and Enum that creates a MessageDescriptor or EnumDescriptor
// reference that only becomes valid after NewFile.
// However, that API approach is more error prone, as it causes more memory
// aliasing and provides more opportunity for misuse.
// Also, it requires that NewFile at least eagerly initialize all
// messages and enums list types. We can always add that API in the future.
// File is a constructor for protoreflect.FileDescriptor.
type File struct {
Syntax protoreflect.Syntax
Path string
Package protoreflect.FullName
Imports []protoreflect.FileImport
Messages []Message
Enums []Enum
Extensions []Extension
Services []Service
*fileMeta
}
// NewFile creates a new protoreflect.FileDescriptor from the provided value.
// The file must represent a valid proto file according to protobuf semantics.
//
// Fields that reference an enum or message that is being declared within the
// same File can be represented using a placeholder descriptor. NewFile will
// automatically resolve the placeholder to point to the concrete type.
//
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields. The input must not contain slices that are
// sub-slices of each other.
func NewFile(t *File) (protoreflect.FileDescriptor, error) {
// TODO: Provide an unverified make that avoids validating the file.
// This is useful for generated code since we know that protoc-gen-go
// already validated the protobuf types.
ft := newFile(t)
if err := validateFile(ft); err != nil {
return nil, err
}
return ft, nil
}
// Message is a constructor for protoreflect.MessageDescriptor.
type Message struct {
Name protoreflect.Name
IsMapEntry bool
Fields []Field
Oneofs []Oneof
ExtensionRanges [][2]protoreflect.FieldNumber
Messages []Message
Enums []Enum
Extensions []Extension
*messageMeta
}
// Field is a constructor for protoreflect.FieldDescriptor.
type Field struct {
Name protoreflect.Name
Number protoreflect.FieldNumber
Cardinality protoreflect.Cardinality
Kind protoreflect.Kind
JSONName string
IsPacked bool
IsWeak bool
Default protoreflect.Value
OneofName protoreflect.Name
MessageType protoreflect.MessageDescriptor
EnumType protoreflect.EnumDescriptor
*fieldMeta
}
// Oneof is a constructor for protoreflect.OneofDescriptor.
type Oneof struct {
Name protoreflect.Name
*oneofMeta
}
// Extension is a constructor for protoreflect.ExtensionDescriptor.
type Extension struct {
Name protoreflect.Name
Number protoreflect.FieldNumber
Cardinality protoreflect.Cardinality
Kind protoreflect.Kind
IsPacked bool
Default protoreflect.Value
MessageType protoreflect.MessageDescriptor
EnumType protoreflect.EnumDescriptor
ExtendedType protoreflect.MessageDescriptor
*extensionMeta
}
// Enum is a constructor for protoreflect.EnumDescriptor.
type Enum struct {
Name protoreflect.Name
Values []EnumValue
*enumMeta
}
// EnumValue is a constructor for protoreflect.EnumValueDescriptor.
type EnumValue struct {
Name protoreflect.Name
Number protoreflect.EnumNumber
*enumValueMeta
}
// Service is a constructor for protoreflect.ServiceDescriptor.
type Service struct {
Name protoreflect.Name
Methods []Method
*serviceMeta
}
// Method is a constructor for protoreflect.MethodDescriptor.
type Method struct {
Name protoreflect.Name
InputType protoreflect.MessageDescriptor
OutputType protoreflect.MessageDescriptor
IsStreamingClient bool
IsStreamingServer bool
*methodMeta
}

View File

@ -0,0 +1,100 @@
// 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 prototype
import (
"fmt"
"sync"
"google.golang.org/proto/internal/pragma"
"google.golang.org/proto/internal/set"
pref "google.golang.org/proto/reflect/protoreflect"
)
type numbersMeta struct {
once sync.Once
ns []pref.FieldNumber
nss set.Ints
}
type numbers numbersMeta
func (p *numbersMeta) lazyInit(fs []Field) *numbers {
p.once.Do(func() {
for i := range fs {
if f := &fs[i]; f.Cardinality == pref.Required {
p.ns = append(p.ns, f.Number)
p.nss.Set(uint64(f.Number))
}
}
})
return (*numbers)(p)
}
func (p *numbers) Len() int { return len(p.ns) }
func (p *numbers) Get(i int) pref.FieldNumber { return p.ns[i] }
func (p *numbers) Has(n pref.FieldNumber) bool { return p.nss.Has(uint64(n)) }
func (p *numbers) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *numbers) ProtoInternal(pragma.DoNotImplement) {}
type ranges [][2]pref.FieldNumber
func (p *ranges) Len() int { return len(*p) }
func (p *ranges) Get(i int) [2]pref.FieldNumber { return (*p)[i] }
func (p *ranges) Has(n pref.FieldNumber) bool {
for _, r := range *p {
if r[0] <= n && n < r[1] {
return true
}
}
return false
}
func (p *ranges) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *ranges) ProtoInternal(pragma.DoNotImplement) {}
type fileImports []pref.FileImport
func (p *fileImports) Len() int { return len(*p) }
func (p *fileImports) Get(i int) pref.FileImport { return (*p)[i] }
func (p *fileImports) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *fileImports) ProtoInternal(pragma.DoNotImplement) {}
type oneofFieldsMeta struct {
once sync.Once
typs []pref.FieldDescriptor
byName map[pref.Name]pref.FieldDescriptor
byJSON map[string]pref.FieldDescriptor
byNum map[pref.FieldNumber]pref.FieldDescriptor
}
type oneofFields oneofFieldsMeta
func (p *oneofFieldsMeta) lazyInit(parent pref.Descriptor) *oneofFields {
p.once.Do(func() {
otyp := parent.(pref.OneofDescriptor)
mtyp, _ := parent.Parent()
fs := mtyp.(pref.MessageDescriptor).Fields()
for i := 0; i < fs.Len(); i++ {
if f := fs.Get(i); otyp == f.OneofType() {
p.typs = append(p.typs, f)
}
}
if len(p.typs) > 0 {
p.byName = make(map[pref.Name]pref.FieldDescriptor, len(p.typs))
p.byJSON = make(map[string]pref.FieldDescriptor, len(p.typs))
p.byNum = make(map[pref.FieldNumber]pref.FieldDescriptor, len(p.typs))
for _, f := range p.typs {
p.byName[f.Name()] = f
p.byJSON[f.JSONName()] = f
p.byNum[f.Number()] = f
}
}
})
return (*oneofFields)(p)
}
func (p *oneofFields) Len() int { return len(p.typs) }
func (p *oneofFields) Get(i int) pref.FieldDescriptor { return p.typs[i] }
func (p *oneofFields) ByName(s pref.Name) pref.FieldDescriptor { return p.byName[s] }
func (p *oneofFields) ByJSONName(s string) pref.FieldDescriptor { return p.byJSON[s] }
func (p *oneofFields) ByNumber(n pref.FieldNumber) pref.FieldDescriptor { return p.byNum[n] }
func (p *oneofFields) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *oneofFields) ProtoInternal(pragma.DoNotImplement) {}

View File

@ -0,0 +1,444 @@
// 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.
// Code generated by generate-types. DO NOT EDIT.
package prototype
import (
"fmt"
"sync"
"google.golang.org/proto/internal/pragma"
"google.golang.org/proto/reflect/protoreflect"
)
type messagesMeta struct {
once sync.Once
typs []Message
nameOnce sync.Once
byName map[protoreflect.Name]*Message
}
type messages messagesMeta
func (p *messagesMeta) lazyInit(parent protoreflect.Descriptor, ts []Message) *messages {
p.once.Do(func() {
nb := nameBuilderPool.Get().(*nameBuilder)
defer nameBuilderPool.Put(nb)
metas := make([]messageMeta, len(ts))
for i := range ts {
t := &ts[i]
if t.messageMeta != nil {
panic("already initialized")
}
t.messageMeta = &metas[i]
t.inheritedMeta.init(nb, parent, t.Name, false)
}
p.typs = ts
})
return (*messages)(p)
}
func (p *messages) Len() int { return len(p.typs) }
func (p *messages) Get(i int) protoreflect.MessageDescriptor { return messageDesc{&p.typs[i]} }
func (p *messages) ByName(s protoreflect.Name) protoreflect.MessageDescriptor {
p.nameOnce.Do(func() {
if len(p.typs) > 0 {
p.byName = make(map[protoreflect.Name]*Message, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
p.byName[t.Name] = t
}
}
})
t := p.byName[s]
if t == nil {
return nil
}
return messageDesc{t}
}
func (p *messages) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *messages) ProtoInternal(pragma.DoNotImplement) {}
type fieldsMeta struct {
once sync.Once
typs []Field
nameOnce sync.Once
byName map[protoreflect.Name]*Field
jsonOnce sync.Once
byJSON map[string]*Field
numOnce sync.Once
byNum map[protoreflect.FieldNumber]*Field
}
type fields fieldsMeta
func (p *fieldsMeta) lazyInit(parent protoreflect.Descriptor, ts []Field) *fields {
p.once.Do(func() {
nb := nameBuilderPool.Get().(*nameBuilder)
defer nameBuilderPool.Put(nb)
metas := make([]fieldMeta, len(ts))
for i := range ts {
t := &ts[i]
if t.fieldMeta != nil {
panic("already initialized")
}
t.fieldMeta = &metas[i]
t.inheritedMeta.init(nb, parent, t.Name, false)
}
p.typs = ts
})
return (*fields)(p)
}
func (p *fields) Len() int { return len(p.typs) }
func (p *fields) Get(i int) protoreflect.FieldDescriptor { return fieldDesc{&p.typs[i]} }
func (p *fields) ByName(s protoreflect.Name) protoreflect.FieldDescriptor {
p.nameOnce.Do(func() {
if len(p.typs) > 0 {
p.byName = make(map[protoreflect.Name]*Field, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
p.byName[t.Name] = t
}
}
})
t := p.byName[s]
if t == nil {
return nil
}
return fieldDesc{t}
}
func (p *fields) ByJSONName(s string) protoreflect.FieldDescriptor {
p.jsonOnce.Do(func() {
if len(p.typs) > 0 {
p.byJSON = make(map[string]*Field, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
s := fieldDesc{t}.JSONName()
if _, ok := p.byJSON[s]; !ok {
p.byJSON[s] = t
}
}
}
})
t := p.byJSON[s]
if t == nil {
return nil
}
return fieldDesc{t}
}
func (p *fields) ByNumber(n protoreflect.FieldNumber) protoreflect.FieldDescriptor {
p.numOnce.Do(func() {
if len(p.typs) > 0 {
p.byNum = make(map[protoreflect.FieldNumber]*Field, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
if _, ok := p.byNum[t.Number]; !ok {
p.byNum[t.Number] = t
}
}
}
})
t := p.byNum[n]
if t == nil {
return nil
}
return fieldDesc{t}
}
func (p *fields) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *fields) ProtoInternal(pragma.DoNotImplement) {}
type oneofsMeta struct {
once sync.Once
typs []Oneof
nameOnce sync.Once
byName map[protoreflect.Name]*Oneof
}
type oneofs oneofsMeta
func (p *oneofsMeta) lazyInit(parent protoreflect.Descriptor, ts []Oneof) *oneofs {
p.once.Do(func() {
nb := nameBuilderPool.Get().(*nameBuilder)
defer nameBuilderPool.Put(nb)
metas := make([]oneofMeta, len(ts))
for i := range ts {
t := &ts[i]
if t.oneofMeta != nil {
panic("already initialized")
}
t.oneofMeta = &metas[i]
t.inheritedMeta.init(nb, parent, t.Name, false)
}
p.typs = ts
})
return (*oneofs)(p)
}
func (p *oneofs) Len() int { return len(p.typs) }
func (p *oneofs) Get(i int) protoreflect.OneofDescriptor { return oneofDesc{&p.typs[i]} }
func (p *oneofs) ByName(s protoreflect.Name) protoreflect.OneofDescriptor {
p.nameOnce.Do(func() {
if len(p.typs) > 0 {
p.byName = make(map[protoreflect.Name]*Oneof, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
p.byName[t.Name] = t
}
}
})
t := p.byName[s]
if t == nil {
return nil
}
return oneofDesc{t}
}
func (p *oneofs) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *oneofs) ProtoInternal(pragma.DoNotImplement) {}
type extensionsMeta struct {
once sync.Once
typs []Extension
nameOnce sync.Once
byName map[protoreflect.Name]*Extension
}
type extensions extensionsMeta
func (p *extensionsMeta) lazyInit(parent protoreflect.Descriptor, ts []Extension) *extensions {
p.once.Do(func() {
nb := nameBuilderPool.Get().(*nameBuilder)
defer nameBuilderPool.Put(nb)
metas := make([]extensionMeta, len(ts))
for i := range ts {
t := &ts[i]
if t.extensionMeta != nil {
panic("already initialized")
}
t.extensionMeta = &metas[i]
t.inheritedMeta.init(nb, parent, t.Name, false)
}
p.typs = ts
})
return (*extensions)(p)
}
func (p *extensions) Len() int { return len(p.typs) }
func (p *extensions) Get(i int) protoreflect.ExtensionDescriptor { return extensionDesc{&p.typs[i]} }
func (p *extensions) ByName(s protoreflect.Name) protoreflect.ExtensionDescriptor {
p.nameOnce.Do(func() {
if len(p.typs) > 0 {
p.byName = make(map[protoreflect.Name]*Extension, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
p.byName[t.Name] = t
}
}
})
t := p.byName[s]
if t == nil {
return nil
}
return extensionDesc{t}
}
func (p *extensions) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *extensions) ProtoInternal(pragma.DoNotImplement) {}
type enumsMeta struct {
once sync.Once
typs []Enum
nameOnce sync.Once
byName map[protoreflect.Name]*Enum
}
type enums enumsMeta
func (p *enumsMeta) lazyInit(parent protoreflect.Descriptor, ts []Enum) *enums {
p.once.Do(func() {
nb := nameBuilderPool.Get().(*nameBuilder)
defer nameBuilderPool.Put(nb)
metas := make([]enumMeta, len(ts))
for i := range ts {
t := &ts[i]
if t.enumMeta != nil {
panic("already initialized")
}
t.enumMeta = &metas[i]
t.inheritedMeta.init(nb, parent, t.Name, false)
}
p.typs = ts
})
return (*enums)(p)
}
func (p *enums) Len() int { return len(p.typs) }
func (p *enums) Get(i int) protoreflect.EnumDescriptor { return enumDesc{&p.typs[i]} }
func (p *enums) ByName(s protoreflect.Name) protoreflect.EnumDescriptor {
p.nameOnce.Do(func() {
if len(p.typs) > 0 {
p.byName = make(map[protoreflect.Name]*Enum, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
p.byName[t.Name] = t
}
}
})
t := p.byName[s]
if t == nil {
return nil
}
return enumDesc{t}
}
func (p *enums) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *enums) ProtoInternal(pragma.DoNotImplement) {}
type enumValuesMeta struct {
once sync.Once
typs []EnumValue
nameOnce sync.Once
byName map[protoreflect.Name]*EnumValue
numOnce sync.Once
byNum map[protoreflect.EnumNumber]*EnumValue
}
type enumValues enumValuesMeta
func (p *enumValuesMeta) lazyInit(parent protoreflect.Descriptor, ts []EnumValue) *enumValues {
p.once.Do(func() {
nb := nameBuilderPool.Get().(*nameBuilder)
defer nameBuilderPool.Put(nb)
metas := make([]enumValueMeta, len(ts))
for i := range ts {
t := &ts[i]
if t.enumValueMeta != nil {
panic("already initialized")
}
t.enumValueMeta = &metas[i]
t.inheritedMeta.init(nb, parent, t.Name, true)
}
p.typs = ts
})
return (*enumValues)(p)
}
func (p *enumValues) Len() int { return len(p.typs) }
func (p *enumValues) Get(i int) protoreflect.EnumValueDescriptor { return enumValueDesc{&p.typs[i]} }
func (p *enumValues) ByName(s protoreflect.Name) protoreflect.EnumValueDescriptor {
p.nameOnce.Do(func() {
if len(p.typs) > 0 {
p.byName = make(map[protoreflect.Name]*EnumValue, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
p.byName[t.Name] = t
}
}
})
t := p.byName[s]
if t == nil {
return nil
}
return enumValueDesc{t}
}
func (p *enumValues) ByNumber(n protoreflect.EnumNumber) protoreflect.EnumValueDescriptor {
p.numOnce.Do(func() {
if len(p.typs) > 0 {
p.byNum = make(map[protoreflect.EnumNumber]*EnumValue, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
if _, ok := p.byNum[t.Number]; !ok {
p.byNum[t.Number] = t
}
}
}
})
t := p.byNum[n]
if t == nil {
return nil
}
return enumValueDesc{t}
}
func (p *enumValues) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *enumValues) ProtoInternal(pragma.DoNotImplement) {}
type servicesMeta struct {
once sync.Once
typs []Service
nameOnce sync.Once
byName map[protoreflect.Name]*Service
}
type services servicesMeta
func (p *servicesMeta) lazyInit(parent protoreflect.Descriptor, ts []Service) *services {
p.once.Do(func() {
nb := nameBuilderPool.Get().(*nameBuilder)
defer nameBuilderPool.Put(nb)
metas := make([]serviceMeta, len(ts))
for i := range ts {
t := &ts[i]
if t.serviceMeta != nil {
panic("already initialized")
}
t.serviceMeta = &metas[i]
t.inheritedMeta.init(nb, parent, t.Name, false)
}
p.typs = ts
})
return (*services)(p)
}
func (p *services) Len() int { return len(p.typs) }
func (p *services) Get(i int) protoreflect.ServiceDescriptor { return serviceDesc{&p.typs[i]} }
func (p *services) ByName(s protoreflect.Name) protoreflect.ServiceDescriptor {
p.nameOnce.Do(func() {
if len(p.typs) > 0 {
p.byName = make(map[protoreflect.Name]*Service, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
p.byName[t.Name] = t
}
}
})
t := p.byName[s]
if t == nil {
return nil
}
return serviceDesc{t}
}
func (p *services) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *services) ProtoInternal(pragma.DoNotImplement) {}
type methodsMeta struct {
once sync.Once
typs []Method
nameOnce sync.Once
byName map[protoreflect.Name]*Method
}
type methods methodsMeta
func (p *methodsMeta) lazyInit(parent protoreflect.Descriptor, ts []Method) *methods {
p.once.Do(func() {
nb := nameBuilderPool.Get().(*nameBuilder)
defer nameBuilderPool.Put(nb)
metas := make([]methodMeta, len(ts))
for i := range ts {
t := &ts[i]
if t.methodMeta != nil {
panic("already initialized")
}
t.methodMeta = &metas[i]
t.inheritedMeta.init(nb, parent, t.Name, false)
}
p.typs = ts
})
return (*methods)(p)
}
func (p *methods) Len() int { return len(p.typs) }
func (p *methods) Get(i int) protoreflect.MethodDescriptor { return methodDesc{&p.typs[i]} }
func (p *methods) ByName(s protoreflect.Name) protoreflect.MethodDescriptor {
p.nameOnce.Do(func() {
if len(p.typs) > 0 {
p.byName = make(map[protoreflect.Name]*Method, len(p.typs))
for i := range p.typs {
t := &p.typs[i]
p.byName[t.Name] = t
}
}
})
t := p.byName[s]
if t == nil {
return nil
}
return methodDesc{t}
}
func (p *methods) Format(s fmt.State, r rune) { formatList(s, r, p) }
func (p *methods) ProtoInternal(pragma.DoNotImplement) {}

View File

@ -0,0 +1,506 @@
// 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 prototype
import (
"bytes"
"fmt"
"strings"
"sync"
"google.golang.org/proto/internal/pragma"
pref "google.golang.org/proto/reflect/protoreflect"
)
// inheritedMeta is information inherited from the parent.
type inheritedMeta struct {
parent pref.Descriptor
syntax pref.Syntax
fullName pref.FullName
desc descriptorSubMeta
opts descriptorOptionsMeta
}
func (m *inheritedMeta) init(nb *nameBuilder, parent pref.Descriptor, name pref.Name, child bool) {
// Most descriptors are namespaced as a child of their parent.
// However, EnumValues are the exception in that they are namespaced
// as a sibling of the parent Enum type.
prefix := parent.FullName()
if child {
prefix = prefix.Parent()
}
m.parent = parent
m.syntax = parent.Syntax()
m.fullName = nb.Append(prefix, name)
}
type fileMeta struct {
desc descriptorFileMeta
opts descriptorOptionsMeta
ms messagesMeta
es enumsMeta
xs extensionsMeta
ss servicesMeta
}
type fileDesc struct{ f *File }
func newFile(f *File) fileDesc {
if f.fileMeta != nil {
panic("already initialized")
}
f.fileMeta = new(fileMeta)
return fileDesc{f}
}
func (t fileDesc) Parent() (pref.Descriptor, bool) { return nil, false }
func (t fileDesc) Syntax() pref.Syntax { return t.f.Syntax }
func (t fileDesc) Name() pref.Name { return t.f.Package.Name() }
func (t fileDesc) FullName() pref.FullName { return t.f.Package }
func (t fileDesc) IsPlaceholder() bool { return false }
func (t fileDesc) DescriptorProto() (pref.Message, bool) { return t.f.desc.lazyInit(t) }
func (t fileDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.f.opts.lazyInit(t) }
func (t fileDesc) Path() string { return t.f.Path }
func (t fileDesc) Package() pref.FullName { return t.f.Package }
func (t fileDesc) Imports() pref.FileImports { return (*fileImports)(&t.f.Imports) }
func (t fileDesc) Messages() pref.MessageDescriptors { return t.f.ms.lazyInit(t, t.f.Messages) }
func (t fileDesc) Enums() pref.EnumDescriptors { return t.f.es.lazyInit(t, t.f.Enums) }
func (t fileDesc) Extensions() pref.ExtensionDescriptors { return t.f.xs.lazyInit(t, t.f.Extensions) }
func (t fileDesc) Services() pref.ServiceDescriptors { return t.f.ss.lazyInit(t, t.f.Services) }
func (t fileDesc) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t fileDesc) ProtoType(pref.FileDescriptor) {}
func (t fileDesc) ProtoInternal(pragma.DoNotImplement) {}
type messageMeta struct {
inheritedMeta
fs fieldsMeta
os oneofsMeta
ns numbersMeta
ms messagesMeta
es enumsMeta
xs extensionsMeta
}
type messageDesc struct{ m *Message }
func (t messageDesc) Parent() (pref.Descriptor, bool) { return t.m.parent, true }
func (t messageDesc) Syntax() pref.Syntax { return t.m.syntax }
func (t messageDesc) Name() pref.Name { return t.m.Name }
func (t messageDesc) FullName() pref.FullName { return t.m.fullName }
func (t messageDesc) IsPlaceholder() bool { return false }
func (t messageDesc) DescriptorProto() (pref.Message, bool) { return t.m.desc.lazyInit(t) }
func (t messageDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.m.opts.lazyInit(t) }
func (t messageDesc) IsMapEntry() bool { return t.m.IsMapEntry }
func (t messageDesc) Fields() pref.FieldDescriptors { return t.m.fs.lazyInit(t, t.m.Fields) }
func (t messageDesc) Oneofs() pref.OneofDescriptors { return t.m.os.lazyInit(t, t.m.Oneofs) }
func (t messageDesc) RequiredNumbers() pref.FieldNumbers { return t.m.ns.lazyInit(t.m.Fields) }
func (t messageDesc) ExtensionRanges() pref.FieldRanges { return (*ranges)(&t.m.ExtensionRanges) }
func (t messageDesc) Messages() pref.MessageDescriptors { return t.m.ms.lazyInit(t, t.m.Messages) }
func (t messageDesc) Enums() pref.EnumDescriptors { return t.m.es.lazyInit(t, t.m.Enums) }
func (t messageDesc) Extensions() pref.ExtensionDescriptors { return t.m.xs.lazyInit(t, t.m.Extensions) }
func (t messageDesc) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t messageDesc) ProtoType(pref.MessageDescriptor) {}
func (t messageDesc) ProtoInternal(pragma.DoNotImplement) {}
type fieldMeta struct {
inheritedMeta
js jsonName
dv defaultValue
ot oneofReference
mt messageReference
et enumReference
}
type fieldDesc struct{ f *Field }
func (t fieldDesc) Parent() (pref.Descriptor, bool) { return t.f.parent, true }
func (t fieldDesc) Syntax() pref.Syntax { return t.f.syntax }
func (t fieldDesc) Name() pref.Name { return t.f.Name }
func (t fieldDesc) FullName() pref.FullName { return t.f.fullName }
func (t fieldDesc) IsPlaceholder() bool { return false }
func (t fieldDesc) DescriptorProto() (pref.Message, bool) { return t.f.desc.lazyInit(t) }
func (t fieldDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.f.opts.lazyInit(t) }
func (t fieldDesc) Number() pref.FieldNumber { return t.f.Number }
func (t fieldDesc) Cardinality() pref.Cardinality { return t.f.Cardinality }
func (t fieldDesc) Kind() pref.Kind { return t.f.Kind }
func (t fieldDesc) JSONName() string { return t.f.js.lazyInit(t.f) }
func (t fieldDesc) IsPacked() bool { return t.f.IsPacked }
func (t fieldDesc) IsMap() bool { return isMap(t) }
func (t fieldDesc) IsWeak() bool { return t.f.IsWeak }
func (t fieldDesc) Default() pref.Value { return t.f.dv.lazyInit(t, t.f.Default) }
func (t fieldDesc) OneofType() pref.OneofDescriptor { return t.f.ot.lazyInit(t, t.f.OneofName) }
func (t fieldDesc) ExtendedType() pref.MessageDescriptor { return nil }
func (t fieldDesc) MessageType() pref.MessageDescriptor { return t.f.mt.lazyInit(t, &t.f.MessageType) }
func (t fieldDesc) EnumType() pref.EnumDescriptor { return t.f.et.lazyInit(t, &t.f.EnumType) }
func (t fieldDesc) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t fieldDesc) ProtoType(pref.FieldDescriptor) {}
func (t fieldDesc) ProtoInternal(pragma.DoNotImplement) {}
func isMap(t pref.FieldDescriptor) bool {
if t.Cardinality() == pref.Repeated && t.Kind() == pref.MessageKind {
if mt := t.MessageType(); mt != nil {
return mt.IsMapEntry()
}
}
return false
}
type jsonName struct{ once sync.Once }
func (p *jsonName) lazyInit(f *Field) string {
p.once.Do(func() {
// TODO: We may need to share this logic with jsonpb for implementation
// of the FieldMask well-known type.
if f.JSONName != "" {
return
}
var b []byte
var wasUnderscore bool
for i := 0; i < len(f.Name); i++ { // proto identifiers are always ASCII
c := f.Name[i]
if c != '_' {
isLower := 'a' <= c && c <= 'z'
if wasUnderscore && isLower {
c -= 'a' - 'A'
}
b = append(b, c)
}
wasUnderscore = c == '_'
}
f.JSONName = string(b)
})
return f.JSONName
}
// oneofReference resolves the name of a oneof by searching the parent
// message for the matching OneofDescriptor declaration.
type oneofReference struct {
once sync.Once
otyp pref.OneofDescriptor
}
func (p *oneofReference) lazyInit(parent pref.Descriptor, name pref.Name) pref.OneofDescriptor {
p.once.Do(func() {
if name != "" {
mtyp, _ := parent.Parent()
p.otyp = mtyp.(pref.MessageDescriptor).Oneofs().ByName(name)
// TODO: We need validate to detect this mismatch.
}
})
return p.otyp
}
type oneofMeta struct {
inheritedMeta
fs oneofFieldsMeta
}
type oneofDesc struct{ o *Oneof }
func (t oneofDesc) Parent() (pref.Descriptor, bool) { return t.o.parent, true }
func (t oneofDesc) Syntax() pref.Syntax { return t.o.syntax }
func (t oneofDesc) Name() pref.Name { return t.o.Name }
func (t oneofDesc) FullName() pref.FullName { return t.o.fullName }
func (t oneofDesc) IsPlaceholder() bool { return false }
func (t oneofDesc) DescriptorProto() (pref.Message, bool) { return t.o.desc.lazyInit(t) }
func (t oneofDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.o.opts.lazyInit(t) }
func (t oneofDesc) Fields() pref.FieldDescriptors { return t.o.fs.lazyInit(t) }
func (t oneofDesc) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t oneofDesc) ProtoType(pref.OneofDescriptor) {}
func (t oneofDesc) ProtoInternal(pragma.DoNotImplement) {}
type extensionMeta struct {
inheritedMeta
dv defaultValue
xt messageReference
mt messageReference
et enumReference
}
type extensionDesc struct{ x *Extension }
func (t extensionDesc) Parent() (pref.Descriptor, bool) { return t.x.parent, true }
func (t extensionDesc) Syntax() pref.Syntax { return t.x.syntax }
func (t extensionDesc) Name() pref.Name { return t.x.Name }
func (t extensionDesc) FullName() pref.FullName { return t.x.fullName }
func (t extensionDesc) IsPlaceholder() bool { return false }
func (t extensionDesc) DescriptorProto() (pref.Message, bool) { return t.x.desc.lazyInit(t) }
func (t extensionDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.x.opts.lazyInit(t) }
func (t extensionDesc) Number() pref.FieldNumber { return t.x.Number }
func (t extensionDesc) Cardinality() pref.Cardinality { return t.x.Cardinality }
func (t extensionDesc) Kind() pref.Kind { return t.x.Kind }
func (t extensionDesc) JSONName() string { return "" }
func (t extensionDesc) IsPacked() bool { return t.x.IsPacked }
func (t extensionDesc) IsMap() bool { return false }
func (t extensionDesc) IsWeak() bool { return false }
func (t extensionDesc) Default() pref.Value { return t.x.dv.lazyInit(t, t.x.Default) }
func (t extensionDesc) OneofType() pref.OneofDescriptor { return nil }
func (t extensionDesc) ExtendedType() pref.MessageDescriptor {
return t.x.xt.lazyInit(t, &t.x.ExtendedType)
}
func (t extensionDesc) MessageType() pref.MessageDescriptor {
return t.x.mt.lazyInit(t, &t.x.MessageType)
}
func (t extensionDesc) EnumType() pref.EnumDescriptor { return t.x.et.lazyInit(t, &t.x.EnumType) }
func (t extensionDesc) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t extensionDesc) ProtoType(pref.FieldDescriptor) {}
func (t extensionDesc) ProtoInternal(pragma.DoNotImplement) {}
type enumMeta struct {
inheritedMeta
vs enumValuesMeta
}
type enumDesc struct{ e *Enum }
func (t enumDesc) Parent() (pref.Descriptor, bool) { return t.e.parent, true }
func (t enumDesc) Syntax() pref.Syntax { return t.e.syntax }
func (t enumDesc) Name() pref.Name { return t.e.Name }
func (t enumDesc) FullName() pref.FullName { return t.e.fullName }
func (t enumDesc) IsPlaceholder() bool { return false }
func (t enumDesc) DescriptorProto() (pref.Message, bool) { return t.e.desc.lazyInit(t) }
func (t enumDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.e.opts.lazyInit(t) }
func (t enumDesc) Values() pref.EnumValueDescriptors { return t.e.vs.lazyInit(t, t.e.Values) }
func (t enumDesc) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t enumDesc) ProtoType(pref.EnumDescriptor) {}
func (t enumDesc) ProtoInternal(pragma.DoNotImplement) {}
type enumValueMeta struct {
inheritedMeta
}
type enumValueDesc struct{ v *EnumValue }
func (t enumValueDesc) Parent() (pref.Descriptor, bool) { return t.v.parent, true }
func (t enumValueDesc) Syntax() pref.Syntax { return t.v.syntax }
func (t enumValueDesc) Name() pref.Name { return t.v.Name }
func (t enumValueDesc) FullName() pref.FullName { return t.v.fullName }
func (t enumValueDesc) IsPlaceholder() bool { return false }
func (t enumValueDesc) DescriptorProto() (pref.Message, bool) { return t.v.desc.lazyInit(t) }
func (t enumValueDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.v.opts.lazyInit(t) }
func (t enumValueDesc) Number() pref.EnumNumber { return t.v.Number }
func (t enumValueDesc) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t enumValueDesc) ProtoType(pref.EnumValueDescriptor) {}
func (t enumValueDesc) ProtoInternal(pragma.DoNotImplement) {}
type serviceMeta struct {
inheritedMeta
ms methodsMeta
}
type serviceDesc struct{ s *Service }
func (t serviceDesc) Parent() (pref.Descriptor, bool) { return t.s.parent, true }
func (t serviceDesc) Syntax() pref.Syntax { return t.s.syntax }
func (t serviceDesc) Name() pref.Name { return t.s.Name }
func (t serviceDesc) FullName() pref.FullName { return t.s.fullName }
func (t serviceDesc) IsPlaceholder() bool { return false }
func (t serviceDesc) DescriptorProto() (pref.Message, bool) { return t.s.desc.lazyInit(t) }
func (t serviceDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.s.opts.lazyInit(t) }
func (t serviceDesc) Methods() pref.MethodDescriptors { return t.s.ms.lazyInit(t, t.s.Methods) }
func (t serviceDesc) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t serviceDesc) ProtoType(pref.ServiceDescriptor) {}
func (t serviceDesc) ProtoInternal(pragma.DoNotImplement) {}
type methodMeta struct {
inheritedMeta
mit messageReference
mot messageReference
}
type methodDesc struct{ m *Method }
func (t methodDesc) Parent() (pref.Descriptor, bool) { return t.m.parent, true }
func (t methodDesc) Syntax() pref.Syntax { return t.m.syntax }
func (t methodDesc) Name() pref.Name { return t.m.Name }
func (t methodDesc) FullName() pref.FullName { return t.m.fullName }
func (t methodDesc) IsPlaceholder() bool { return false }
func (t methodDesc) DescriptorProto() (pref.Message, bool) { return t.m.desc.lazyInit(t) }
func (t methodDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.m.opts.lazyInit(t) }
func (t methodDesc) InputType() pref.MessageDescriptor { return t.m.mit.lazyInit(t, &t.m.InputType) }
func (t methodDesc) OutputType() pref.MessageDescriptor { return t.m.mot.lazyInit(t, &t.m.OutputType) }
func (t methodDesc) IsStreamingClient() bool { return t.m.IsStreamingClient }
func (t methodDesc) IsStreamingServer() bool { return t.m.IsStreamingServer }
func (t methodDesc) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t methodDesc) ProtoType(pref.MethodDescriptor) {}
func (t methodDesc) ProtoInternal(pragma.DoNotImplement) {}
type defaultValue struct {
once sync.Once
val pref.Value
buf []byte
}
var (
zeroBool = pref.ValueOf(false)
zeroInt32 = pref.ValueOf(int32(0))
zeroInt64 = pref.ValueOf(int64(0))
zeroUint32 = pref.ValueOf(uint32(0))
zeroUint64 = pref.ValueOf(uint64(0))
zeroFloat32 = pref.ValueOf(float32(0))
zeroFloat64 = pref.ValueOf(float64(0))
zeroString = pref.ValueOf(string(""))
zeroBytes = pref.ValueOf([]byte(nil))
zeroEnum = pref.ValueOf(pref.EnumNumber(0))
)
func (p *defaultValue) lazyInit(t pref.FieldDescriptor, v pref.Value) pref.Value {
p.once.Do(func() {
p.val = v
if !v.IsNull() {
if t.Kind() == pref.BytesKind {
if b, ok := v.Interface().([]byte); ok && len(b) > 0 {
p.buf = append([]byte(nil), b...)
}
}
return
}
switch t.Kind() {
case pref.BoolKind:
p.val = zeroBool
case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
p.val = zeroInt32
case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
p.val = zeroInt64
case pref.Uint32Kind, pref.Fixed32Kind:
p.val = zeroUint32
case pref.Uint64Kind, pref.Fixed64Kind:
p.val = zeroUint64
case pref.FloatKind:
p.val = zeroFloat32
case pref.DoubleKind:
p.val = zeroFloat64
case pref.StringKind:
p.val = zeroString
case pref.BytesKind:
p.val = zeroBytes
case pref.EnumKind:
p.val = zeroEnum
if t.Syntax() == pref.Proto2 {
if et := t.EnumType(); et != nil {
if vs := et.Values(); vs.Len() > 0 {
p.val = pref.ValueOf(vs.Get(0).Number())
}
}
}
}
})
if len(p.buf) > 0 && !bytes.Equal(p.buf, p.val.Bytes()) {
// TODO: Avoid panic if we're running with the race detector and instead
// spawn a goroutine that periodically resets this value back to the
// original to induce a race that can be detected by the detector.
panic(fmt.Sprintf("proto: detected mutation on the default bytes for %v", t.FullName()))
}
return p.val
}
// messageReference resolves PlaceholderMessages that reference declarations
// within the FileDescriptor tree that parent is a member of.
type messageReference struct{ once sync.Once }
func (p *messageReference) lazyInit(parent pref.Descriptor, pt *pref.MessageDescriptor) pref.MessageDescriptor {
p.once.Do(func() {
if t := *pt; t != nil && t.IsPlaceholder() {
if d, ok := resolveReference(parent, t.FullName()).(pref.MessageDescriptor); ok {
*pt = d
}
}
})
return *pt
}
// enumReference resolves PlaceholderEnums that reference declarations
// within the FileDescriptor tree that parent is a member of.
type enumReference struct{ once sync.Once }
func (p *enumReference) lazyInit(parent pref.Descriptor, pt *pref.EnumDescriptor) pref.EnumDescriptor {
p.once.Do(func() {
if t := *pt; t != nil && t.IsPlaceholder() {
if d, ok := resolveReference(parent, t.FullName()).(pref.EnumDescriptor); ok {
*pt = d
}
}
})
return *pt
}
// resolveReference searches parent for the MessageDescriptor or EnumDescriptor
// declaration identified by refName. This returns nil if not found.
func resolveReference(parent pref.Descriptor, refName pref.FullName) pref.Descriptor {
// Ascend upwards until a prefix match is found.
cur := parent
for cur != nil {
curName := cur.FullName()
if strings.HasPrefix(string(refName), string(curName)) {
if len(refName) == len(curName) {
refName = refName[len(curName):]
break // e.g., refName: foo.firetruck, curName: foo.firetruck
} else if refName[len(curName)] == '.' {
refName = refName[len(curName)+len("."):]
break // e.g., refName: foo.firetruck.driver, curName: foo.firetruck
}
// No match. (e.g., refName: foo.firetruck, curName: foo.fire)
}
cur, _ = cur.Parent() // nil after ascending above FileDescriptor
}
// Descend downwards to resolve all relative names.
for cur != nil && len(refName) > 0 {
var head pref.Name
head, refName = pref.Name(refName), ""
if i := strings.IndexByte(string(head), '.'); i >= 0 {
head, refName = head[:i], pref.FullName(head[i+len("."):])
}
// Search the current descriptor for the nested declaration.
var next pref.Descriptor
if t, ok := cur.(interface {
Messages() pref.MessageDescriptors
}); ok && next == nil {
if d := t.Messages().ByName(head); d != nil {
next = d
}
}
if t, ok := cur.(interface {
Enums() pref.EnumDescriptors
}); ok && next == nil {
if d := t.Enums().ByName(head); d != nil {
next = d
}
}
cur = next // nil if not found
}
return cur
}
var nameBuilderPool = sync.Pool{
New: func() interface{} { return new(nameBuilder) },
}
type nameBuilder struct {
sb strings.Builder
// TODO: See https://golang.org/issue/26269
rem int // conservative approximation for Cap-Len
}
// Append is equivalent to protoreflect.FullName.Append, but is optimized for
// large batches of operations where each name has a shared lifetime.
func (b *nameBuilder) Append(prefix pref.FullName, name pref.Name) pref.FullName {
const batchSize = 1 << 12
n := len(prefix) + len(".") + len(name)
if b.rem < n {
b.sb.Reset()
b.sb.Grow(batchSize)
b.rem = batchSize - n
}
if !strings.HasSuffix(b.sb.String(), string(prefix)) {
b.sb.WriteString(string(prefix))
}
b.sb.WriteByte('.')
b.sb.WriteString(string(name))
s := b.sb.String()
return pref.FullName(strings.TrimPrefix(s[len(s)-n:], "."))
}

View File

@ -0,0 +1,85 @@
// 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 prototype
import (
"google.golang.org/proto/reflect/protoreflect"
)
// TODO: Should the constructors take in a value rather than a pointer?
// TODO: Support initializing StandaloneMessage from a google.protobuf.Type?
// StandaloneMessage is a constructor for a protoreflect.MessageDescriptor
// that does not have a parent and has no child declarations.
type StandaloneMessage struct {
Syntax protoreflect.Syntax
FullName protoreflect.FullName
IsMapEntry bool
Fields []Field
Oneofs []Oneof
ExtensionRanges [][2]protoreflect.FieldNumber
fields fieldsMeta
oneofs oneofsMeta
nums numbersMeta
}
// NewMessage creates a new protoreflect.MessageDescriptor.
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields.
func NewMessage(t *StandaloneMessage) (protoreflect.MessageDescriptor, error) {
mt := standaloneMessage{t}
if err := validateMessage(mt); err != nil {
return nil, err
}
return mt, nil
}
// StandaloneEnum is a constructor for a protoreflect.EnumDescriptor
// that does not have a parent.
type StandaloneEnum struct {
Syntax protoreflect.Syntax
FullName protoreflect.FullName
Values []EnumValue
vals enumValuesMeta
}
// NewEnum creates a new protoreflect.EnumDescriptor.
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields.
func NewEnum(t *StandaloneEnum) (protoreflect.EnumDescriptor, error) {
et := standaloneEnum{t}
if err := validateEnum(et); err != nil {
return nil, err
}
return et, nil
}
// StandaloneExtension is a constructor for a protoreflect.ExtensionDescriptor
// that does not have a parent.
type StandaloneExtension struct {
Syntax protoreflect.Syntax
FullName protoreflect.FullName
Number protoreflect.FieldNumber
Cardinality protoreflect.Cardinality
Kind protoreflect.Kind
IsPacked bool
Default protoreflect.Value
MessageType protoreflect.MessageDescriptor
EnumType protoreflect.EnumDescriptor
ExtendedType protoreflect.MessageDescriptor
}
// NewExtension creates a new protoreflect.ExtensionDescriptor.
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields.
func NewExtension(t *StandaloneExtension) (protoreflect.ExtensionDescriptor, error) {
xt := standaloneExtension{t}
if err := validateExtension(xt); err != nil {
return nil, err
}
return xt, nil
}

View File

@ -0,0 +1,72 @@
// 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 prototype
import (
"fmt"
"google.golang.org/proto/internal/pragma"
pref "google.golang.org/proto/reflect/protoreflect"
)
type standaloneMessage struct{ m *StandaloneMessage }
func (t standaloneMessage) Parent() (pref.Descriptor, bool) { return nil, false }
func (t standaloneMessage) Syntax() pref.Syntax { return t.m.Syntax }
func (t standaloneMessage) Name() pref.Name { return t.m.FullName.Name() }
func (t standaloneMessage) FullName() pref.FullName { return t.m.FullName }
func (t standaloneMessage) IsPlaceholder() bool { return false }
func (t standaloneMessage) DescriptorProto() (pref.Message, bool) { return nil, false }
func (t standaloneMessage) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
func (t standaloneMessage) IsMapEntry() bool { return t.m.IsMapEntry }
func (t standaloneMessage) Fields() pref.FieldDescriptors { return t.m.fields.lazyInit(t, t.m.Fields) }
func (t standaloneMessage) Oneofs() pref.OneofDescriptors { return t.m.oneofs.lazyInit(t, t.m.Oneofs) }
func (t standaloneMessage) RequiredNumbers() pref.FieldNumbers { return t.m.nums.lazyInit(t.m.Fields) }
func (t standaloneMessage) ExtensionRanges() pref.FieldRanges { return (*ranges)(&t.m.ExtensionRanges) }
func (t standaloneMessage) Messages() pref.MessageDescriptors { return &emptyMessages }
func (t standaloneMessage) Enums() pref.EnumDescriptors { return &emptyEnums }
func (t standaloneMessage) Extensions() pref.ExtensionDescriptors { return &emptyExtensions }
func (t standaloneMessage) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t standaloneMessage) ProtoType(pref.MessageDescriptor) {}
func (t standaloneMessage) ProtoInternal(pragma.DoNotImplement) {}
type standaloneEnum struct{ e *StandaloneEnum }
func (t standaloneEnum) Parent() (pref.Descriptor, bool) { return nil, false }
func (t standaloneEnum) Syntax() pref.Syntax { return t.e.Syntax }
func (t standaloneEnum) Name() pref.Name { return t.e.FullName.Name() }
func (t standaloneEnum) FullName() pref.FullName { return t.e.FullName }
func (t standaloneEnum) IsPlaceholder() bool { return false }
func (t standaloneEnum) DescriptorProto() (pref.Message, bool) { return nil, false }
func (t standaloneEnum) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
func (t standaloneEnum) Values() pref.EnumValueDescriptors { return t.e.vals.lazyInit(t, t.e.Values) }
func (t standaloneEnum) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t standaloneEnum) ProtoType(pref.EnumDescriptor) {}
func (t standaloneEnum) ProtoInternal(pragma.DoNotImplement) {}
type standaloneExtension struct{ x *StandaloneExtension }
func (t standaloneExtension) Parent() (pref.Descriptor, bool) { return nil, false }
func (t standaloneExtension) Syntax() pref.Syntax { return t.x.Syntax }
func (t standaloneExtension) Name() pref.Name { return t.x.FullName.Name() }
func (t standaloneExtension) FullName() pref.FullName { return t.x.FullName }
func (t standaloneExtension) IsPlaceholder() bool { return false }
func (t standaloneExtension) DescriptorProto() (pref.Message, bool) { return nil, false }
func (t standaloneExtension) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
func (t standaloneExtension) Number() pref.FieldNumber { return t.x.Number }
func (t standaloneExtension) Cardinality() pref.Cardinality { return t.x.Cardinality }
func (t standaloneExtension) Kind() pref.Kind { return t.x.Kind }
func (t standaloneExtension) JSONName() string { return "" }
func (t standaloneExtension) IsPacked() bool { return t.x.IsPacked }
func (t standaloneExtension) IsMap() bool { return false }
func (t standaloneExtension) IsWeak() bool { return false }
func (t standaloneExtension) Default() pref.Value { return t.x.Default }
func (t standaloneExtension) OneofType() pref.OneofDescriptor { return nil }
func (t standaloneExtension) MessageType() pref.MessageDescriptor { return t.x.MessageType }
func (t standaloneExtension) EnumType() pref.EnumDescriptor { return t.x.EnumType }
func (t standaloneExtension) ExtendedType() pref.MessageDescriptor { return t.x.ExtendedType }
func (t standaloneExtension) Format(s fmt.State, r rune) { formatDesc(s, r, t) }
func (t standaloneExtension) ProtoType(pref.FieldDescriptor) {}
func (t standaloneExtension) ProtoInternal(pragma.DoNotImplement) {}

View File

@ -0,0 +1,26 @@
// 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 prototype
import (
"fmt"
"google.golang.org/proto/internal/pragma"
pref "google.golang.org/proto/reflect/protoreflect"
)
// TODO: This is useful for print descriptor types in a human readable way.
// This is not strictly necessary.
// list is an interface that matches any of the list interfaces defined in the
// protoreflect package.
type list interface {
Len() int
pragma.DoNotImplement
}
func formatList(s fmt.State, r rune, vs list) {}
func formatDesc(s fmt.State, r rune, t pref.Descriptor) {}

View File

@ -0,0 +1,585 @@
// 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 prototype
import (
"reflect"
"strconv"
"strings"
"sync"
"testing"
pref "google.golang.org/proto/reflect/protoreflect"
)
// TestDescriptors tests that the implementations do not declare additional
// methods that do not exist on the interface types.
func TestDescriptors(t *testing.T) {
tests := []interface{}{
[]pref.FileDescriptor{placeholderFile{}, fileDesc{}},
[]pref.MessageDescriptor{placeholderMessage{}, standaloneMessage{}, messageDesc{}},
[]pref.FieldDescriptor{standaloneExtension{}, fieldDesc{}, extensionDesc{}},
[]pref.OneofDescriptor{oneofDesc{}},
[]pref.EnumDescriptor{placeholderEnum{}, standaloneEnum{}, enumDesc{}},
[]pref.EnumValueDescriptor{enumValueDesc{}},
[]pref.ServiceDescriptor{serviceDesc{}},
[]pref.MethodDescriptor{methodDesc{}},
[]pref.FileImports{(*fileImports)(nil)},
[]pref.MessageDescriptors{(*messages)(nil)},
[]pref.FieldNumbers{(*numbers)(nil)},
[]pref.FieldRanges{(*ranges)(nil)},
[]pref.FieldDescriptors{(*fields)(nil), (*oneofFields)(nil)},
[]pref.OneofDescriptors{(*oneofs)(nil)},
[]pref.ExtensionDescriptors{(*extensions)(nil)},
[]pref.EnumDescriptors{(*enums)(nil)},
[]pref.EnumValueDescriptors{(*enumValues)(nil)},
[]pref.ServiceDescriptors{(*services)(nil)},
[]pref.MethodDescriptors{(*methods)(nil)},
}
for _, tt := range tests {
v := reflect.ValueOf(tt) // []T where T is an interface
ifaceType := v.Type().Elem()
for i := 0; i < v.Len(); i++ {
implType := v.Index(i).Elem().Type()
var hasName bool
for j := 0; j < implType.NumMethod(); j++ {
if name := implType.Method(j).Name; name == "Format" {
hasName = true
} else if _, ok := ifaceType.MethodByName(name); !ok {
t.Errorf("spurious method: %v.%v", implType, name)
}
}
if !hasName {
t.Errorf("missing method: %v.Format", implType)
}
}
}
}
func TestFile(t *testing.T) {
f := &File{
Syntax: pref.Proto2,
Path: "path/to/file.proto",
Package: "test",
Messages: []Message{{
Name: "A", // "test.A"
IsMapEntry: true,
Fields: []Field{{
Name: "key", // "test.A.key"
Number: 1,
Cardinality: pref.Optional,
Kind: pref.StringKind,
}, {
Name: "value", // "test.A.value"
Number: 2,
Cardinality: pref.Optional,
Kind: pref.MessageKind,
MessageType: PlaceholderMessage("test.B"),
}},
}, {
Name: "B", // "test.B"
Fields: []Field{{
Name: "field_one",
Number: 1,
Cardinality: pref.Optional,
Kind: pref.StringKind,
Default: pref.ValueOf("hello"),
OneofName: "O1",
}, {
Name: "field_two",
JSONName: "Field2",
Number: 2,
Cardinality: pref.Optional,
Kind: pref.EnumKind,
Default: pref.ValueOf(pref.EnumNumber(1)),
EnumType: PlaceholderEnum("test.E1"),
OneofName: "O2",
}, {
Name: "field_three",
Number: 3,
Cardinality: pref.Optional,
Kind: pref.MessageKind,
MessageType: PlaceholderMessage("test.C"),
OneofName: "O2",
}, {
Name: "field_four",
JSONName: "Field4",
Number: 4,
Cardinality: pref.Repeated,
Kind: pref.MessageKind,
MessageType: PlaceholderMessage("test.A"),
}, {
Name: "field_five",
Number: 5,
Cardinality: pref.Repeated,
Kind: pref.Int32Kind,
IsPacked: true,
}, {
Name: "field_six",
Number: 6,
Cardinality: pref.Required,
Kind: pref.StringKind,
}},
Oneofs: []Oneof{{Name: "O1"}, {Name: "O2"}},
ExtensionRanges: [][2]pref.FieldNumber{{1000, 2000}},
}, {
Name: "C", // "test.C"
Messages: []Message{{
Name: "A", // "test.C.A"
Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Required, Kind: pref.BytesKind}},
}},
Enums: []Enum{{
Name: "E1", // "test.C.E1"
Values: []EnumValue{{Name: "FOO", Number: 0}, {Name: "BAR", Number: 1}},
}},
Extensions: []Extension{{
Name: "X", // "test.C.X"
Number: 1000,
Cardinality: pref.Repeated,
Kind: pref.MessageKind,
IsPacked: false,
MessageType: PlaceholderMessage("test.C"),
ExtendedType: PlaceholderMessage("test.B"),
}},
}},
Enums: []Enum{{
Name: "E1", // "test.E1"
Values: []EnumValue{{Name: "FOO", Number: 0}, {Name: "BAR", Number: 1}},
}},
Extensions: []Extension{{
Name: "X", // "test.X"
Number: 1000,
Cardinality: pref.Repeated,
Kind: pref.MessageKind,
IsPacked: false,
MessageType: PlaceholderMessage("test.C"),
ExtendedType: PlaceholderMessage("test.B"),
}},
Services: []Service{{
Name: "S", // "test.S"
Methods: []Method{{
Name: "M", // "test.S.M"
InputType: PlaceholderMessage("test.A"),
OutputType: PlaceholderMessage("test.C.A"),
IsStreamingClient: true,
IsStreamingServer: true,
}},
}},
}
fd, err := NewFile(f)
if err != nil {
t.Fatalf("NewFile() error: %v", err)
}
// Represent the descriptor as a map where each key is an accessor method
// and the value is either the wanted tail value or another accessor map.
type M = map[string]interface{}
want := M{
"Parent": nil,
"Syntax": pref.Proto2,
"Name": pref.Name("test"),
"FullName": pref.FullName("test"),
"Path": "path/to/file.proto",
"Package": pref.FullName("test"),
"IsPlaceholder": false,
"Messages": M{
"Len": 3,
"Get:0": M{
"Parent": M{"FullName": pref.FullName("test")},
"Syntax": pref.Proto2,
"Name": pref.Name("A"),
"FullName": pref.FullName("test.A"),
"IsPlaceholder": false,
"IsMapEntry": true,
"Fields": M{
"Len": 2,
"ByNumber:1": M{
"Parent": M{"FullName": pref.FullName("test.A")},
"Name": pref.Name("key"),
"FullName": pref.FullName("test.A.key"),
"Number": pref.FieldNumber(1),
"Cardinality": pref.Optional,
"Kind": pref.StringKind,
"JSONName": "key",
"IsPacked": false,
"IsMap": false,
"IsWeak": false,
"Default": "",
"OneofType": nil,
"ExtendedType": nil,
"MessageType": nil,
"EnumType": nil,
},
"ByNumber:2": M{
"Parent": M{"FullName": pref.FullName("test.A")},
"Name": pref.Name("value"),
"FullName": pref.FullName("test.A.value"),
"Number": pref.FieldNumber(2),
"Cardinality": pref.Optional,
"Kind": pref.MessageKind,
"JSONName": "value",
"IsPacked": false,
"IsMap": false,
"IsWeak": false,
"Default": nil,
"OneofType": nil,
"ExtendedType": nil,
"MessageType": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
"EnumType": nil,
},
"ByNumber:3": nil,
},
"Oneofs": M{"Len": 0},
"RequiredNumbers": M{"Len": 0},
"ExtensionRanges": M{"Len": 0},
"Messages": M{"Len": 0},
"Enums": M{"Len": 0},
"Extensions": M{"Len": 0},
},
"ByName:B": M{
"Name": pref.Name("B"),
"Fields": M{
"Len": 6,
"ByJSONName:field_one": nil,
"ByJSONName:fieldOne": M{
"Name": pref.Name("field_one"),
"JSONName": "fieldOne",
"Default": "hello",
"OneofType": M{"Name": pref.Name("O1"), "IsPlaceholder": false},
},
"ByJSONName:fieldTwo": nil,
"ByJSONName:Field2": M{
"Name": pref.Name("field_two"),
"JSONName": "Field2",
"Default": pref.EnumNumber(1),
"OneofType": M{"Name": pref.Name("O2"), "IsPlaceholder": false},
},
"ByName:fieldThree": nil,
"ByName:field_three": M{
"IsMap": false,
"MessageType": M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
"OneofType": M{"Name": pref.Name("O2"), "IsPlaceholder": false},
},
"ByNumber:12": nil,
"ByNumber:4": M{
"Cardinality": pref.Repeated,
"IsMap": true,
"Default": nil,
"MessageType": M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
},
"ByNumber:5": M{
"Cardinality": pref.Repeated,
"Kind": pref.Int32Kind,
"IsPacked": true,
"Default": int32(0),
},
"ByNumber:6": M{
"Cardinality": pref.Required,
"Default": "",
"OneofType": nil,
},
},
"Oneofs": M{
"Len": 2,
"ByName:O0": nil,
"ByName:O1": M{
"FullName": pref.FullName("test.B.O1"),
"Fields": M{
"Len": 1,
"Get:0": M{"FullName": pref.FullName("test.B.field_one")},
},
},
"Get:1": M{
"FullName": pref.FullName("test.B.O2"),
"Fields": M{
"Len": 2,
"ByName:field_two": M{"Name": pref.Name("field_two")},
"Get:1": M{"Name": pref.Name("field_three")},
},
},
},
"RequiredNumbers": M{
"Len": 1,
"Get:0": pref.FieldNumber(6),
"Has:1": false,
"Has:6": true,
},
"ExtensionRanges": M{
"Len": 1,
"Get:0": [2]pref.FieldNumber{1000, 2000},
"Has:999": false,
"Has:1000": true,
"Has:1500": true,
"Has:1999": true,
"Has:2000": false,
},
},
"Get:2": M{
"Name": pref.Name("C"),
"Messages": M{
"Len": 1,
"Get:0": M{"FullName": pref.FullName("test.C.A")},
},
"Enums": M{
"Len": 1,
"Get:0": M{"FullName": pref.FullName("test.C.E1")},
},
"Extensions": M{
"Len": 1,
"Get:0": M{"FullName": pref.FullName("test.C.X")},
},
},
},
"Enums": M{
"Len": 1,
"Get:0": M{
"Name": pref.Name("E1"),
"Values": M{
"Len": 2,
"ByName:Foo": nil,
"ByName:FOO": M{"FullName": pref.FullName("test.FOO")},
"ByNumber:2": nil,
"ByNumber:1": M{"FullName": pref.FullName("test.BAR")},
},
},
},
"Extensions": M{
"Len": 1,
"ByName:X": M{
"Name": pref.Name("X"),
"Number": pref.FieldNumber(1000),
"Cardinality": pref.Repeated,
"Kind": pref.MessageKind,
"IsPacked": false,
"MessageType": M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
"ExtendedType": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
},
},
"Services": M{
"Len": 1,
"ByName:s": nil,
"ByName:S": M{
"Parent": M{"FullName": pref.FullName("test")},
"Name": pref.Name("S"),
"FullName": pref.FullName("test.S"),
"Methods": M{
"Len": 1,
"Get:0": M{
"Parent": M{"FullName": pref.FullName("test.S")},
"Name": pref.Name("M"),
"FullName": pref.FullName("test.S.M"),
"InputType": M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
"OutputType": M{"FullName": pref.FullName("test.C.A"), "IsPlaceholder": false},
"IsStreamingClient": true,
"IsStreamingServer": true,
},
},
},
},
}
// Concurrently explore the file tree to induce races.
const numGoRoutines = 2
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < numGoRoutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
checkAccessors(t, "", reflect.ValueOf(fd), want)
}()
}
}
func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]interface{}) {
if rv.Interface() == nil {
t.Errorf("%v is nil, want non-nil", p)
return
}
for s, v := range want {
// Call the accessor method.
p := p + "." + s
var rets []reflect.Value
if i := strings.IndexByte(s, ':'); i >= 0 {
// Accessor method takes in a single argument, which is encoded
// after the accessor name, separated by a ':' delimiter.
fnc := rv.MethodByName(s[:i])
arg := reflect.New(fnc.Type().In(0)).Elem()
s = s[i+len(":"):]
switch arg.Kind() {
case reflect.String:
arg.SetString(s)
case reflect.Int32, reflect.Int:
n, _ := strconv.ParseInt(s, 0, 64)
arg.SetInt(n)
}
rets = fnc.Call([]reflect.Value{arg})
} else {
rets = rv.MethodByName(s).Call(nil)
}
// Check that (val, ok) pattern is internally consistent.
if len(rets) == 2 {
if rets[0].IsNil() && rets[1].Bool() {
t.Errorf("%v = (nil, true), want (nil, false)", p)
}
if !rets[0].IsNil() && !rets[1].Bool() {
t.Errorf("%v = (non-nil, false), want (non-nil, true)", p)
}
}
// Check that the accessor output matches.
if want, ok := v.(map[string]interface{}); ok {
checkAccessors(t, p, rets[0], want)
} else {
got := rets[0].Interface()
if pv, ok := got.(pref.Value); ok {
got = pv.Interface()
}
if want := v; !reflect.DeepEqual(got, want) {
t.Errorf("%v = %v, want %v", p, got, want)
}
}
}
}
func TestResolve(t *testing.T) {
f := &File{
Syntax: pref.Proto2,
Package: "test",
Messages: []Message{{
Name: "FooMessage",
Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
Messages: []Message{{
Name: "FooMessage",
Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
}, {
Name: "BarMessage",
Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
}},
Enums: []Enum{{
Name: "FooEnum",
Values: []EnumValue{{Name: "E", Number: 0}},
}, {
Name: "BarEnum",
Values: []EnumValue{{Name: "E", Number: 0}},
}},
}, {
Name: "BarMessage",
Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
}},
Enums: []Enum{{
Name: "FooEnum",
Values: []EnumValue{{Name: "E", Number: 0}},
}, {
Name: "BarEnum",
Values: []EnumValue{{Name: "E", Number: 0}},
}},
}
fd, err := NewFile(f)
if err != nil {
t.Fatalf("NewFile() error: %v", err)
}
tests := []struct {
parent pref.Descriptor
name pref.FullName
want pref.Descriptor
}{{
parent: fd.Enums().Get(0),
name: "test.Foo",
want: nil,
}, {
parent: fd.Enums().Get(0),
name: "test.FooEnum",
want: fd.Enums().Get(0),
}, {
parent: fd.Enums().Get(0),
name: "test.BarEnum",
want: fd.Enums().Get(1),
}, {
parent: fd.Enums().Get(0),
name: "test.BarMessage",
want: fd.Messages().Get(1),
}, {
parent: fd.Enums().Get(0),
name: "test.FooMessage.BarMessage",
want: fd.Messages().Get(0).Messages().Get(1),
}, {
parent: fd.Enums().Get(0),
name: "test.FooMessage.Bar",
want: nil,
}, {
parent: fd.Messages().Get(1),
name: "test.FooMessage.BarEnum",
want: fd.Messages().Get(0).Enums().Get(1),
}, {
parent: fd.Messages().Get(1),
name: "test.FooEnum",
want: fd.Enums().Get(0),
}, {
parent: fd.Messages().Get(0),
name: "test.FooEnum",
want: fd.Enums().Get(0),
}, {
parent: fd.Messages().Get(0),
name: "test.FooEnum.NonExistent",
want: nil,
}, {
parent: fd.Messages().Get(0),
name: "test.FooMessage.FooEnum",
want: fd.Messages().Get(0).Enums().Get(0),
}, {
parent: fd.Messages().Get(0),
name: "test.FooMessage",
want: fd.Messages().Get(0),
}, {
parent: fd.Messages().Get(0),
name: "test.FooMessage.Fizz",
want: nil,
}, {
parent: fd.Messages().Get(0).Messages().Get(0),
name: "test.FooMessage.FooMessage",
want: fd.Messages().Get(0).Messages().Get(0),
}, {
parent: fd.Messages().Get(0).Messages().Get(0),
name: "test.FooMessage.BarMessage",
want: fd.Messages().Get(0).Messages().Get(1),
}, {
parent: fd.Messages().Get(0).Messages().Get(0),
name: "test.BarMessage.FooMessage",
want: nil,
}, {
parent: fd.Messages().Get(0).Messages().Get(0),
name: "test.BarMessage",
want: fd.Messages().Get(1),
}, {
parent: fd.Messages().Get(0).Messages().Get(0),
name: "test.BarMessageExtra",
want: nil,
}, {
parent: fd.Messages().Get(0).Messages().Get(0),
name: "taste.BarMessage",
want: nil,
}}
for _, tt := range tests {
got := resolveReference(tt.parent, tt.name)
if got != tt.want {
fullName := func(d pref.Descriptor) string {
if d == nil {
return "<nil>"
}
return string(d.FullName())
}
t.Errorf("resolveReference(%v, %v) = %v, want %v", fullName(tt.parent), tt.name, fullName(got), fullName(tt.want))
}
}
}

View File

@ -0,0 +1,28 @@
// 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 prototype
import (
pref "google.golang.org/proto/reflect/protoreflect"
)
// TODO: This is important to prevent users from creating invalid types,
// but is not functionality needed now.
func validateFile(t pref.FileDescriptor) error {
return nil
}
func validateMessage(t pref.MessageDescriptor) error {
return nil
}
func validateExtension(t pref.ExtensionDescriptor) error {
return nil
}
func validateEnum(t pref.EnumDescriptor) error {
return nil
}

View File

@ -29,8 +29,8 @@ fi
export PATH=$TEST_DIR/$PROTOBUF_DIR/src:$TEST_DIR/$PROTOBUF_DIR/conformance:$PATH
# Download each Go toolchain version.
GO_LATEST=1.11beta3
GO_VERSIONS=(1.9.7 1.10.3 $GO_LATEST)
GO_LATEST=1.11rc1
GO_VERSIONS=(1.10.3 $GO_LATEST)
for GO_VERSION in ${GO_VERSIONS[@]}; do
GO_DIR=go$GO_VERSION
if [ ! -d $GO_DIR ]; then
@ -91,7 +91,13 @@ for I in ${!PIDS[@]}; do
fi
done
# TODO: Check for stale generated Go source files.
# Check for stale generated source files.
GEN_DIFF=$(cd $REPO_ROOT && ${GO_LATEST_BIN} run ./internal/cmd/generate-types 2>&1)
if [ ! -z "$GEN_DIFF" ]; then
print "go run ./internal/cmd/generate-types"
echo "$GEN_DIFF"
RET=1
fi
# Check for unformatted Go source files.
FMT_DIFF=$(cd $REPO_ROOT && ${GO_LATEST_BIN}fmt -d . 2>&1)