editions.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Copyright 2019 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package protodesc
  5. import (
  6. "fmt"
  7. "os"
  8. "sync"
  9. "google.golang.org/protobuf/internal/editiondefaults"
  10. "google.golang.org/protobuf/internal/filedesc"
  11. "google.golang.org/protobuf/proto"
  12. "google.golang.org/protobuf/reflect/protoreflect"
  13. "google.golang.org/protobuf/types/descriptorpb"
  14. gofeaturespb "google.golang.org/protobuf/types/gofeaturespb"
  15. )
  16. var defaults = &descriptorpb.FeatureSetDefaults{}
  17. var defaultsCacheMu sync.Mutex
  18. var defaultsCache = make(map[filedesc.Edition]*descriptorpb.FeatureSet)
  19. func init() {
  20. err := proto.Unmarshal(editiondefaults.Defaults, defaults)
  21. if err != nil {
  22. fmt.Fprintf(os.Stderr, "unmarshal editions defaults: %v\n", err)
  23. os.Exit(1)
  24. }
  25. }
  26. func fromEditionProto(epb descriptorpb.Edition) filedesc.Edition {
  27. return filedesc.Edition(epb)
  28. }
  29. func toEditionProto(ed filedesc.Edition) descriptorpb.Edition {
  30. switch ed {
  31. case filedesc.EditionUnknown:
  32. return descriptorpb.Edition_EDITION_UNKNOWN
  33. case filedesc.EditionProto2:
  34. return descriptorpb.Edition_EDITION_PROTO2
  35. case filedesc.EditionProto3:
  36. return descriptorpb.Edition_EDITION_PROTO3
  37. case filedesc.Edition2023:
  38. return descriptorpb.Edition_EDITION_2023
  39. default:
  40. panic(fmt.Sprintf("unknown value for edition: %v", ed))
  41. }
  42. }
  43. func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet {
  44. defaultsCacheMu.Lock()
  45. defer defaultsCacheMu.Unlock()
  46. if def, ok := defaultsCache[ed]; ok {
  47. return def
  48. }
  49. edpb := toEditionProto(ed)
  50. if defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb {
  51. // This should never happen protodesc.(FileOptions).New would fail when
  52. // initializing the file descriptor.
  53. // This most likely means the embedded defaults were not updated.
  54. fmt.Fprintf(os.Stderr, "internal error: unsupported edition %v (did you forget to update the embedded defaults (i.e. the bootstrap descriptor proto)?)\n", edpb)
  55. os.Exit(1)
  56. }
  57. fsed := defaults.GetDefaults()[0]
  58. // Using a linear search for now.
  59. // Editions are guaranteed to be sorted and thus we could use a binary search.
  60. // Given that there are only a handful of editions (with one more per year)
  61. // there is not much reason to use a binary search.
  62. for _, def := range defaults.GetDefaults() {
  63. if def.GetEdition() <= edpb {
  64. fsed = def
  65. } else {
  66. break
  67. }
  68. }
  69. fs := proto.Clone(fsed.GetFixedFeatures()).(*descriptorpb.FeatureSet)
  70. proto.Merge(fs, fsed.GetOverridableFeatures())
  71. defaultsCache[ed] = fs
  72. return fs
  73. }
  74. // mergeEditionFeatures merges the parent and child feature sets. This function
  75. // should be used when initializing Go descriptors from descriptor protos which
  76. // is why the parent is a filedesc.EditionsFeatures (Go representation) while
  77. // the child is a descriptorproto.FeatureSet (protoc representation).
  78. // Any feature set by the child overwrites what is set by the parent.
  79. func mergeEditionFeatures(parentDesc protoreflect.Descriptor, child *descriptorpb.FeatureSet) filedesc.EditionFeatures {
  80. var parentFS filedesc.EditionFeatures
  81. switch p := parentDesc.(type) {
  82. case *filedesc.File:
  83. parentFS = p.L1.EditionFeatures
  84. case *filedesc.Message:
  85. parentFS = p.L1.EditionFeatures
  86. default:
  87. panic(fmt.Sprintf("unknown parent type %T", parentDesc))
  88. }
  89. if child == nil {
  90. return parentFS
  91. }
  92. if fp := child.FieldPresence; fp != nil {
  93. parentFS.IsFieldPresence = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED ||
  94. *fp == descriptorpb.FeatureSet_EXPLICIT
  95. parentFS.IsLegacyRequired = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED
  96. }
  97. if et := child.EnumType; et != nil {
  98. parentFS.IsOpenEnum = *et == descriptorpb.FeatureSet_OPEN
  99. }
  100. if rfe := child.RepeatedFieldEncoding; rfe != nil {
  101. parentFS.IsPacked = *rfe == descriptorpb.FeatureSet_PACKED
  102. }
  103. if utf8val := child.Utf8Validation; utf8val != nil {
  104. parentFS.IsUTF8Validated = *utf8val == descriptorpb.FeatureSet_VERIFY
  105. }
  106. if me := child.MessageEncoding; me != nil {
  107. parentFS.IsDelimitedEncoded = *me == descriptorpb.FeatureSet_DELIMITED
  108. }
  109. if jf := child.JsonFormat; jf != nil {
  110. parentFS.IsJSONCompliant = *jf == descriptorpb.FeatureSet_ALLOW
  111. }
  112. if goFeatures, ok := proto.GetExtension(child, gofeaturespb.E_Go).(*gofeaturespb.GoFeatures); ok && goFeatures != nil {
  113. if luje := goFeatures.LegacyUnmarshalJsonEnum; luje != nil {
  114. parentFS.GenerateLegacyUnmarshalJSON = *luje
  115. }
  116. }
  117. return parentFS
  118. }
  119. // initFileDescFromFeatureSet initializes editions related fields in fd based
  120. // on fs. If fs is nil it is assumed to be an empty featureset and all fields
  121. // will be initialized with the appropriate default. fd.L1.Edition must be set
  122. // before calling this function.
  123. func initFileDescFromFeatureSet(fd *filedesc.File, fs *descriptorpb.FeatureSet) {
  124. dfs := getFeatureSetFor(fd.L1.Edition)
  125. // initialize the featureset with the defaults
  126. fd.L1.EditionFeatures = mergeEditionFeatures(fd, dfs)
  127. // overwrite any options explicitly specified
  128. fd.L1.EditionFeatures = mergeEditionFeatures(fd, fs)
  129. }