mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-30 12:32:36 +00:00
671c2db939
When I implemented this initially, I thought the parent of an extension is the extendee. This is incorrect. The parent is the scope in which the extension is defined. This CL changes the code to use the correct parent. This also allows us to reduce some complexity in the implementation because we don't need to wait until the extendee is resolved before we can resolve the features. Change-Id: I6d7012f7502ef95457ab96f3e8abc4ab763d5bcb Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/579275 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Stapelberg <stapelberg@google.com> Auto-Submit: Lasse Folger <lassefolger@google.com>
730 lines
26 KiB
Go
730 lines
26 KiB
Go
// Copyright 2019 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 filedesc
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"google.golang.org/protobuf/internal/descfmt"
|
|
"google.golang.org/protobuf/internal/descopts"
|
|
"google.golang.org/protobuf/internal/encoding/defval"
|
|
"google.golang.org/protobuf/internal/encoding/messageset"
|
|
"google.golang.org/protobuf/internal/genid"
|
|
"google.golang.org/protobuf/internal/pragma"
|
|
"google.golang.org/protobuf/internal/strs"
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
"google.golang.org/protobuf/reflect/protoregistry"
|
|
)
|
|
|
|
// Edition is an Enum for proto2.Edition
|
|
type Edition int32
|
|
|
|
// These values align with the value of Enum in descriptor.proto which allows
|
|
// direct conversion between the proto enum and this enum.
|
|
const (
|
|
EditionUnknown Edition = 0
|
|
EditionProto2 Edition = 998
|
|
EditionProto3 Edition = 999
|
|
Edition2023 Edition = 1000
|
|
EditionUnsupported Edition = 100000
|
|
)
|
|
|
|
// The types in this file may have a suffix:
|
|
// • L0: Contains fields common to all descriptors (except File) and
|
|
// must be initialized up front.
|
|
// • L1: Contains fields specific to a descriptor and
|
|
// must be initialized up front. If the associated proto uses Editions, the
|
|
// Editions features must always be resolved. If not explicitly set, the
|
|
// appropriate default must be resolved and set.
|
|
// • L2: Contains fields that are lazily initialized when constructing
|
|
// from the raw file descriptor. When constructing as a literal, the L2
|
|
// fields must be initialized up front.
|
|
//
|
|
// The types are exported so that packages like reflect/protodesc can
|
|
// directly construct descriptors.
|
|
|
|
type (
|
|
File struct {
|
|
fileRaw
|
|
L1 FileL1
|
|
|
|
once uint32 // atomically set if L2 is valid
|
|
mu sync.Mutex // protects L2
|
|
L2 *FileL2
|
|
}
|
|
FileL1 struct {
|
|
Syntax protoreflect.Syntax
|
|
Edition Edition // Only used if Syntax == Editions
|
|
Path string
|
|
Package protoreflect.FullName
|
|
|
|
Enums Enums
|
|
Messages Messages
|
|
Extensions Extensions
|
|
Services Services
|
|
|
|
EditionFeatures EditionFeatures
|
|
}
|
|
FileL2 struct {
|
|
Options func() protoreflect.ProtoMessage
|
|
Imports FileImports
|
|
Locations SourceLocations
|
|
}
|
|
|
|
EditionFeatures struct {
|
|
// IsFieldPresence is true if field_presence is EXPLICIT
|
|
// https://protobuf.dev/editions/features/#field_presence
|
|
IsFieldPresence bool
|
|
// IsFieldPresence is true if field_presence is LEGACY_REQUIRED
|
|
// https://protobuf.dev/editions/features/#field_presence
|
|
IsLegacyRequired bool
|
|
// IsOpenEnum is true if enum_type is OPEN
|
|
// https://protobuf.dev/editions/features/#enum_type
|
|
IsOpenEnum bool
|
|
// IsPacked is true if repeated_field_encoding is PACKED
|
|
// https://protobuf.dev/editions/features/#repeated_field_encoding
|
|
IsPacked bool
|
|
// IsUTF8Validated is true if utf_validation is VERIFY
|
|
// https://protobuf.dev/editions/features/#utf8_validation
|
|
IsUTF8Validated bool
|
|
// IsDelimitedEncoded is true if message_encoding is DELIMITED
|
|
// https://protobuf.dev/editions/features/#message_encoding
|
|
IsDelimitedEncoded bool
|
|
// IsJSONCompliant is true if json_format is ALLOW
|
|
// https://protobuf.dev/editions/features/#json_format
|
|
IsJSONCompliant bool
|
|
// GenerateLegacyUnmarshalJSON determines if the plugin generates the
|
|
// UnmarshalJSON([]byte) error method for enums.
|
|
GenerateLegacyUnmarshalJSON bool
|
|
}
|
|
)
|
|
|
|
func (fd *File) ParentFile() protoreflect.FileDescriptor { return fd }
|
|
func (fd *File) Parent() protoreflect.Descriptor { return nil }
|
|
func (fd *File) Index() int { return 0 }
|
|
func (fd *File) Syntax() protoreflect.Syntax { return fd.L1.Syntax }
|
|
|
|
// Not exported and just used to reconstruct the original FileDescriptor proto
|
|
func (fd *File) Edition() int32 { return int32(fd.L1.Edition) }
|
|
func (fd *File) Name() protoreflect.Name { return fd.L1.Package.Name() }
|
|
func (fd *File) FullName() protoreflect.FullName { return fd.L1.Package }
|
|
func (fd *File) IsPlaceholder() bool { return false }
|
|
func (fd *File) Options() protoreflect.ProtoMessage {
|
|
if f := fd.lazyInit().Options; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.File
|
|
}
|
|
func (fd *File) Path() string { return fd.L1.Path }
|
|
func (fd *File) Package() protoreflect.FullName { return fd.L1.Package }
|
|
func (fd *File) Imports() protoreflect.FileImports { return &fd.lazyInit().Imports }
|
|
func (fd *File) Enums() protoreflect.EnumDescriptors { return &fd.L1.Enums }
|
|
func (fd *File) Messages() protoreflect.MessageDescriptors { return &fd.L1.Messages }
|
|
func (fd *File) Extensions() protoreflect.ExtensionDescriptors { return &fd.L1.Extensions }
|
|
func (fd *File) Services() protoreflect.ServiceDescriptors { return &fd.L1.Services }
|
|
func (fd *File) SourceLocations() protoreflect.SourceLocations { return &fd.lazyInit().Locations }
|
|
func (fd *File) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, fd) }
|
|
func (fd *File) ProtoType(protoreflect.FileDescriptor) {}
|
|
func (fd *File) ProtoInternal(pragma.DoNotImplement) {}
|
|
|
|
func (fd *File) lazyInit() *FileL2 {
|
|
if atomic.LoadUint32(&fd.once) == 0 {
|
|
fd.lazyInitOnce()
|
|
}
|
|
return fd.L2
|
|
}
|
|
|
|
func (fd *File) lazyInitOnce() {
|
|
fd.mu.Lock()
|
|
if fd.L2 == nil {
|
|
fd.lazyRawInit() // recursively initializes all L2 structures
|
|
}
|
|
atomic.StoreUint32(&fd.once, 1)
|
|
fd.mu.Unlock()
|
|
}
|
|
|
|
// GoPackagePath is a pseudo-internal API for determining the Go package path
|
|
// that this file descriptor is declared in.
|
|
//
|
|
// WARNING: This method is exempt from the compatibility promise and may be
|
|
// removed in the future without warning.
|
|
func (fd *File) GoPackagePath() string {
|
|
return fd.builder.GoPackagePath
|
|
}
|
|
|
|
type (
|
|
Enum struct {
|
|
Base
|
|
L1 EnumL1
|
|
L2 *EnumL2 // protected by fileDesc.once
|
|
}
|
|
EnumL1 struct {
|
|
eagerValues bool // controls whether EnumL2.Values is already populated
|
|
|
|
EditionFeatures EditionFeatures
|
|
}
|
|
EnumL2 struct {
|
|
Options func() protoreflect.ProtoMessage
|
|
Values EnumValues
|
|
ReservedNames Names
|
|
ReservedRanges EnumRanges
|
|
}
|
|
|
|
EnumValue struct {
|
|
Base
|
|
L1 EnumValueL1
|
|
}
|
|
EnumValueL1 struct {
|
|
Options func() protoreflect.ProtoMessage
|
|
Number protoreflect.EnumNumber
|
|
}
|
|
)
|
|
|
|
func (ed *Enum) Options() protoreflect.ProtoMessage {
|
|
if f := ed.lazyInit().Options; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.Enum
|
|
}
|
|
func (ed *Enum) Values() protoreflect.EnumValueDescriptors {
|
|
if ed.L1.eagerValues {
|
|
return &ed.L2.Values
|
|
}
|
|
return &ed.lazyInit().Values
|
|
}
|
|
func (ed *Enum) ReservedNames() protoreflect.Names { return &ed.lazyInit().ReservedNames }
|
|
func (ed *Enum) ReservedRanges() protoreflect.EnumRanges { return &ed.lazyInit().ReservedRanges }
|
|
func (ed *Enum) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, ed) }
|
|
func (ed *Enum) ProtoType(protoreflect.EnumDescriptor) {}
|
|
func (ed *Enum) lazyInit() *EnumL2 {
|
|
ed.L0.ParentFile.lazyInit() // implicitly initializes L2
|
|
return ed.L2
|
|
}
|
|
func (ed *Enum) IsClosed() bool {
|
|
return !ed.L1.EditionFeatures.IsOpenEnum
|
|
}
|
|
|
|
func (ed *EnumValue) Options() protoreflect.ProtoMessage {
|
|
if f := ed.L1.Options; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.EnumValue
|
|
}
|
|
func (ed *EnumValue) Number() protoreflect.EnumNumber { return ed.L1.Number }
|
|
func (ed *EnumValue) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, ed) }
|
|
func (ed *EnumValue) ProtoType(protoreflect.EnumValueDescriptor) {}
|
|
|
|
type (
|
|
Message struct {
|
|
Base
|
|
L1 MessageL1
|
|
L2 *MessageL2 // protected by fileDesc.once
|
|
}
|
|
MessageL1 struct {
|
|
Enums Enums
|
|
Messages Messages
|
|
Extensions Extensions
|
|
IsMapEntry bool // promoted from google.protobuf.MessageOptions
|
|
IsMessageSet bool // promoted from google.protobuf.MessageOptions
|
|
|
|
EditionFeatures EditionFeatures
|
|
}
|
|
MessageL2 struct {
|
|
Options func() protoreflect.ProtoMessage
|
|
Fields Fields
|
|
Oneofs Oneofs
|
|
ReservedNames Names
|
|
ReservedRanges FieldRanges
|
|
RequiredNumbers FieldNumbers // must be consistent with Fields.Cardinality
|
|
ExtensionRanges FieldRanges
|
|
ExtensionRangeOptions []func() protoreflect.ProtoMessage // must be same length as ExtensionRanges
|
|
}
|
|
|
|
Field struct {
|
|
Base
|
|
L1 FieldL1
|
|
}
|
|
FieldL1 struct {
|
|
Options func() protoreflect.ProtoMessage
|
|
Number protoreflect.FieldNumber
|
|
Cardinality protoreflect.Cardinality // must be consistent with Message.RequiredNumbers
|
|
Kind protoreflect.Kind
|
|
StringName stringName
|
|
IsProto3Optional bool // promoted from google.protobuf.FieldDescriptorProto
|
|
IsWeak bool // promoted from google.protobuf.FieldOptions
|
|
Default defaultValue
|
|
ContainingOneof protoreflect.OneofDescriptor // must be consistent with Message.Oneofs.Fields
|
|
Enum protoreflect.EnumDescriptor
|
|
Message protoreflect.MessageDescriptor
|
|
|
|
EditionFeatures EditionFeatures
|
|
}
|
|
|
|
Oneof struct {
|
|
Base
|
|
L1 OneofL1
|
|
}
|
|
OneofL1 struct {
|
|
Options func() protoreflect.ProtoMessage
|
|
Fields OneofFields // must be consistent with Message.Fields.ContainingOneof
|
|
|
|
EditionFeatures EditionFeatures
|
|
}
|
|
)
|
|
|
|
func (md *Message) Options() protoreflect.ProtoMessage {
|
|
if f := md.lazyInit().Options; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.Message
|
|
}
|
|
func (md *Message) IsMapEntry() bool { return md.L1.IsMapEntry }
|
|
func (md *Message) Fields() protoreflect.FieldDescriptors { return &md.lazyInit().Fields }
|
|
func (md *Message) Oneofs() protoreflect.OneofDescriptors { return &md.lazyInit().Oneofs }
|
|
func (md *Message) ReservedNames() protoreflect.Names { return &md.lazyInit().ReservedNames }
|
|
func (md *Message) ReservedRanges() protoreflect.FieldRanges { return &md.lazyInit().ReservedRanges }
|
|
func (md *Message) RequiredNumbers() protoreflect.FieldNumbers { return &md.lazyInit().RequiredNumbers }
|
|
func (md *Message) ExtensionRanges() protoreflect.FieldRanges { return &md.lazyInit().ExtensionRanges }
|
|
func (md *Message) ExtensionRangeOptions(i int) protoreflect.ProtoMessage {
|
|
if f := md.lazyInit().ExtensionRangeOptions[i]; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.ExtensionRange
|
|
}
|
|
func (md *Message) Enums() protoreflect.EnumDescriptors { return &md.L1.Enums }
|
|
func (md *Message) Messages() protoreflect.MessageDescriptors { return &md.L1.Messages }
|
|
func (md *Message) Extensions() protoreflect.ExtensionDescriptors { return &md.L1.Extensions }
|
|
func (md *Message) ProtoType(protoreflect.MessageDescriptor) {}
|
|
func (md *Message) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, md) }
|
|
func (md *Message) lazyInit() *MessageL2 {
|
|
md.L0.ParentFile.lazyInit() // implicitly initializes L2
|
|
return md.L2
|
|
}
|
|
|
|
// IsMessageSet is a pseudo-internal API for checking whether a message
|
|
// should serialize in the proto1 message format.
|
|
//
|
|
// WARNING: This method is exempt from the compatibility promise and may be
|
|
// removed in the future without warning.
|
|
func (md *Message) IsMessageSet() bool {
|
|
return md.L1.IsMessageSet
|
|
}
|
|
|
|
func (fd *Field) Options() protoreflect.ProtoMessage {
|
|
if f := fd.L1.Options; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.Field
|
|
}
|
|
func (fd *Field) Number() protoreflect.FieldNumber { return fd.L1.Number }
|
|
func (fd *Field) Cardinality() protoreflect.Cardinality { return fd.L1.Cardinality }
|
|
func (fd *Field) Kind() protoreflect.Kind {
|
|
return fd.L1.Kind
|
|
}
|
|
func (fd *Field) HasJSONName() bool { return fd.L1.StringName.hasJSON }
|
|
func (fd *Field) JSONName() string { return fd.L1.StringName.getJSON(fd) }
|
|
func (fd *Field) TextName() string { return fd.L1.StringName.getText(fd) }
|
|
func (fd *Field) HasPresence() bool {
|
|
if fd.L1.Cardinality == protoreflect.Repeated {
|
|
return false
|
|
}
|
|
return fd.IsExtension() || fd.L1.EditionFeatures.IsFieldPresence || fd.L1.Message != nil || fd.L1.ContainingOneof != nil
|
|
}
|
|
func (fd *Field) HasOptionalKeyword() bool {
|
|
return (fd.L0.ParentFile.L1.Syntax == protoreflect.Proto2 && fd.L1.Cardinality == protoreflect.Optional && fd.L1.ContainingOneof == nil) || fd.L1.IsProto3Optional
|
|
}
|
|
func (fd *Field) IsPacked() bool {
|
|
if fd.L1.Cardinality != protoreflect.Repeated {
|
|
return false
|
|
}
|
|
switch fd.L1.Kind {
|
|
case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind:
|
|
return false
|
|
}
|
|
return fd.L1.EditionFeatures.IsPacked
|
|
}
|
|
func (fd *Field) IsExtension() bool { return false }
|
|
func (fd *Field) IsWeak() bool { return fd.L1.IsWeak }
|
|
func (fd *Field) IsList() bool { return fd.Cardinality() == protoreflect.Repeated && !fd.IsMap() }
|
|
func (fd *Field) IsMap() bool { return fd.Message() != nil && fd.Message().IsMapEntry() }
|
|
func (fd *Field) MapKey() protoreflect.FieldDescriptor {
|
|
if !fd.IsMap() {
|
|
return nil
|
|
}
|
|
return fd.Message().Fields().ByNumber(genid.MapEntry_Key_field_number)
|
|
}
|
|
func (fd *Field) MapValue() protoreflect.FieldDescriptor {
|
|
if !fd.IsMap() {
|
|
return nil
|
|
}
|
|
return fd.Message().Fields().ByNumber(genid.MapEntry_Value_field_number)
|
|
}
|
|
func (fd *Field) HasDefault() bool { return fd.L1.Default.has }
|
|
func (fd *Field) Default() protoreflect.Value { return fd.L1.Default.get(fd) }
|
|
func (fd *Field) DefaultEnumValue() protoreflect.EnumValueDescriptor { return fd.L1.Default.enum }
|
|
func (fd *Field) ContainingOneof() protoreflect.OneofDescriptor { return fd.L1.ContainingOneof }
|
|
func (fd *Field) ContainingMessage() protoreflect.MessageDescriptor {
|
|
return fd.L0.Parent.(protoreflect.MessageDescriptor)
|
|
}
|
|
func (fd *Field) Enum() protoreflect.EnumDescriptor {
|
|
return fd.L1.Enum
|
|
}
|
|
func (fd *Field) Message() protoreflect.MessageDescriptor {
|
|
if fd.L1.IsWeak {
|
|
if d, _ := protoregistry.GlobalFiles.FindDescriptorByName(fd.L1.Message.FullName()); d != nil {
|
|
return d.(protoreflect.MessageDescriptor)
|
|
}
|
|
}
|
|
return fd.L1.Message
|
|
}
|
|
func (fd *Field) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, fd) }
|
|
func (fd *Field) ProtoType(protoreflect.FieldDescriptor) {}
|
|
|
|
// EnforceUTF8 is a pseudo-internal API to determine whether to enforce UTF-8
|
|
// validation for the string field. This exists for Google-internal use only
|
|
// since proto3 did not enforce UTF-8 validity prior to the open-source release.
|
|
// If this method does not exist, the default is to enforce valid UTF-8.
|
|
//
|
|
// WARNING: This method is exempt from the compatibility promise and may be
|
|
// removed in the future without warning.
|
|
func (fd *Field) EnforceUTF8() bool {
|
|
return fd.L1.EditionFeatures.IsUTF8Validated
|
|
}
|
|
|
|
func (od *Oneof) IsSynthetic() bool {
|
|
return od.L0.ParentFile.L1.Syntax == protoreflect.Proto3 && len(od.L1.Fields.List) == 1 && od.L1.Fields.List[0].HasOptionalKeyword()
|
|
}
|
|
func (od *Oneof) Options() protoreflect.ProtoMessage {
|
|
if f := od.L1.Options; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.Oneof
|
|
}
|
|
func (od *Oneof) Fields() protoreflect.FieldDescriptors { return &od.L1.Fields }
|
|
func (od *Oneof) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, od) }
|
|
func (od *Oneof) ProtoType(protoreflect.OneofDescriptor) {}
|
|
|
|
type (
|
|
Extension struct {
|
|
Base
|
|
L1 ExtensionL1
|
|
L2 *ExtensionL2 // protected by fileDesc.once
|
|
}
|
|
ExtensionL1 struct {
|
|
Number protoreflect.FieldNumber
|
|
Extendee protoreflect.MessageDescriptor
|
|
Cardinality protoreflect.Cardinality
|
|
Kind protoreflect.Kind
|
|
EditionFeatures EditionFeatures
|
|
}
|
|
ExtensionL2 struct {
|
|
Options func() protoreflect.ProtoMessage
|
|
StringName stringName
|
|
IsProto3Optional bool // promoted from google.protobuf.FieldDescriptorProto
|
|
Default defaultValue
|
|
Enum protoreflect.EnumDescriptor
|
|
Message protoreflect.MessageDescriptor
|
|
}
|
|
)
|
|
|
|
func (xd *Extension) Options() protoreflect.ProtoMessage {
|
|
if f := xd.lazyInit().Options; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.Field
|
|
}
|
|
func (xd *Extension) Number() protoreflect.FieldNumber { return xd.L1.Number }
|
|
func (xd *Extension) Cardinality() protoreflect.Cardinality { return xd.L1.Cardinality }
|
|
func (xd *Extension) Kind() protoreflect.Kind { return xd.L1.Kind }
|
|
func (xd *Extension) HasJSONName() bool { return xd.lazyInit().StringName.hasJSON }
|
|
func (xd *Extension) JSONName() string { return xd.lazyInit().StringName.getJSON(xd) }
|
|
func (xd *Extension) TextName() string { return xd.lazyInit().StringName.getText(xd) }
|
|
func (xd *Extension) HasPresence() bool { return xd.L1.Cardinality != protoreflect.Repeated }
|
|
func (xd *Extension) HasOptionalKeyword() bool {
|
|
return (xd.L0.ParentFile.L1.Syntax == protoreflect.Proto2 && xd.L1.Cardinality == protoreflect.Optional) || xd.lazyInit().IsProto3Optional
|
|
}
|
|
func (xd *Extension) IsPacked() bool {
|
|
if xd.L1.Cardinality != protoreflect.Repeated {
|
|
return false
|
|
}
|
|
switch xd.L1.Kind {
|
|
case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind:
|
|
return false
|
|
}
|
|
return xd.L1.EditionFeatures.IsPacked
|
|
}
|
|
func (xd *Extension) IsExtension() bool { return true }
|
|
func (xd *Extension) IsWeak() bool { return false }
|
|
func (xd *Extension) IsList() bool { return xd.Cardinality() == protoreflect.Repeated }
|
|
func (xd *Extension) IsMap() bool { return false }
|
|
func (xd *Extension) MapKey() protoreflect.FieldDescriptor { return nil }
|
|
func (xd *Extension) MapValue() protoreflect.FieldDescriptor { return nil }
|
|
func (xd *Extension) HasDefault() bool { return xd.lazyInit().Default.has }
|
|
func (xd *Extension) Default() protoreflect.Value { return xd.lazyInit().Default.get(xd) }
|
|
func (xd *Extension) DefaultEnumValue() protoreflect.EnumValueDescriptor {
|
|
return xd.lazyInit().Default.enum
|
|
}
|
|
func (xd *Extension) ContainingOneof() protoreflect.OneofDescriptor { return nil }
|
|
func (xd *Extension) ContainingMessage() protoreflect.MessageDescriptor { return xd.L1.Extendee }
|
|
func (xd *Extension) Enum() protoreflect.EnumDescriptor { return xd.lazyInit().Enum }
|
|
func (xd *Extension) Message() protoreflect.MessageDescriptor { return xd.lazyInit().Message }
|
|
func (xd *Extension) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, xd) }
|
|
func (xd *Extension) ProtoType(protoreflect.FieldDescriptor) {}
|
|
func (xd *Extension) ProtoInternal(pragma.DoNotImplement) {}
|
|
func (xd *Extension) lazyInit() *ExtensionL2 {
|
|
xd.L0.ParentFile.lazyInit() // implicitly initializes L2
|
|
return xd.L2
|
|
}
|
|
|
|
type (
|
|
Service struct {
|
|
Base
|
|
L1 ServiceL1
|
|
L2 *ServiceL2 // protected by fileDesc.once
|
|
}
|
|
ServiceL1 struct{}
|
|
ServiceL2 struct {
|
|
Options func() protoreflect.ProtoMessage
|
|
Methods Methods
|
|
}
|
|
|
|
Method struct {
|
|
Base
|
|
L1 MethodL1
|
|
}
|
|
MethodL1 struct {
|
|
Options func() protoreflect.ProtoMessage
|
|
Input protoreflect.MessageDescriptor
|
|
Output protoreflect.MessageDescriptor
|
|
IsStreamingClient bool
|
|
IsStreamingServer bool
|
|
}
|
|
)
|
|
|
|
func (sd *Service) Options() protoreflect.ProtoMessage {
|
|
if f := sd.lazyInit().Options; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.Service
|
|
}
|
|
func (sd *Service) Methods() protoreflect.MethodDescriptors { return &sd.lazyInit().Methods }
|
|
func (sd *Service) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, sd) }
|
|
func (sd *Service) ProtoType(protoreflect.ServiceDescriptor) {}
|
|
func (sd *Service) ProtoInternal(pragma.DoNotImplement) {}
|
|
func (sd *Service) lazyInit() *ServiceL2 {
|
|
sd.L0.ParentFile.lazyInit() // implicitly initializes L2
|
|
return sd.L2
|
|
}
|
|
|
|
func (md *Method) Options() protoreflect.ProtoMessage {
|
|
if f := md.L1.Options; f != nil {
|
|
return f()
|
|
}
|
|
return descopts.Method
|
|
}
|
|
func (md *Method) Input() protoreflect.MessageDescriptor { return md.L1.Input }
|
|
func (md *Method) Output() protoreflect.MessageDescriptor { return md.L1.Output }
|
|
func (md *Method) IsStreamingClient() bool { return md.L1.IsStreamingClient }
|
|
func (md *Method) IsStreamingServer() bool { return md.L1.IsStreamingServer }
|
|
func (md *Method) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, md) }
|
|
func (md *Method) ProtoType(protoreflect.MethodDescriptor) {}
|
|
func (md *Method) ProtoInternal(pragma.DoNotImplement) {}
|
|
|
|
// Surrogate files are can be used to create standalone descriptors
|
|
// where the syntax is only information derived from the parent file.
|
|
var (
|
|
SurrogateProto2 = &File{L1: FileL1{Syntax: protoreflect.Proto2}, L2: &FileL2{}}
|
|
SurrogateProto3 = &File{L1: FileL1{Syntax: protoreflect.Proto3}, L2: &FileL2{}}
|
|
SurrogateEdition2023 = &File{L1: FileL1{Syntax: protoreflect.Editions, Edition: Edition2023}, L2: &FileL2{}}
|
|
)
|
|
|
|
type (
|
|
Base struct {
|
|
L0 BaseL0
|
|
}
|
|
BaseL0 struct {
|
|
FullName protoreflect.FullName // must be populated
|
|
ParentFile *File // must be populated
|
|
Parent protoreflect.Descriptor
|
|
Index int
|
|
}
|
|
)
|
|
|
|
func (d *Base) Name() protoreflect.Name { return d.L0.FullName.Name() }
|
|
func (d *Base) FullName() protoreflect.FullName { return d.L0.FullName }
|
|
func (d *Base) ParentFile() protoreflect.FileDescriptor {
|
|
if d.L0.ParentFile == SurrogateProto2 || d.L0.ParentFile == SurrogateProto3 {
|
|
return nil // surrogate files are not real parents
|
|
}
|
|
return d.L0.ParentFile
|
|
}
|
|
func (d *Base) Parent() protoreflect.Descriptor { return d.L0.Parent }
|
|
func (d *Base) Index() int { return d.L0.Index }
|
|
func (d *Base) Syntax() protoreflect.Syntax { return d.L0.ParentFile.Syntax() }
|
|
func (d *Base) IsPlaceholder() bool { return false }
|
|
func (d *Base) ProtoInternal(pragma.DoNotImplement) {}
|
|
|
|
type stringName struct {
|
|
hasJSON bool
|
|
once sync.Once
|
|
nameJSON string
|
|
nameText string
|
|
}
|
|
|
|
// InitJSON initializes the name. It is exported for use by other internal packages.
|
|
func (s *stringName) InitJSON(name string) {
|
|
s.hasJSON = true
|
|
s.nameJSON = name
|
|
}
|
|
|
|
// Returns true if this field is structured like the synthetic field of a proto2
|
|
// group. This allows us to expand our treatment of delimited fields without
|
|
// breaking proto2 files that have been upgraded to editions.
|
|
func isGroupLike(fd protoreflect.FieldDescriptor) bool {
|
|
// Groups are always group types.
|
|
if fd.Kind() != protoreflect.GroupKind {
|
|
return false
|
|
}
|
|
|
|
// Group fields are always the lowercase type name.
|
|
if strings.ToLower(string(fd.Message().Name())) != string(fd.Name()) {
|
|
return false
|
|
}
|
|
|
|
// Groups could only be defined in the same file they're used.
|
|
if fd.Message().ParentFile() != fd.ParentFile() {
|
|
return false
|
|
}
|
|
|
|
// Group messages are always defined in the same scope as the field. File
|
|
// level extensions will compare NULL == NULL here, which is why the file
|
|
// comparison above is necessary to ensure both come from the same file.
|
|
if fd.IsExtension() {
|
|
return fd.Parent() == fd.Message().Parent()
|
|
}
|
|
return fd.ContainingMessage() == fd.Message().Parent()
|
|
}
|
|
|
|
func (s *stringName) lazyInit(fd protoreflect.FieldDescriptor) *stringName {
|
|
s.once.Do(func() {
|
|
if fd.IsExtension() {
|
|
// For extensions, JSON and text are formatted the same way.
|
|
var name string
|
|
if messageset.IsMessageSetExtension(fd) {
|
|
name = string("[" + fd.FullName().Parent() + "]")
|
|
} else {
|
|
name = string("[" + fd.FullName() + "]")
|
|
}
|
|
s.nameJSON = name
|
|
s.nameText = name
|
|
} else {
|
|
// Format the JSON name.
|
|
if !s.hasJSON {
|
|
s.nameJSON = strs.JSONCamelCase(string(fd.Name()))
|
|
}
|
|
|
|
// Format the text name.
|
|
s.nameText = string(fd.Name())
|
|
if isGroupLike(fd) {
|
|
s.nameText = string(fd.Message().Name())
|
|
}
|
|
}
|
|
})
|
|
return s
|
|
}
|
|
|
|
func (s *stringName) getJSON(fd protoreflect.FieldDescriptor) string { return s.lazyInit(fd).nameJSON }
|
|
func (s *stringName) getText(fd protoreflect.FieldDescriptor) string { return s.lazyInit(fd).nameText }
|
|
|
|
func DefaultValue(v protoreflect.Value, ev protoreflect.EnumValueDescriptor) defaultValue {
|
|
dv := defaultValue{has: v.IsValid(), val: v, enum: ev}
|
|
if b, ok := v.Interface().([]byte); ok {
|
|
// Store a copy of the default bytes, so that we can detect
|
|
// accidental mutations of the original value.
|
|
dv.bytes = append([]byte(nil), b...)
|
|
}
|
|
return dv
|
|
}
|
|
|
|
func unmarshalDefault(b []byte, k protoreflect.Kind, pf *File, ed protoreflect.EnumDescriptor) defaultValue {
|
|
var evs protoreflect.EnumValueDescriptors
|
|
if k == protoreflect.EnumKind {
|
|
// If the enum is declared within the same file, be careful not to
|
|
// blindly call the Values method, lest we bind ourselves in a deadlock.
|
|
if e, ok := ed.(*Enum); ok && e.L0.ParentFile == pf {
|
|
evs = &e.L2.Values
|
|
} else {
|
|
evs = ed.Values()
|
|
}
|
|
|
|
// If we are unable to resolve the enum dependency, use a placeholder
|
|
// enum value since we will not be able to parse the default value.
|
|
if ed.IsPlaceholder() && protoreflect.Name(b).IsValid() {
|
|
v := protoreflect.ValueOfEnum(0)
|
|
ev := PlaceholderEnumValue(ed.FullName().Parent().Append(protoreflect.Name(b)))
|
|
return DefaultValue(v, ev)
|
|
}
|
|
}
|
|
|
|
v, ev, err := defval.Unmarshal(string(b), k, evs, defval.Descriptor)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return DefaultValue(v, ev)
|
|
}
|
|
|
|
type defaultValue struct {
|
|
has bool
|
|
val protoreflect.Value
|
|
enum protoreflect.EnumValueDescriptor
|
|
bytes []byte
|
|
}
|
|
|
|
func (dv *defaultValue) get(fd protoreflect.FieldDescriptor) protoreflect.Value {
|
|
// Return the zero value as the default if unpopulated.
|
|
if !dv.has {
|
|
if fd.Cardinality() == protoreflect.Repeated {
|
|
return protoreflect.Value{}
|
|
}
|
|
switch fd.Kind() {
|
|
case protoreflect.BoolKind:
|
|
return protoreflect.ValueOfBool(false)
|
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
|
|
return protoreflect.ValueOfInt32(0)
|
|
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
|
return protoreflect.ValueOfInt64(0)
|
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
|
|
return protoreflect.ValueOfUint32(0)
|
|
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
|
return protoreflect.ValueOfUint64(0)
|
|
case protoreflect.FloatKind:
|
|
return protoreflect.ValueOfFloat32(0)
|
|
case protoreflect.DoubleKind:
|
|
return protoreflect.ValueOfFloat64(0)
|
|
case protoreflect.StringKind:
|
|
return protoreflect.ValueOfString("")
|
|
case protoreflect.BytesKind:
|
|
return protoreflect.ValueOfBytes(nil)
|
|
case protoreflect.EnumKind:
|
|
if evs := fd.Enum().Values(); evs.Len() > 0 {
|
|
return protoreflect.ValueOfEnum(evs.Get(0).Number())
|
|
}
|
|
return protoreflect.ValueOfEnum(0)
|
|
}
|
|
}
|
|
|
|
if len(dv.bytes) > 0 && !bytes.Equal(dv.bytes, dv.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.
|
|
panic(fmt.Sprintf("detected mutation on the default bytes for %v", fd.FullName()))
|
|
}
|
|
return dv.val
|
|
}
|