blob: be287f61033316c153a993e6e3ff3c02f56b62ce [file] [log] [blame]
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/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 opts != nil {
if len(opts.UninterpretedOption) > 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 opts != nil {
if len(opts.UninterpretedOption) > 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 opts != nil {
if len(opts.UninterpretedOption) > 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 opts != nil {
if len(opts.UninterpretedOption) > 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 opts != nil {
if len(opts.UninterpretedOption) > 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 err == nil && index >= 0 {
opt := uo[index]
optNode := r.getOptionNode(opt)
// attribute source code info
if on, ok := optNode.(*optionNode); ok {
r.interpretedOptions[on] = []int32{-1, internal.Field_jsonNameTag}
}
uo = removeOption(uo, index)
if opt.StringValue == nil {
return ErrorWithSourcePos{Pos: optNode.getValue().start(), Underlying: fmt.Errorf("%s: expecting string value for json_name option", scope)}
}
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 err == nil && index >= 0 {
// attribute source code info
optNode := r.getOptionNode(uo[index])
if on, ok := optNode.(*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 {
return -1, err
} else if found == -1 {
return -1, nil
}
opt := uos[found]
optNode := res.getOptionNode(opt)
fdp := fld.AsFieldDescriptorProto()
if fdp.GetLabel() == dpb.FieldDescriptorProto_LABEL_REPEATED {
return -1, ErrorWithSourcePos{Pos: optNode.getName().start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: optNode.getName().start(), Underlying: fmt.Errorf("%s: default value cannot be set because field is a message", scope)}
}
val := optNode.getValue()
if _, ok := val.(*aggregateLiteralNode); ok {
return -1, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%s: default value cannot be an aggregate", 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, 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 opts != nil {
if len(opts.UninterpretedOption) > 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, 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, 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
return nil, ErrorWithSourcePos{Pos: node.getName().start(), Underlying: fmt.Errorf("%vinvalid option 'uninterpreted_option'", mc)}
}
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.(*optionNode); ok {
res.interpretedOptions[optn] = path
}
}
if err := dm.ValidateRecursive(); err != nil {
// if lenient, we'll let this pass, but it means that some required field was not set!
// TODO: do this in a more granular way, so we can validate individual fields
// and leave them uninterpreted, instead of just having to live with the
// thing having invalid data in extensions.
if !res.lenient {
node := res.nodes[element.AsProto()]
return nil, ErrorWithSourcePos{Pos: node.start(), Underlying: fmt.Errorf("error in %s options: %v", descriptorType(element.AsProto()), err)}
}
}
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.ConvertTo(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)
} else {
// not lenient: try to convert into the passed in message
// and fail is not successful
if err := dm.ConvertTo(opts); err != nil {
node := res.nodes[element.AsProto()]
return nil, ErrorWithSourcePos{Pos: node.start(), Underlying: err}
}
}
return remain, 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, ErrorWithSourcePos{
Pos: node.start(),
Underlying: fmt.Errorf("%vunrecognized extension %s of %s",
mc, extName, dm.GetMessageDescriptor().GetFullyQualifiedName()),
}
}
if fld.GetOwner().GetFullyQualifiedName() != dm.GetMessageDescriptor().GetFullyQualifiedName() {
return nil, ErrorWithSourcePos{
Pos: node.start(),
Underlying: fmt.Errorf("%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, ErrorWithSourcePos{
Pos: node.start(),
Underlying: fmt.Errorf("%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, ErrorWithSourcePos{
Pos: nextnode.start(),
Underlying: fmt.Errorf("%vcannot set field %s because %s is not a message",
mc, nextnm.GetNamePart(), nm.GetNamePart()),
}
}
if fld.IsRepeated() {
return nil, ErrorWithSourcePos{
Pos: nextnode.start(),
Underlying: fmt.Errorf("%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, 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, 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 node, val valueNode) error {
v := val.value()
if sl, ok := v.([]valueNode); ok {
// handle slices a little differently than the others
if !fld.IsRepeated() {
return ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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 ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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 ErrorWithSourcePos{Pos: name.start(), Underlying: fmt.Errorf("%vnon-repeated option field %s already set", mc, fieldName(fld))}
}
err = dm.TrySetField(fld, v)
}
if err != nil {
return ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%verror setting value: %s", mc, err)}
}
return nil
}
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 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 []*aggregateEntryNode:
return "message"
default:
return fmt.Sprintf("%T", val)
}
}
func fieldValue(res *parseResult, mc *messageContext, fld fldDescriptorish, val valueNode, enumAsString bool) (interface{}, error) {
v := val.value()
t := fld.AsFieldDescriptorProto().GetType()
switch t {
case dpb.FieldDescriptorProto_TYPE_ENUM:
if id, ok := v.(identifier); ok {
ev := fld.GetEnumType().FindValueByName(string(id))
if ev == nil {
return nil, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vexpecting enum, got %s", mc, valueKind(v))}
case dpb.FieldDescriptorProto_TYPE_MESSAGE, dpb.FieldDescriptorProto_TYPE_GROUP:
if aggs, ok := v.([]*aggregateEntryNode); 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 := a.name.name.val
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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vfield %s not found", mc, a.name.name.val)}
}
if err := setOptionField(res, mc, fdm, ffld, a.name, a.val); err != nil {
return nil, err
}
}
return fdm, nil
}
return nil, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vexpecting message, got %s", mc, valueKind(v))}
case dpb.FieldDescriptorProto_TYPE_BOOL:
if b, ok := v.(bool); ok {
return b, nil
}
return nil, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vexpecting bool, got %s", mc, valueKind(v))}
case dpb.FieldDescriptorProto_TYPE_BYTES:
if str, ok := v.(string); ok {
return []byte(str), nil
}
return nil, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vexpecting bytes, got %s", mc, valueKind(v))}
case dpb.FieldDescriptorProto_TYPE_STRING:
if str, ok := v.(string); ok {
return str, nil
}
return nil, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vvalue %d is out of range for int32", mc, ui)}
}
return int32(ui), nil
}
return nil, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vvalue %d is out of range for uint32", mc, ui)}
}
return uint32(ui), nil
}
return nil, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vvalue %d is out of range for int64", mc, ui)}
}
return int64(ui), nil
}
return nil, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%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, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vexpecting float, got %s", mc, valueKind(v))}
default:
return nil, ErrorWithSourcePos{Pos: val.start(), Underlying: fmt.Errorf("%vunrecognized field type: %s", mc, t)}
}
}