1414 lines
39 KiB
Go
1414 lines
39 KiB
Go
package protoparse
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
|
|
|
"github.com/jhump/protoreflect/desc"
|
|
"github.com/jhump/protoreflect/desc/internal"
|
|
"github.com/jhump/protoreflect/desc/protoparse/ast"
|
|
"github.com/jhump/protoreflect/dynamic"
|
|
)
|
|
|
|
// NB: To process options, we need descriptors, but we may not have rich
|
|
// descriptors when trying to interpret options for unlinked parsed files.
|
|
// So we define minimal interfaces that can be backed by both rich descriptors
|
|
// as well as their poorer cousins, plain ol' descriptor protos.
|
|
|
|
type descriptorish interface {
|
|
GetFile() fileDescriptorish
|
|
GetFullyQualifiedName() string
|
|
AsProto() proto.Message
|
|
}
|
|
|
|
type fileDescriptorish interface {
|
|
descriptorish
|
|
GetFileOptions() *dpb.FileOptions
|
|
GetPackage() string
|
|
FindSymbol(name string) desc.Descriptor
|
|
GetPublicDependencies() []fileDescriptorish
|
|
GetDependencies() []fileDescriptorish
|
|
GetMessageTypes() []msgDescriptorish
|
|
GetExtensions() []fldDescriptorish
|
|
GetEnumTypes() []enumDescriptorish
|
|
GetServices() []svcDescriptorish
|
|
}
|
|
|
|
type msgDescriptorish interface {
|
|
descriptorish
|
|
GetMessageOptions() *dpb.MessageOptions
|
|
GetFields() []fldDescriptorish
|
|
GetOneOfs() []oneofDescriptorish
|
|
GetExtensionRanges() []extRangeDescriptorish
|
|
GetNestedMessageTypes() []msgDescriptorish
|
|
GetNestedExtensions() []fldDescriptorish
|
|
GetNestedEnumTypes() []enumDescriptorish
|
|
}
|
|
|
|
type fldDescriptorish interface {
|
|
descriptorish
|
|
GetFieldOptions() *dpb.FieldOptions
|
|
GetMessageType() *desc.MessageDescriptor
|
|
GetEnumType() *desc.EnumDescriptor
|
|
AsFieldDescriptorProto() *dpb.FieldDescriptorProto
|
|
}
|
|
|
|
type oneofDescriptorish interface {
|
|
descriptorish
|
|
GetOneOfOptions() *dpb.OneofOptions
|
|
}
|
|
|
|
type enumDescriptorish interface {
|
|
descriptorish
|
|
GetEnumOptions() *dpb.EnumOptions
|
|
GetValues() []enumValDescriptorish
|
|
}
|
|
|
|
type enumValDescriptorish interface {
|
|
descriptorish
|
|
GetEnumValueOptions() *dpb.EnumValueOptions
|
|
}
|
|
|
|
type svcDescriptorish interface {
|
|
descriptorish
|
|
GetServiceOptions() *dpb.ServiceOptions
|
|
GetMethods() []methodDescriptorish
|
|
}
|
|
|
|
type methodDescriptorish interface {
|
|
descriptorish
|
|
GetMethodOptions() *dpb.MethodOptions
|
|
}
|
|
|
|
// The hierarchy of descriptorish implementations backed by
|
|
// rich descriptors:
|
|
|
|
type richFileDescriptorish struct {
|
|
*desc.FileDescriptor
|
|
}
|
|
|
|
func (d richFileDescriptorish) GetFile() fileDescriptorish {
|
|
return d
|
|
}
|
|
|
|
func (d richFileDescriptorish) GetPublicDependencies() []fileDescriptorish {
|
|
deps := d.FileDescriptor.GetPublicDependencies()
|
|
ret := make([]fileDescriptorish, len(deps))
|
|
for i, d := range deps {
|
|
ret[i] = richFileDescriptorish{FileDescriptor: d}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richFileDescriptorish) GetDependencies() []fileDescriptorish {
|
|
deps := d.FileDescriptor.GetDependencies()
|
|
ret := make([]fileDescriptorish, len(deps))
|
|
for i, d := range deps {
|
|
ret[i] = richFileDescriptorish{FileDescriptor: d}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richFileDescriptorish) GetMessageTypes() []msgDescriptorish {
|
|
msgs := d.FileDescriptor.GetMessageTypes()
|
|
ret := make([]msgDescriptorish, len(msgs))
|
|
for i, m := range msgs {
|
|
ret[i] = richMsgDescriptorish{MessageDescriptor: m}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richFileDescriptorish) GetExtensions() []fldDescriptorish {
|
|
flds := d.FileDescriptor.GetExtensions()
|
|
ret := make([]fldDescriptorish, len(flds))
|
|
for i, f := range flds {
|
|
ret[i] = richFldDescriptorish{FieldDescriptor: f}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richFileDescriptorish) GetEnumTypes() []enumDescriptorish {
|
|
ens := d.FileDescriptor.GetEnumTypes()
|
|
ret := make([]enumDescriptorish, len(ens))
|
|
for i, en := range ens {
|
|
ret[i] = richEnumDescriptorish{EnumDescriptor: en}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richFileDescriptorish) GetServices() []svcDescriptorish {
|
|
svcs := d.FileDescriptor.GetServices()
|
|
ret := make([]svcDescriptorish, len(svcs))
|
|
for i, s := range svcs {
|
|
ret[i] = richSvcDescriptorish{ServiceDescriptor: s}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type richMsgDescriptorish struct {
|
|
*desc.MessageDescriptor
|
|
}
|
|
|
|
func (d richMsgDescriptorish) GetFile() fileDescriptorish {
|
|
return richFileDescriptorish{FileDescriptor: d.MessageDescriptor.GetFile()}
|
|
}
|
|
|
|
func (d richMsgDescriptorish) GetFields() []fldDescriptorish {
|
|
flds := d.MessageDescriptor.GetFields()
|
|
ret := make([]fldDescriptorish, len(flds))
|
|
for i, f := range flds {
|
|
ret[i] = richFldDescriptorish{FieldDescriptor: f}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richMsgDescriptorish) GetOneOfs() []oneofDescriptorish {
|
|
oos := d.MessageDescriptor.GetOneOfs()
|
|
ret := make([]oneofDescriptorish, len(oos))
|
|
for i, oo := range oos {
|
|
ret[i] = richOneOfDescriptorish{OneOfDescriptor: oo}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richMsgDescriptorish) GetExtensionRanges() []extRangeDescriptorish {
|
|
md := d.MessageDescriptor
|
|
mdFqn := md.GetFullyQualifiedName()
|
|
extrs := md.AsDescriptorProto().GetExtensionRange()
|
|
ret := make([]extRangeDescriptorish, len(extrs))
|
|
for i, extr := range extrs {
|
|
ret[i] = extRangeDescriptorish{
|
|
er: extr,
|
|
qual: mdFqn,
|
|
file: richFileDescriptorish{FileDescriptor: md.GetFile()},
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richMsgDescriptorish) GetNestedMessageTypes() []msgDescriptorish {
|
|
msgs := d.MessageDescriptor.GetNestedMessageTypes()
|
|
ret := make([]msgDescriptorish, len(msgs))
|
|
for i, m := range msgs {
|
|
ret[i] = richMsgDescriptorish{MessageDescriptor: m}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richMsgDescriptorish) GetNestedExtensions() []fldDescriptorish {
|
|
flds := d.MessageDescriptor.GetNestedExtensions()
|
|
ret := make([]fldDescriptorish, len(flds))
|
|
for i, f := range flds {
|
|
ret[i] = richFldDescriptorish{FieldDescriptor: f}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d richMsgDescriptorish) GetNestedEnumTypes() []enumDescriptorish {
|
|
ens := d.MessageDescriptor.GetNestedEnumTypes()
|
|
ret := make([]enumDescriptorish, len(ens))
|
|
for i, en := range ens {
|
|
ret[i] = richEnumDescriptorish{EnumDescriptor: en}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type richFldDescriptorish struct {
|
|
*desc.FieldDescriptor
|
|
}
|
|
|
|
func (d richFldDescriptorish) GetFile() fileDescriptorish {
|
|
return richFileDescriptorish{FileDescriptor: d.FieldDescriptor.GetFile()}
|
|
}
|
|
|
|
func (d richFldDescriptorish) AsFieldDescriptorProto() *dpb.FieldDescriptorProto {
|
|
return d.FieldDescriptor.AsFieldDescriptorProto()
|
|
}
|
|
|
|
type richOneOfDescriptorish struct {
|
|
*desc.OneOfDescriptor
|
|
}
|
|
|
|
func (d richOneOfDescriptorish) GetFile() fileDescriptorish {
|
|
return richFileDescriptorish{FileDescriptor: d.OneOfDescriptor.GetFile()}
|
|
}
|
|
|
|
type richEnumDescriptorish struct {
|
|
*desc.EnumDescriptor
|
|
}
|
|
|
|
func (d richEnumDescriptorish) GetFile() fileDescriptorish {
|
|
return richFileDescriptorish{FileDescriptor: d.EnumDescriptor.GetFile()}
|
|
}
|
|
|
|
func (d richEnumDescriptorish) GetValues() []enumValDescriptorish {
|
|
vals := d.EnumDescriptor.GetValues()
|
|
ret := make([]enumValDescriptorish, len(vals))
|
|
for i, val := range vals {
|
|
ret[i] = richEnumValDescriptorish{EnumValueDescriptor: val}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type richEnumValDescriptorish struct {
|
|
*desc.EnumValueDescriptor
|
|
}
|
|
|
|
func (d richEnumValDescriptorish) GetFile() fileDescriptorish {
|
|
return richFileDescriptorish{FileDescriptor: d.EnumValueDescriptor.GetFile()}
|
|
}
|
|
|
|
type richSvcDescriptorish struct {
|
|
*desc.ServiceDescriptor
|
|
}
|
|
|
|
func (d richSvcDescriptorish) GetFile() fileDescriptorish {
|
|
return richFileDescriptorish{FileDescriptor: d.ServiceDescriptor.GetFile()}
|
|
}
|
|
|
|
func (d richSvcDescriptorish) GetMethods() []methodDescriptorish {
|
|
mtds := d.ServiceDescriptor.GetMethods()
|
|
ret := make([]methodDescriptorish, len(mtds))
|
|
for i, mtd := range mtds {
|
|
ret[i] = richMethodDescriptorish{MethodDescriptor: mtd}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type richMethodDescriptorish struct {
|
|
*desc.MethodDescriptor
|
|
}
|
|
|
|
func (d richMethodDescriptorish) GetFile() fileDescriptorish {
|
|
return richFileDescriptorish{FileDescriptor: d.MethodDescriptor.GetFile()}
|
|
}
|
|
|
|
// The hierarchy of descriptorish implementations backed by
|
|
// plain descriptor protos:
|
|
|
|
type poorFileDescriptorish struct {
|
|
*dpb.FileDescriptorProto
|
|
}
|
|
|
|
func (d poorFileDescriptorish) GetFile() fileDescriptorish {
|
|
return d
|
|
}
|
|
|
|
func (d poorFileDescriptorish) GetFullyQualifiedName() string {
|
|
return d.FileDescriptorProto.GetName()
|
|
}
|
|
|
|
func (d poorFileDescriptorish) AsProto() proto.Message {
|
|
return d.FileDescriptorProto
|
|
}
|
|
|
|
func (d poorFileDescriptorish) GetFileOptions() *dpb.FileOptions {
|
|
return d.FileDescriptorProto.GetOptions()
|
|
}
|
|
|
|
func (d poorFileDescriptorish) FindSymbol(name string) desc.Descriptor {
|
|
return nil
|
|
}
|
|
|
|
func (d poorFileDescriptorish) GetPublicDependencies() []fileDescriptorish {
|
|
return nil
|
|
}
|
|
|
|
func (d poorFileDescriptorish) GetDependencies() []fileDescriptorish {
|
|
return nil
|
|
}
|
|
|
|
func (d poorFileDescriptorish) GetMessageTypes() []msgDescriptorish {
|
|
msgs := d.FileDescriptorProto.GetMessageType()
|
|
pkg := d.FileDescriptorProto.GetPackage()
|
|
ret := make([]msgDescriptorish, len(msgs))
|
|
for i, m := range msgs {
|
|
ret[i] = poorMsgDescriptorish{
|
|
DescriptorProto: m,
|
|
qual: pkg,
|
|
file: d,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d poorFileDescriptorish) GetExtensions() []fldDescriptorish {
|
|
exts := d.FileDescriptorProto.GetExtension()
|
|
pkg := d.FileDescriptorProto.GetPackage()
|
|
ret := make([]fldDescriptorish, len(exts))
|
|
for i, e := range exts {
|
|
ret[i] = poorFldDescriptorish{
|
|
FieldDescriptorProto: e,
|
|
qual: pkg,
|
|
file: d,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d poorFileDescriptorish) GetEnumTypes() []enumDescriptorish {
|
|
ens := d.FileDescriptorProto.GetEnumType()
|
|
pkg := d.FileDescriptorProto.GetPackage()
|
|
ret := make([]enumDescriptorish, len(ens))
|
|
for i, e := range ens {
|
|
ret[i] = poorEnumDescriptorish{
|
|
EnumDescriptorProto: e,
|
|
qual: pkg,
|
|
file: d,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d poorFileDescriptorish) GetServices() []svcDescriptorish {
|
|
svcs := d.FileDescriptorProto.GetService()
|
|
pkg := d.FileDescriptorProto.GetPackage()
|
|
ret := make([]svcDescriptorish, len(svcs))
|
|
for i, s := range svcs {
|
|
ret[i] = poorSvcDescriptorish{
|
|
ServiceDescriptorProto: s,
|
|
qual: pkg,
|
|
file: d,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type poorMsgDescriptorish struct {
|
|
*dpb.DescriptorProto
|
|
qual string
|
|
file fileDescriptorish
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) GetFile() fileDescriptorish {
|
|
return d.file
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) GetFullyQualifiedName() string {
|
|
return qualify(d.qual, d.DescriptorProto.GetName())
|
|
}
|
|
|
|
func qualify(qual, name string) string {
|
|
if qual == "" {
|
|
return name
|
|
} else {
|
|
return fmt.Sprintf("%s.%s", qual, name)
|
|
}
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) AsProto() proto.Message {
|
|
return d.DescriptorProto
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) GetMessageOptions() *dpb.MessageOptions {
|
|
return d.DescriptorProto.GetOptions()
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) GetFields() []fldDescriptorish {
|
|
flds := d.DescriptorProto.GetField()
|
|
ret := make([]fldDescriptorish, len(flds))
|
|
for i, f := range flds {
|
|
ret[i] = poorFldDescriptorish{
|
|
FieldDescriptorProto: f,
|
|
qual: d.GetFullyQualifiedName(),
|
|
file: d.file,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) GetOneOfs() []oneofDescriptorish {
|
|
oos := d.DescriptorProto.GetOneofDecl()
|
|
ret := make([]oneofDescriptorish, len(oos))
|
|
for i, oo := range oos {
|
|
ret[i] = poorOneOfDescriptorish{
|
|
OneofDescriptorProto: oo,
|
|
qual: d.GetFullyQualifiedName(),
|
|
file: d.file,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) GetExtensionRanges() []extRangeDescriptorish {
|
|
mdFqn := d.GetFullyQualifiedName()
|
|
extrs := d.DescriptorProto.GetExtensionRange()
|
|
ret := make([]extRangeDescriptorish, len(extrs))
|
|
for i, extr := range extrs {
|
|
ret[i] = extRangeDescriptorish{
|
|
er: extr,
|
|
qual: mdFqn,
|
|
file: d.file,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) GetNestedMessageTypes() []msgDescriptorish {
|
|
msgs := d.DescriptorProto.GetNestedType()
|
|
ret := make([]msgDescriptorish, len(msgs))
|
|
for i, m := range msgs {
|
|
ret[i] = poorMsgDescriptorish{
|
|
DescriptorProto: m,
|
|
qual: d.GetFullyQualifiedName(),
|
|
file: d.file,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) GetNestedExtensions() []fldDescriptorish {
|
|
flds := d.DescriptorProto.GetExtension()
|
|
ret := make([]fldDescriptorish, len(flds))
|
|
for i, f := range flds {
|
|
ret[i] = poorFldDescriptorish{
|
|
FieldDescriptorProto: f,
|
|
qual: d.GetFullyQualifiedName(),
|
|
file: d.file,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d poorMsgDescriptorish) GetNestedEnumTypes() []enumDescriptorish {
|
|
ens := d.DescriptorProto.GetEnumType()
|
|
ret := make([]enumDescriptorish, len(ens))
|
|
for i, en := range ens {
|
|
ret[i] = poorEnumDescriptorish{
|
|
EnumDescriptorProto: en,
|
|
qual: d.GetFullyQualifiedName(),
|
|
file: d.file,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type poorFldDescriptorish struct {
|
|
*dpb.FieldDescriptorProto
|
|
qual string
|
|
file fileDescriptorish
|
|
}
|
|
|
|
func (d poorFldDescriptorish) GetFile() fileDescriptorish {
|
|
return d.file
|
|
}
|
|
|
|
func (d poorFldDescriptorish) GetFullyQualifiedName() string {
|
|
return qualify(d.qual, d.FieldDescriptorProto.GetName())
|
|
}
|
|
|
|
func (d poorFldDescriptorish) AsProto() proto.Message {
|
|
return d.FieldDescriptorProto
|
|
}
|
|
|
|
func (d poorFldDescriptorish) GetFieldOptions() *dpb.FieldOptions {
|
|
return d.FieldDescriptorProto.GetOptions()
|
|
}
|
|
|
|
func (d poorFldDescriptorish) GetMessageType() *desc.MessageDescriptor {
|
|
return nil
|
|
}
|
|
|
|
func (d poorFldDescriptorish) GetEnumType() *desc.EnumDescriptor {
|
|
return nil
|
|
}
|
|
|
|
type poorOneOfDescriptorish struct {
|
|
*dpb.OneofDescriptorProto
|
|
qual string
|
|
file fileDescriptorish
|
|
}
|
|
|
|
func (d poorOneOfDescriptorish) GetFile() fileDescriptorish {
|
|
return d.file
|
|
}
|
|
|
|
func (d poorOneOfDescriptorish) GetFullyQualifiedName() string {
|
|
return qualify(d.qual, d.OneofDescriptorProto.GetName())
|
|
}
|
|
|
|
func (d poorOneOfDescriptorish) AsProto() proto.Message {
|
|
return d.OneofDescriptorProto
|
|
}
|
|
|
|
func (d poorOneOfDescriptorish) GetOneOfOptions() *dpb.OneofOptions {
|
|
return d.OneofDescriptorProto.GetOptions()
|
|
}
|
|
|
|
func (d poorFldDescriptorish) AsFieldDescriptorProto() *dpb.FieldDescriptorProto {
|
|
return d.FieldDescriptorProto
|
|
}
|
|
|
|
type poorEnumDescriptorish struct {
|
|
*dpb.EnumDescriptorProto
|
|
qual string
|
|
file fileDescriptorish
|
|
}
|
|
|
|
func (d poorEnumDescriptorish) GetFile() fileDescriptorish {
|
|
return d.file
|
|
}
|
|
|
|
func (d poorEnumDescriptorish) GetFullyQualifiedName() string {
|
|
return qualify(d.qual, d.EnumDescriptorProto.GetName())
|
|
}
|
|
|
|
func (d poorEnumDescriptorish) AsProto() proto.Message {
|
|
return d.EnumDescriptorProto
|
|
}
|
|
|
|
func (d poorEnumDescriptorish) GetEnumOptions() *dpb.EnumOptions {
|
|
return d.EnumDescriptorProto.GetOptions()
|
|
}
|
|
|
|
func (d poorEnumDescriptorish) GetValues() []enumValDescriptorish {
|
|
vals := d.EnumDescriptorProto.GetValue()
|
|
ret := make([]enumValDescriptorish, len(vals))
|
|
for i, v := range vals {
|
|
ret[i] = poorEnumValDescriptorish{
|
|
EnumValueDescriptorProto: v,
|
|
qual: d.GetFullyQualifiedName(),
|
|
file: d.file,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type poorEnumValDescriptorish struct {
|
|
*dpb.EnumValueDescriptorProto
|
|
qual string
|
|
file fileDescriptorish
|
|
}
|
|
|
|
func (d poorEnumValDescriptorish) GetFile() fileDescriptorish {
|
|
return d.file
|
|
}
|
|
|
|
func (d poorEnumValDescriptorish) GetFullyQualifiedName() string {
|
|
return qualify(d.qual, d.EnumValueDescriptorProto.GetName())
|
|
}
|
|
|
|
func (d poorEnumValDescriptorish) AsProto() proto.Message {
|
|
return d.EnumValueDescriptorProto
|
|
}
|
|
|
|
func (d poorEnumValDescriptorish) GetEnumValueOptions() *dpb.EnumValueOptions {
|
|
return d.EnumValueDescriptorProto.GetOptions()
|
|
}
|
|
|
|
type poorSvcDescriptorish struct {
|
|
*dpb.ServiceDescriptorProto
|
|
qual string
|
|
file fileDescriptorish
|
|
}
|
|
|
|
func (d poorSvcDescriptorish) GetFile() fileDescriptorish {
|
|
return d.file
|
|
}
|
|
|
|
func (d poorSvcDescriptorish) GetFullyQualifiedName() string {
|
|
return qualify(d.qual, d.ServiceDescriptorProto.GetName())
|
|
}
|
|
|
|
func (d poorSvcDescriptorish) AsProto() proto.Message {
|
|
return d.ServiceDescriptorProto
|
|
}
|
|
|
|
func (d poorSvcDescriptorish) GetServiceOptions() *dpb.ServiceOptions {
|
|
return d.ServiceDescriptorProto.GetOptions()
|
|
}
|
|
|
|
func (d poorSvcDescriptorish) GetMethods() []methodDescriptorish {
|
|
mtds := d.ServiceDescriptorProto.GetMethod()
|
|
ret := make([]methodDescriptorish, len(mtds))
|
|
for i, m := range mtds {
|
|
ret[i] = poorMethodDescriptorish{
|
|
MethodDescriptorProto: m,
|
|
qual: d.GetFullyQualifiedName(),
|
|
file: d.file,
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type poorMethodDescriptorish struct {
|
|
*dpb.MethodDescriptorProto
|
|
qual string
|
|
file fileDescriptorish
|
|
}
|
|
|
|
func (d poorMethodDescriptorish) GetFile() fileDescriptorish {
|
|
return d.file
|
|
}
|
|
|
|
func (d poorMethodDescriptorish) GetFullyQualifiedName() string {
|
|
return qualify(d.qual, d.MethodDescriptorProto.GetName())
|
|
}
|
|
|
|
func (d poorMethodDescriptorish) AsProto() proto.Message {
|
|
return d.MethodDescriptorProto
|
|
}
|
|
|
|
func (d poorMethodDescriptorish) GetMethodOptions() *dpb.MethodOptions {
|
|
return d.MethodDescriptorProto.GetOptions()
|
|
}
|
|
|
|
type extRangeDescriptorish struct {
|
|
er *dpb.DescriptorProto_ExtensionRange
|
|
qual string
|
|
file fileDescriptorish
|
|
}
|
|
|
|
func (er extRangeDescriptorish) GetFile() fileDescriptorish {
|
|
return er.file
|
|
}
|
|
|
|
func (er extRangeDescriptorish) GetFullyQualifiedName() string {
|
|
return qualify(er.qual, fmt.Sprintf("%d-%d", er.er.GetStart(), er.er.GetEnd()-1))
|
|
}
|
|
|
|
func (er extRangeDescriptorish) AsProto() proto.Message {
|
|
return er.er
|
|
}
|
|
|
|
func (er extRangeDescriptorish) GetExtensionRangeOptions() *dpb.ExtensionRangeOptions {
|
|
return er.er.GetOptions()
|
|
}
|
|
|
|
func interpretFileOptions(r *parseResult, fd fileDescriptorish) error {
|
|
opts := fd.GetFileOptions()
|
|
if opts != nil {
|
|
if len(opts.UninterpretedOption) > 0 {
|
|
if remain, err := interpretOptions(r, fd, opts, opts.UninterpretedOption); err != nil {
|
|
return err
|
|
} else {
|
|
opts.UninterpretedOption = remain
|
|
}
|
|
}
|
|
}
|
|
for _, md := range fd.GetMessageTypes() {
|
|
if err := interpretMessageOptions(r, md); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, fld := range fd.GetExtensions() {
|
|
if err := interpretFieldOptions(r, fld); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, ed := range fd.GetEnumTypes() {
|
|
if err := interpretEnumOptions(r, ed); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, sd := range fd.GetServices() {
|
|
opts := sd.GetServiceOptions()
|
|
if len(opts.GetUninterpretedOption()) > 0 {
|
|
if remain, err := interpretOptions(r, sd, opts, opts.UninterpretedOption); err != nil {
|
|
return err
|
|
} else {
|
|
opts.UninterpretedOption = remain
|
|
}
|
|
}
|
|
for _, mtd := range sd.GetMethods() {
|
|
opts := mtd.GetMethodOptions()
|
|
if len(opts.GetUninterpretedOption()) > 0 {
|
|
if remain, err := interpretOptions(r, mtd, opts, opts.UninterpretedOption); err != nil {
|
|
return err
|
|
} else {
|
|
opts.UninterpretedOption = remain
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func interpretMessageOptions(r *parseResult, md msgDescriptorish) error {
|
|
opts := md.GetMessageOptions()
|
|
if opts != nil {
|
|
if len(opts.UninterpretedOption) > 0 {
|
|
if remain, err := interpretOptions(r, md, opts, opts.UninterpretedOption); err != nil {
|
|
return err
|
|
} else {
|
|
opts.UninterpretedOption = remain
|
|
}
|
|
}
|
|
}
|
|
for _, fld := range md.GetFields() {
|
|
if err := interpretFieldOptions(r, fld); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, ood := range md.GetOneOfs() {
|
|
opts := ood.GetOneOfOptions()
|
|
if len(opts.GetUninterpretedOption()) > 0 {
|
|
if remain, err := interpretOptions(r, ood, opts, opts.UninterpretedOption); err != nil {
|
|
return err
|
|
} else {
|
|
opts.UninterpretedOption = remain
|
|
}
|
|
}
|
|
}
|
|
for _, fld := range md.GetNestedExtensions() {
|
|
if err := interpretFieldOptions(r, fld); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, er := range md.GetExtensionRanges() {
|
|
opts := er.GetExtensionRangeOptions()
|
|
if len(opts.GetUninterpretedOption()) > 0 {
|
|
if remain, err := interpretOptions(r, er, opts, opts.UninterpretedOption); err != nil {
|
|
return err
|
|
} else {
|
|
opts.UninterpretedOption = remain
|
|
}
|
|
}
|
|
}
|
|
for _, nmd := range md.GetNestedMessageTypes() {
|
|
if err := interpretMessageOptions(r, nmd); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, ed := range md.GetNestedEnumTypes() {
|
|
if err := interpretEnumOptions(r, ed); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func interpretFieldOptions(r *parseResult, fld fldDescriptorish) error {
|
|
opts := fld.GetFieldOptions()
|
|
if len(opts.GetUninterpretedOption()) > 0 {
|
|
uo := opts.UninterpretedOption
|
|
scope := fmt.Sprintf("field %s", fld.GetFullyQualifiedName())
|
|
|
|
// process json_name pseudo-option
|
|
if index, err := findOption(r, scope, uo, "json_name"); err != nil && !r.lenient {
|
|
return err
|
|
} else if index >= 0 {
|
|
opt := uo[index]
|
|
optNode := r.getOptionNode(opt)
|
|
|
|
// attribute source code info
|
|
if on, ok := optNode.(*ast.OptionNode); ok {
|
|
r.interpretedOptions[on] = []int32{-1, internal.Field_jsonNameTag}
|
|
}
|
|
uo = removeOption(uo, index)
|
|
if opt.StringValue == nil {
|
|
if err := r.errs.handleErrorWithPos(optNode.GetValue().Start(), "%s: expecting string value for json_name option", scope); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
fld.AsFieldDescriptorProto().JsonName = proto.String(string(opt.StringValue))
|
|
}
|
|
}
|
|
|
|
// and process default pseudo-option
|
|
if index, err := processDefaultOption(r, scope, fld, uo); err != nil && !r.lenient {
|
|
return err
|
|
} else if index >= 0 {
|
|
// attribute source code info
|
|
optNode := r.getOptionNode(uo[index])
|
|
if on, ok := optNode.(*ast.OptionNode); ok {
|
|
r.interpretedOptions[on] = []int32{-1, internal.Field_defaultTag}
|
|
}
|
|
uo = removeOption(uo, index)
|
|
}
|
|
|
|
if len(uo) == 0 {
|
|
// no real options, only pseudo-options above? clear out options
|
|
fld.AsFieldDescriptorProto().Options = nil
|
|
} else if remain, err := interpretOptions(r, fld, opts, uo); err != nil {
|
|
return err
|
|
} else {
|
|
opts.UninterpretedOption = remain
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func processDefaultOption(res *parseResult, scope string, fld fldDescriptorish, uos []*dpb.UninterpretedOption) (defaultIndex int, err error) {
|
|
found, err := findOption(res, scope, uos, "default")
|
|
if err != nil || found == -1 {
|
|
return -1, err
|
|
}
|
|
opt := uos[found]
|
|
optNode := res.getOptionNode(opt)
|
|
fdp := fld.AsFieldDescriptorProto()
|
|
if fdp.GetLabel() == dpb.FieldDescriptorProto_LABEL_REPEATED {
|
|
return -1, res.errs.handleErrorWithPos(optNode.GetName().Start(), "%s: default value cannot be set because field is repeated", scope)
|
|
}
|
|
if fdp.GetType() == dpb.FieldDescriptorProto_TYPE_GROUP || fdp.GetType() == dpb.FieldDescriptorProto_TYPE_MESSAGE {
|
|
return -1, res.errs.handleErrorWithPos(optNode.GetName().Start(), "%s: default value cannot be set because field is a message", scope)
|
|
}
|
|
val := optNode.GetValue()
|
|
if _, ok := val.(*ast.MessageLiteralNode); ok {
|
|
return -1, res.errs.handleErrorWithPos(val.Start(), "%s: default value cannot be a message", scope)
|
|
}
|
|
mc := &messageContext{
|
|
res: res,
|
|
file: fld.GetFile(),
|
|
elementName: fld.GetFullyQualifiedName(),
|
|
elementType: descriptorType(fld.AsProto()),
|
|
option: opt,
|
|
}
|
|
v, err := fieldValue(res, mc, fld, val, true)
|
|
if err != nil {
|
|
return -1, res.errs.handleError(err)
|
|
}
|
|
if str, ok := v.(string); ok {
|
|
fld.AsFieldDescriptorProto().DefaultValue = proto.String(str)
|
|
} else if b, ok := v.([]byte); ok {
|
|
fld.AsFieldDescriptorProto().DefaultValue = proto.String(encodeDefaultBytes(b))
|
|
} else {
|
|
var flt float64
|
|
var ok bool
|
|
if flt, ok = v.(float64); !ok {
|
|
var flt32 float32
|
|
if flt32, ok = v.(float32); ok {
|
|
flt = float64(flt32)
|
|
}
|
|
}
|
|
if ok {
|
|
if math.IsInf(flt, 1) {
|
|
fld.AsFieldDescriptorProto().DefaultValue = proto.String("inf")
|
|
} else if ok && math.IsInf(flt, -1) {
|
|
fld.AsFieldDescriptorProto().DefaultValue = proto.String("-inf")
|
|
} else if ok && math.IsNaN(flt) {
|
|
fld.AsFieldDescriptorProto().DefaultValue = proto.String("nan")
|
|
} else {
|
|
fld.AsFieldDescriptorProto().DefaultValue = proto.String(fmt.Sprintf("%v", v))
|
|
}
|
|
} else {
|
|
fld.AsFieldDescriptorProto().DefaultValue = proto.String(fmt.Sprintf("%v", v))
|
|
}
|
|
}
|
|
return found, nil
|
|
}
|
|
|
|
func encodeDefaultBytes(b []byte) string {
|
|
var buf bytes.Buffer
|
|
writeEscapedBytes(&buf, b)
|
|
return buf.String()
|
|
}
|
|
|
|
func interpretEnumOptions(r *parseResult, ed enumDescriptorish) error {
|
|
opts := ed.GetEnumOptions()
|
|
if opts != nil {
|
|
if len(opts.UninterpretedOption) > 0 {
|
|
if remain, err := interpretOptions(r, ed, opts, opts.UninterpretedOption); err != nil {
|
|
return err
|
|
} else {
|
|
opts.UninterpretedOption = remain
|
|
}
|
|
}
|
|
}
|
|
for _, evd := range ed.GetValues() {
|
|
opts := evd.GetEnumValueOptions()
|
|
if len(opts.GetUninterpretedOption()) > 0 {
|
|
if remain, err := interpretOptions(r, evd, opts, opts.UninterpretedOption); err != nil {
|
|
return err
|
|
} else {
|
|
opts.UninterpretedOption = remain
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func interpretOptions(res *parseResult, element descriptorish, opts proto.Message, uninterpreted []*dpb.UninterpretedOption) ([]*dpb.UninterpretedOption, error) {
|
|
optsd, err := desc.LoadMessageDescriptorForMessage(opts)
|
|
if err != nil {
|
|
if res.lenient {
|
|
return uninterpreted, nil
|
|
}
|
|
return nil, res.errs.handleError(err)
|
|
}
|
|
dm := dynamic.NewMessage(optsd)
|
|
err = dm.ConvertFrom(opts)
|
|
if err != nil {
|
|
if res.lenient {
|
|
return uninterpreted, nil
|
|
}
|
|
node := res.nodes[element.AsProto()]
|
|
return nil, res.errs.handleError(ErrorWithSourcePos{Pos: node.Start(), Underlying: err})
|
|
}
|
|
|
|
mc := &messageContext{res: res, file: element.GetFile(), elementName: element.GetFullyQualifiedName(), elementType: descriptorType(element.AsProto())}
|
|
var remain []*dpb.UninterpretedOption
|
|
for _, uo := range uninterpreted {
|
|
node := res.getOptionNode(uo)
|
|
if !uo.Name[0].GetIsExtension() && uo.Name[0].GetNamePart() == "uninterpreted_option" {
|
|
if res.lenient {
|
|
remain = append(remain, uo)
|
|
continue
|
|
}
|
|
// uninterpreted_option might be found reflectively, but is not actually valid for use
|
|
if err := res.errs.handleErrorWithPos(node.GetName().Start(), "%vinvalid option 'uninterpreted_option'", mc); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
mc.option = uo
|
|
path, err := interpretField(res, mc, element, dm, uo, 0, nil)
|
|
if err != nil {
|
|
if res.lenient {
|
|
remain = append(remain, uo)
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
if optn, ok := node.(*ast.OptionNode); ok {
|
|
res.interpretedOptions[optn] = path
|
|
}
|
|
}
|
|
|
|
if res.lenient {
|
|
// If we're lenient, then we don't want to clobber the passed in message
|
|
// and leave it partially populated. So we convert into a copy first
|
|
optsClone := proto.Clone(opts)
|
|
if err := dm.ConvertToDeterministic(optsClone); err != nil {
|
|
// TODO: do this in a more granular way, so we can convert individual
|
|
// fields and leave bad ones uninterpreted instead of skipping all of
|
|
// the work we've done so far.
|
|
return uninterpreted, nil
|
|
}
|
|
// conversion from dynamic message above worked, so now
|
|
// it is safe to overwrite the passed in message
|
|
opts.Reset()
|
|
proto.Merge(opts, optsClone)
|
|
|
|
return remain, nil
|
|
}
|
|
|
|
if err := dm.ValidateRecursive(); err != nil {
|
|
node := res.nodes[element.AsProto()]
|
|
if err := res.errs.handleErrorWithPos(node.Start(), "error in %s options: %v", descriptorType(element.AsProto()), err); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// nw try to convert into the passed in message and fail if not successful
|
|
if err := dm.ConvertToDeterministic(opts); err != nil {
|
|
node := res.nodes[element.AsProto()]
|
|
return nil, res.errs.handleError(ErrorWithSourcePos{Pos: node.Start(), Underlying: err})
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func interpretField(res *parseResult, mc *messageContext, element descriptorish, dm *dynamic.Message, opt *dpb.UninterpretedOption, nameIndex int, pathPrefix []int32) (path []int32, err error) {
|
|
var fld *desc.FieldDescriptor
|
|
nm := opt.GetName()[nameIndex]
|
|
node := res.getOptionNamePartNode(nm)
|
|
if nm.GetIsExtension() {
|
|
extName := nm.GetNamePart()
|
|
if extName[0] == '.' {
|
|
extName = extName[1:] /* skip leading dot */
|
|
}
|
|
fld = findExtension(element.GetFile(), extName, false, map[fileDescriptorish]struct{}{})
|
|
if fld == nil {
|
|
return nil, res.errs.handleErrorWithPos(node.Start(),
|
|
"%vunrecognized extension %s of %s",
|
|
mc, extName, dm.GetMessageDescriptor().GetFullyQualifiedName())
|
|
}
|
|
if fld.GetOwner().GetFullyQualifiedName() != dm.GetMessageDescriptor().GetFullyQualifiedName() {
|
|
return nil, res.errs.handleErrorWithPos(node.Start(),
|
|
"%vextension %s should extend %s but instead extends %s",
|
|
mc, extName, dm.GetMessageDescriptor().GetFullyQualifiedName(), fld.GetOwner().GetFullyQualifiedName())
|
|
}
|
|
} else {
|
|
fld = dm.GetMessageDescriptor().FindFieldByName(nm.GetNamePart())
|
|
if fld == nil {
|
|
return nil, res.errs.handleErrorWithPos(node.Start(),
|
|
"%vfield %s of %s does not exist",
|
|
mc, nm.GetNamePart(), dm.GetMessageDescriptor().GetFullyQualifiedName())
|
|
}
|
|
}
|
|
|
|
path = append(pathPrefix, fld.GetNumber())
|
|
|
|
if len(opt.GetName()) > nameIndex+1 {
|
|
nextnm := opt.GetName()[nameIndex+1]
|
|
nextnode := res.getOptionNamePartNode(nextnm)
|
|
if fld.GetType() != dpb.FieldDescriptorProto_TYPE_MESSAGE {
|
|
return nil, res.errs.handleErrorWithPos(nextnode.Start(),
|
|
"%vcannot set field %s because %s is not a message",
|
|
mc, nextnm.GetNamePart(), nm.GetNamePart())
|
|
}
|
|
if fld.IsRepeated() {
|
|
return nil, res.errs.handleErrorWithPos(nextnode.Start(),
|
|
"%vcannot set field %s because %s is repeated (must use an aggregate)",
|
|
mc, nextnm.GetNamePart(), nm.GetNamePart())
|
|
}
|
|
var fdm *dynamic.Message
|
|
var err error
|
|
if dm.HasField(fld) {
|
|
var v interface{}
|
|
v, err = dm.TryGetField(fld)
|
|
fdm, _ = v.(*dynamic.Message)
|
|
} else {
|
|
fdm = dynamic.NewMessage(fld.GetMessageType())
|
|
err = dm.TrySetField(fld, fdm)
|
|
}
|
|
if err != nil {
|
|
return nil, res.errs.handleError(ErrorWithSourcePos{Pos: node.Start(), Underlying: err})
|
|
}
|
|
// recurse to set next part of name
|
|
return interpretField(res, mc, element, fdm, opt, nameIndex+1, path)
|
|
}
|
|
|
|
optNode := res.getOptionNode(opt)
|
|
if err := setOptionField(res, mc, dm, fld, node, optNode.GetValue()); err != nil {
|
|
return nil, res.errs.handleError(err)
|
|
}
|
|
if fld.IsRepeated() {
|
|
path = append(path, int32(dm.FieldLength(fld))-1)
|
|
}
|
|
return path, nil
|
|
}
|
|
|
|
func findExtension(fd fileDescriptorish, name string, public bool, checked map[fileDescriptorish]struct{}) *desc.FieldDescriptor {
|
|
if _, ok := checked[fd]; ok {
|
|
return nil
|
|
}
|
|
checked[fd] = struct{}{}
|
|
d := fd.FindSymbol(name)
|
|
if d != nil {
|
|
if fld, ok := d.(*desc.FieldDescriptor); ok {
|
|
return fld
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// When public = false, we are searching only directly imported symbols. But we
|
|
// also need to search transitive public imports due to semantics of public imports.
|
|
if public {
|
|
for _, dep := range fd.GetPublicDependencies() {
|
|
d := findExtension(dep, name, true, checked)
|
|
if d != nil {
|
|
return d
|
|
}
|
|
}
|
|
} else {
|
|
for _, dep := range fd.GetDependencies() {
|
|
d := findExtension(dep, name, true, checked)
|
|
if d != nil {
|
|
return d
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setOptionField(res *parseResult, mc *messageContext, dm *dynamic.Message, fld *desc.FieldDescriptor, name ast.Node, val ast.ValueNode) error {
|
|
v := val.Value()
|
|
if sl, ok := v.([]ast.ValueNode); ok {
|
|
// handle slices a little differently than the others
|
|
if !fld.IsRepeated() {
|
|
return errorWithPos(val.Start(), "%vvalue is an array but field is not repeated", mc)
|
|
}
|
|
origPath := mc.optAggPath
|
|
defer func() {
|
|
mc.optAggPath = origPath
|
|
}()
|
|
for index, item := range sl {
|
|
mc.optAggPath = fmt.Sprintf("%s[%d]", origPath, index)
|
|
if v, err := fieldValue(res, mc, richFldDescriptorish{FieldDescriptor: fld}, item, false); err != nil {
|
|
return err
|
|
} else if err = dm.TryAddRepeatedField(fld, v); err != nil {
|
|
return errorWithPos(val.Start(), "%verror setting value: %s", mc, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
v, err := fieldValue(res, mc, richFldDescriptorish{FieldDescriptor: fld}, val, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if fld.IsRepeated() {
|
|
err = dm.TryAddRepeatedField(fld, v)
|
|
} else {
|
|
if dm.HasField(fld) {
|
|
return errorWithPos(name.Start(), "%vnon-repeated option field %s already set", mc, fieldName(fld))
|
|
}
|
|
err = dm.TrySetField(fld, v)
|
|
}
|
|
if err != nil {
|
|
return errorWithPos(val.Start(), "%verror setting value: %s", mc, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func findOption(res *parseResult, scope string, opts []*dpb.UninterpretedOption, name string) (int, error) {
|
|
found := -1
|
|
for i, opt := range opts {
|
|
if len(opt.Name) != 1 {
|
|
continue
|
|
}
|
|
if opt.Name[0].GetIsExtension() || opt.Name[0].GetNamePart() != name {
|
|
continue
|
|
}
|
|
if found >= 0 {
|
|
optNode := res.getOptionNode(opt)
|
|
return -1, res.errs.handleErrorWithPos(optNode.GetName().Start(), "%s: option %s cannot be defined more than once", scope, name)
|
|
}
|
|
found = i
|
|
}
|
|
return found, nil
|
|
}
|
|
|
|
func removeOption(uo []*dpb.UninterpretedOption, indexToRemove int) []*dpb.UninterpretedOption {
|
|
if indexToRemove == 0 {
|
|
return uo[1:]
|
|
} else if int(indexToRemove) == len(uo)-1 {
|
|
return uo[:len(uo)-1]
|
|
} else {
|
|
return append(uo[:indexToRemove], uo[indexToRemove+1:]...)
|
|
}
|
|
}
|
|
|
|
type messageContext struct {
|
|
res *parseResult
|
|
file fileDescriptorish
|
|
elementType string
|
|
elementName string
|
|
option *dpb.UninterpretedOption
|
|
optAggPath string
|
|
}
|
|
|
|
func (c *messageContext) String() string {
|
|
var ctx bytes.Buffer
|
|
if c.elementType != "file" {
|
|
_, _ = fmt.Fprintf(&ctx, "%s %s: ", c.elementType, c.elementName)
|
|
}
|
|
if c.option != nil && c.option.Name != nil {
|
|
ctx.WriteString("option ")
|
|
writeOptionName(&ctx, c.option.Name)
|
|
if c.res.nodes == nil {
|
|
// if we have no source position info, try to provide as much context
|
|
// as possible (if nodes != nil, we don't need this because any errors
|
|
// will actually have file and line numbers)
|
|
if c.optAggPath != "" {
|
|
_, _ = fmt.Fprintf(&ctx, " at %s", c.optAggPath)
|
|
}
|
|
}
|
|
ctx.WriteString(": ")
|
|
}
|
|
return ctx.String()
|
|
}
|
|
|
|
func writeOptionName(buf *bytes.Buffer, parts []*dpb.UninterpretedOption_NamePart) {
|
|
first := true
|
|
for _, p := range parts {
|
|
if first {
|
|
first = false
|
|
} else {
|
|
buf.WriteByte('.')
|
|
}
|
|
nm := p.GetNamePart()
|
|
if nm[0] == '.' {
|
|
// skip leading dot
|
|
nm = nm[1:]
|
|
}
|
|
if p.GetIsExtension() {
|
|
buf.WriteByte('(')
|
|
buf.WriteString(nm)
|
|
buf.WriteByte(')')
|
|
} else {
|
|
buf.WriteString(nm)
|
|
}
|
|
}
|
|
}
|
|
|
|
func fieldName(fld *desc.FieldDescriptor) string {
|
|
if fld.IsExtension() {
|
|
return fld.GetFullyQualifiedName()
|
|
} else {
|
|
return fld.GetName()
|
|
}
|
|
}
|
|
|
|
func valueKind(val interface{}) string {
|
|
switch val := val.(type) {
|
|
case ast.Identifier:
|
|
return "identifier"
|
|
case bool:
|
|
return "bool"
|
|
case int64:
|
|
if val < 0 {
|
|
return "negative integer"
|
|
}
|
|
return "integer"
|
|
case uint64:
|
|
return "integer"
|
|
case float64:
|
|
return "double"
|
|
case string, []byte:
|
|
return "string"
|
|
case []*ast.MessageFieldNode:
|
|
return "message"
|
|
case []ast.ValueNode:
|
|
return "array"
|
|
default:
|
|
return fmt.Sprintf("%T", val)
|
|
}
|
|
}
|
|
|
|
func fieldValue(res *parseResult, mc *messageContext, fld fldDescriptorish, val ast.ValueNode, enumAsString bool) (interface{}, error) {
|
|
v := val.Value()
|
|
t := fld.AsFieldDescriptorProto().GetType()
|
|
switch t {
|
|
case dpb.FieldDescriptorProto_TYPE_ENUM:
|
|
if id, ok := v.(ast.Identifier); ok {
|
|
ev := fld.GetEnumType().FindValueByName(string(id))
|
|
if ev == nil {
|
|
return nil, errorWithPos(val.Start(), "%venum %s has no value named %s", mc, fld.GetEnumType().GetFullyQualifiedName(), id)
|
|
}
|
|
if enumAsString {
|
|
return ev.GetName(), nil
|
|
} else {
|
|
return ev.GetNumber(), nil
|
|
}
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting enum, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_MESSAGE, dpb.FieldDescriptorProto_TYPE_GROUP:
|
|
if aggs, ok := v.([]*ast.MessageFieldNode); ok {
|
|
fmd := fld.GetMessageType()
|
|
fdm := dynamic.NewMessage(fmd)
|
|
origPath := mc.optAggPath
|
|
defer func() {
|
|
mc.optAggPath = origPath
|
|
}()
|
|
for _, a := range aggs {
|
|
if origPath == "" {
|
|
mc.optAggPath = a.Name.Value()
|
|
} else {
|
|
mc.optAggPath = origPath + "." + a.Name.Value()
|
|
}
|
|
var ffld *desc.FieldDescriptor
|
|
if a.Name.IsExtension() {
|
|
n := string(a.Name.Name.AsIdentifier())
|
|
ffld = findExtension(mc.file, n, false, map[fileDescriptorish]struct{}{})
|
|
if ffld == nil {
|
|
// may need to qualify with package name
|
|
pkg := mc.file.GetPackage()
|
|
if pkg != "" {
|
|
ffld = findExtension(mc.file, pkg+"."+n, false, map[fileDescriptorish]struct{}{})
|
|
}
|
|
}
|
|
} else {
|
|
ffld = fmd.FindFieldByName(a.Name.Value())
|
|
}
|
|
if ffld == nil {
|
|
return nil, errorWithPos(val.Start(), "%vfield %s not found", mc, string(a.Name.Name.AsIdentifier()))
|
|
}
|
|
if err := setOptionField(res, mc, fdm, ffld, a.Name, a.Val); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return fdm, nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting message, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_BOOL:
|
|
if b, ok := v.(bool); ok {
|
|
return b, nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting bool, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_BYTES:
|
|
if str, ok := v.(string); ok {
|
|
return []byte(str), nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting bytes, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_STRING:
|
|
if str, ok := v.(string); ok {
|
|
return str, nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting string, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_INT32, dpb.FieldDescriptorProto_TYPE_SINT32, dpb.FieldDescriptorProto_TYPE_SFIXED32:
|
|
if i, ok := v.(int64); ok {
|
|
if i > math.MaxInt32 || i < math.MinInt32 {
|
|
return nil, errorWithPos(val.Start(), "%vvalue %d is out of range for int32", mc, i)
|
|
}
|
|
return int32(i), nil
|
|
}
|
|
if ui, ok := v.(uint64); ok {
|
|
if ui > math.MaxInt32 {
|
|
return nil, errorWithPos(val.Start(), "%vvalue %d is out of range for int32", mc, ui)
|
|
}
|
|
return int32(ui), nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting int32, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_UINT32, dpb.FieldDescriptorProto_TYPE_FIXED32:
|
|
if i, ok := v.(int64); ok {
|
|
if i > math.MaxUint32 || i < 0 {
|
|
return nil, errorWithPos(val.Start(), "%vvalue %d is out of range for uint32", mc, i)
|
|
}
|
|
return uint32(i), nil
|
|
}
|
|
if ui, ok := v.(uint64); ok {
|
|
if ui > math.MaxUint32 {
|
|
return nil, errorWithPos(val.Start(), "%vvalue %d is out of range for uint32", mc, ui)
|
|
}
|
|
return uint32(ui), nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting uint32, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_INT64, dpb.FieldDescriptorProto_TYPE_SINT64, dpb.FieldDescriptorProto_TYPE_SFIXED64:
|
|
if i, ok := v.(int64); ok {
|
|
return i, nil
|
|
}
|
|
if ui, ok := v.(uint64); ok {
|
|
if ui > math.MaxInt64 {
|
|
return nil, errorWithPos(val.Start(), "%vvalue %d is out of range for int64", mc, ui)
|
|
}
|
|
return int64(ui), nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting int64, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_UINT64, dpb.FieldDescriptorProto_TYPE_FIXED64:
|
|
if i, ok := v.(int64); ok {
|
|
if i < 0 {
|
|
return nil, errorWithPos(val.Start(), "%vvalue %d is out of range for uint64", mc, i)
|
|
}
|
|
return uint64(i), nil
|
|
}
|
|
if ui, ok := v.(uint64); ok {
|
|
return ui, nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting uint64, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_DOUBLE:
|
|
if d, ok := v.(float64); ok {
|
|
return d, nil
|
|
}
|
|
if i, ok := v.(int64); ok {
|
|
return float64(i), nil
|
|
}
|
|
if u, ok := v.(uint64); ok {
|
|
return float64(u), nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting double, got %s", mc, valueKind(v))
|
|
case dpb.FieldDescriptorProto_TYPE_FLOAT:
|
|
if d, ok := v.(float64); ok {
|
|
if (d > math.MaxFloat32 || d < -math.MaxFloat32) && !math.IsInf(d, 1) && !math.IsInf(d, -1) && !math.IsNaN(d) {
|
|
return nil, errorWithPos(val.Start(), "%vvalue %f is out of range for float", mc, d)
|
|
}
|
|
return float32(d), nil
|
|
}
|
|
if i, ok := v.(int64); ok {
|
|
return float32(i), nil
|
|
}
|
|
if u, ok := v.(uint64); ok {
|
|
return float32(u), nil
|
|
}
|
|
return nil, errorWithPos(val.Start(), "%vexpecting float, got %s", mc, valueKind(v))
|
|
default:
|
|
return nil, errorWithPos(val.Start(), "%vunrecognized field type: %s", mc, t)
|
|
}
|
|
}
|