...
1
2
3
4
5 package protodesc
6
7 import (
8 "fmt"
9 "os"
10 "sync"
11
12 "google.golang.org/protobuf/internal/editiondefaults"
13 "google.golang.org/protobuf/internal/filedesc"
14 "google.golang.org/protobuf/proto"
15 "google.golang.org/protobuf/reflect/protoreflect"
16 "google.golang.org/protobuf/types/descriptorpb"
17 gofeaturespb "google.golang.org/protobuf/types/gofeaturespb"
18 )
19
20 var defaults = &descriptorpb.FeatureSetDefaults{}
21 var defaultsCacheMu sync.Mutex
22 var defaultsCache = make(map[filedesc.Edition]*descriptorpb.FeatureSet)
23
24 func init() {
25 err := proto.Unmarshal(editiondefaults.Defaults, defaults)
26 if err != nil {
27 fmt.Fprintf(os.Stderr, "unmarshal editions defaults: %v\n", err)
28 os.Exit(1)
29 }
30 }
31
32 func fromEditionProto(epb descriptorpb.Edition) filedesc.Edition {
33 return filedesc.Edition(epb)
34 }
35
36 func toEditionProto(ed filedesc.Edition) descriptorpb.Edition {
37 switch ed {
38 case filedesc.EditionUnknown:
39 return descriptorpb.Edition_EDITION_UNKNOWN
40 case filedesc.EditionProto2:
41 return descriptorpb.Edition_EDITION_PROTO2
42 case filedesc.EditionProto3:
43 return descriptorpb.Edition_EDITION_PROTO3
44 case filedesc.Edition2023:
45 return descriptorpb.Edition_EDITION_2023
46 default:
47 panic(fmt.Sprintf("unknown value for edition: %v", ed))
48 }
49 }
50
51 func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet {
52 defaultsCacheMu.Lock()
53 defer defaultsCacheMu.Unlock()
54 if def, ok := defaultsCache[ed]; ok {
55 return def
56 }
57 edpb := toEditionProto(ed)
58 if defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb {
59
60
61
62 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)
63 os.Exit(1)
64 }
65 fsed := defaults.GetDefaults()[0]
66
67
68
69
70 for _, def := range defaults.GetDefaults() {
71 if def.GetEdition() <= edpb {
72 fsed = def
73 } else {
74 break
75 }
76 }
77 fs := proto.Clone(fsed.GetFixedFeatures()).(*descriptorpb.FeatureSet)
78 proto.Merge(fs, fsed.GetOverridableFeatures())
79 defaultsCache[ed] = fs
80 return fs
81 }
82
83
84
85
86
87
88 func mergeEditionFeatures(parentDesc protoreflect.Descriptor, child *descriptorpb.FeatureSet) filedesc.EditionFeatures {
89 var parentFS filedesc.EditionFeatures
90 switch p := parentDesc.(type) {
91 case *filedesc.File:
92 parentFS = p.L1.EditionFeatures
93 case *filedesc.Message:
94 parentFS = p.L1.EditionFeatures
95 default:
96 panic(fmt.Sprintf("unknown parent type %T", parentDesc))
97 }
98 if child == nil {
99 return parentFS
100 }
101 if fp := child.FieldPresence; fp != nil {
102 parentFS.IsFieldPresence = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED ||
103 *fp == descriptorpb.FeatureSet_EXPLICIT
104 parentFS.IsLegacyRequired = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED
105 }
106 if et := child.EnumType; et != nil {
107 parentFS.IsOpenEnum = *et == descriptorpb.FeatureSet_OPEN
108 }
109
110 if rfe := child.RepeatedFieldEncoding; rfe != nil {
111 parentFS.IsPacked = *rfe == descriptorpb.FeatureSet_PACKED
112 }
113
114 if utf8val := child.Utf8Validation; utf8val != nil {
115 parentFS.IsUTF8Validated = *utf8val == descriptorpb.FeatureSet_VERIFY
116 }
117
118 if me := child.MessageEncoding; me != nil {
119 parentFS.IsDelimitedEncoded = *me == descriptorpb.FeatureSet_DELIMITED
120 }
121
122 if jf := child.JsonFormat; jf != nil {
123 parentFS.IsJSONCompliant = *jf == descriptorpb.FeatureSet_ALLOW
124 }
125
126 if goFeatures, ok := proto.GetExtension(child, gofeaturespb.E_Go).(*gofeaturespb.GoFeatures); ok && goFeatures != nil {
127 if luje := goFeatures.LegacyUnmarshalJsonEnum; luje != nil {
128 parentFS.GenerateLegacyUnmarshalJSON = *luje
129 }
130 }
131
132 return parentFS
133 }
134
135
136
137
138
139 func initFileDescFromFeatureSet(fd *filedesc.File, fs *descriptorpb.FeatureSet) {
140 dfs := getFeatureSetFor(fd.L1.Edition)
141
142 fd.L1.EditionFeatures = mergeEditionFeatures(fd, dfs)
143
144 fd.L1.EditionFeatures = mergeEditionFeatures(fd, fs)
145 }
146
View as plain text