editions.go 5.0 KB

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