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>
diff --git a/internal/cmd/generate-types/main.go b/internal/cmd/generate-types/main.go
new file mode 100644
index 0000000..9d74a44
--- /dev/null
+++ b/internal/cmd/generate-types/main.go
@@ -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()
+ }
+}
diff --git a/reflect/prototype/descriptor.go b/reflect/prototype/descriptor.go
new file mode 100644
index 0000000..8490ad3
--- /dev/null
+++ b/reflect/prototype/descriptor.go
@@ -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
+}
diff --git a/reflect/prototype/placeholder.go b/reflect/prototype/placeholder.go
new file mode 100644
index 0000000..dde30c9
--- /dev/null
+++ b/reflect/prototype/placeholder.go
@@ -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)}
+}
diff --git a/reflect/prototype/placeholder_type.go b/reflect/prototype/placeholder_type.go
new file mode 100644
index 0000000..1aed8de
--- /dev/null
+++ b/reflect/prototype/placeholder_type.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 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) {}
diff --git a/reflect/prototype/protofile.go b/reflect/prototype/protofile.go
new file mode 100644
index 0000000..e2e4307
--- /dev/null
+++ b/reflect/prototype/protofile.go
@@ -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
+}
diff --git a/reflect/prototype/protofile_list.go b/reflect/prototype/protofile_list.go
new file mode 100644
index 0000000..52570c0
--- /dev/null
+++ b/reflect/prototype/protofile_list.go
@@ -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) {}
diff --git a/reflect/prototype/protofile_list_gen.go b/reflect/prototype/protofile_list_gen.go
new file mode 100644
index 0000000..7a3f564
--- /dev/null
+++ b/reflect/prototype/protofile_list_gen.go
@@ -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) {}
diff --git a/reflect/prototype/protofile_type.go b/reflect/prototype/protofile_type.go
new file mode 100644
index 0000000..2929232
--- /dev/null
+++ b/reflect/prototype/protofile_type.go
@@ -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:], "."))
+}
diff --git a/reflect/prototype/standalone.go b/reflect/prototype/standalone.go
new file mode 100644
index 0000000..84fdac1
--- /dev/null
+++ b/reflect/prototype/standalone.go
@@ -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
+}
diff --git a/reflect/prototype/standalone_type.go b/reflect/prototype/standalone_type.go
new file mode 100644
index 0000000..0009ebe
--- /dev/null
+++ b/reflect/prototype/standalone_type.go
@@ -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) {}
diff --git a/reflect/prototype/stringer.go b/reflect/prototype/stringer.go
new file mode 100644
index 0000000..7541b8d
--- /dev/null
+++ b/reflect/prototype/stringer.go
@@ -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) {}
diff --git a/reflect/prototype/type_test.go b/reflect/prototype/type_test.go
new file mode 100644
index 0000000..5ecfc1a
--- /dev/null
+++ b/reflect/prototype/type_test.go
@@ -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))
+ }
+ }
+}
diff --git a/reflect/prototype/validate.go b/reflect/prototype/validate.go
new file mode 100644
index 0000000..adbb35c
--- /dev/null
+++ b/reflect/prototype/validate.go
@@ -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
+}
diff --git a/test.bash b/test.bash
index c010f94..2c65d76 100755
--- a/test.bash
+++ b/test.bash
@@ -29,8 +29,8 @@
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 @@
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)