Skip to content

Commit 7bb699b

Browse files
[ObjC] Add the concept of a closed enum.
- FieldDescriptor: - Add a new flag to mark when the enum on the field is closed (vs open). - Support computing the state for when generated sources predate the support. - EnumDescriptor: - Support passing flags to the descriptor creation, currently closed is the only new flag. - Add an isClosed property to expose the state of the enum. This does NOT update generation yet, allows things to be tested before the generation support is added. PiperOrigin-RevId: 488671606
1 parent 3a74266 commit 7bb699b

File tree

6 files changed

+143
-32
lines changed

6 files changed

+143
-32
lines changed

objectivec/GPBDescriptor.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,18 @@ typedef NS_ENUM(uint8_t, GPBFieldType) {
235235
@property(nonatomic, readonly, copy) NSString *name;
236236
/** Function that validates that raw values are valid enum values. */
237237
@property(nonatomic, readonly) GPBEnumValidationFunc enumVerifier;
238+
/**
239+
* Is this a closed enum, meaning that it:
240+
* - Has a fixed set of named values.
241+
* - Encountering values not in this set causes them to be treated as unknown
242+
* fields.
243+
* - The first value (i.e., the default) may be nonzero.
244+
*
245+
* NOTE: This is only accurate if the generate sources for a proto file were
246+
* generated with a protobuf release after the v21.9 version, as the ObjC
247+
* generator wasn't capturing this information.
248+
*/
249+
@property(nonatomic, readonly) BOOL isClosed;
238250

239251
/**
240252
* Returns the enum value name for the given raw enum.

objectivec/GPBDescriptor.m

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ - (instancetype)initWithFieldDescription:(void *)description
5151
file:(GPBFileDescriptor *)file
5252
includesDefault:(BOOL)includesDefault
5353
usesClassRefs:(BOOL)usesClassRefs
54-
proto3OptionalKnown:(BOOL)proto3OptionalKnown;
54+
proto3OptionalKnown:(BOOL)proto3OptionalKnown
55+
closedEnumSupportKnown:(BOOL)closedEnumSupportKnown;
5556

5657
@end
5758

@@ -60,7 +61,8 @@ - (instancetype)initWithName:(NSString *)name
6061
valueNames:(const char *)valueNames
6162
values:(const int32_t *)values
6263
count:(uint32_t)valueCount
63-
enumVerifier:(GPBEnumValidationFunc)enumVerifier;
64+
enumVerifier:(GPBEnumValidationFunc)enumVerifier
65+
flags:(GPBEnumDescriptorInitializationFlags)flags;
6466
@end
6567

6668
// Direct access is use for speed, to avoid even internally declaring things
@@ -150,6 +152,8 @@ + (instancetype)allocDescriptorForClass:(Class)messageClass
150152
BOOL fieldsIncludeDefault = (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0;
151153
BOOL usesClassRefs = (flags & GPBDescriptorInitializationFlag_UsesClassRefs) != 0;
152154
BOOL proto3OptionalKnown = (flags & GPBDescriptorInitializationFlag_Proto3OptionalKnown) != 0;
155+
BOOL closedEnumSupportKnown =
156+
(flags & GPBDescriptorInitializationFlag_ClosedEnumSupportKnown) != 0;
153157

154158
void *desc;
155159
for (uint32_t i = 0; i < fieldCount; ++i) {
@@ -164,7 +168,8 @@ + (instancetype)allocDescriptorForClass:(Class)messageClass
164168
file:file
165169
includesDefault:fieldsIncludeDefault
166170
usesClassRefs:usesClassRefs
167-
proto3OptionalKnown:proto3OptionalKnown];
171+
proto3OptionalKnown:proto3OptionalKnown
172+
closedEnumSupportKnown:closedEnumSupportKnown];
168173
[fields addObject:fieldDescriptor];
169174
[fieldDescriptor release];
170175
}
@@ -475,7 +480,8 @@ - (instancetype)initWithFieldDescription:(void *)description
475480
file:(GPBFileDescriptor *)file
476481
includesDefault:(BOOL)includesDefault
477482
usesClassRefs:(BOOL)usesClassRefs
478-
proto3OptionalKnown:(BOOL)proto3OptionalKnown {
483+
proto3OptionalKnown:(BOOL)proto3OptionalKnown
484+
closedEnumSupportKnown:(BOOL)closedEnumSupportKnown {
479485
if ((self = [super init])) {
480486
GPBMessageFieldDescription *coreDesc;
481487
if (includesDefault) {
@@ -506,6 +512,21 @@ - (instancetype)initWithFieldDescription:(void *)description
506512
}
507513
}
508514

515+
// If the ClosedEnum flag wasn't known (i.e. generated code from an older
516+
// version), compute the flag for the rest of the runtime.
517+
if (!closedEnumSupportKnown) {
518+
// NOTE: This isn't correct, it is using the syntax of the file that
519+
// declared the field, not the syntax of the file that declared the
520+
// enum; but for older generated code, that's all we have and that happens
521+
// to be what the runtime was doing (even though it was wrong). This is
522+
// only wrong in the rare cases an enum is declared in a proto3 syntax
523+
// file but used for a field in the proto2 syntax file.
524+
BOOL isClosedEnum = (dataType == GPBDataTypeEnum && file.syntax != GPBFileSyntaxProto3);
525+
if (isClosedEnum) {
526+
coreDesc->flags |= GPBFieldClosedEnum;
527+
}
528+
}
529+
509530
if (isMapOrArray) {
510531
// map<>/repeated fields get a *Count property (inplace of a has*) to
511532
// support checking if there are any entries without triggering
@@ -535,9 +556,14 @@ - (instancetype)initWithFieldDescription:(void *)description
535556
NSAssert(msgClass_, @"Class %s not defined", className);
536557
}
537558
} else if (dataType == GPBDataTypeEnum) {
559+
enumDescriptor_ = coreDesc->dataTypeSpecific.enumDescFunc();
560+
#if defined(DEBUG) && DEBUG
538561
NSAssert((coreDesc->flags & GPBFieldHasEnumDescriptor) != 0,
539562
@"Field must have GPBFieldHasEnumDescriptor set");
540-
enumDescriptor_ = coreDesc->dataTypeSpecific.enumDescFunc();
563+
NSAssert(!closedEnumSupportKnown ||
564+
(((coreDesc->flags & GPBFieldClosedEnum) != 0) == enumDescriptor_.isClosed),
565+
@"Internal error, ClosedEnum flag doesn't agree with EnumDescriptor");
566+
#endif // DEBUG
541567
}
542568

543569
// Non map<>/repeated fields can have defaults in proto2 syntax.
@@ -740,6 +766,7 @@ @implementation GPBEnumDescriptor {
740766
const uint8_t *extraTextFormatInfo_;
741767
uint32_t *nameOffsets_;
742768
uint32_t valueCount_;
769+
uint32_t flags_;
743770
}
744771

745772
@synthesize name = name_;
@@ -749,12 +776,14 @@ + (instancetype)allocDescriptorForName:(NSString *)name
749776
valueNames:(const char *)valueNames
750777
values:(const int32_t *)values
751778
count:(uint32_t)valueCount
752-
enumVerifier:(GPBEnumValidationFunc)enumVerifier {
779+
enumVerifier:(GPBEnumValidationFunc)enumVerifier
780+
flags:(GPBEnumDescriptorInitializationFlags)flags {
753781
GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name
754782
valueNames:valueNames
755783
values:values
756784
count:valueCount
757-
enumVerifier:enumVerifier];
785+
enumVerifier:enumVerifier
786+
flags:flags];
758787
return descriptor;
759788
}
760789

@@ -763,29 +792,61 @@ + (instancetype)allocDescriptorForName:(NSString *)name
763792
values:(const int32_t *)values
764793
count:(uint32_t)valueCount
765794
enumVerifier:(GPBEnumValidationFunc)enumVerifier
795+
flags:(GPBEnumDescriptorInitializationFlags)flags
766796
extraTextFormatInfo:(const char *)extraTextFormatInfo {
767797
// Call the common case.
768798
GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name
769799
valueNames:valueNames
770800
values:values
771801
count:valueCount
772-
enumVerifier:enumVerifier];
802+
enumVerifier:enumVerifier
803+
flags:flags];
773804
// Set the extra info.
774805
descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo;
775806
return descriptor;
776807
}
777808

809+
+ (instancetype)allocDescriptorForName:(NSString *)name
810+
valueNames:(const char *)valueNames
811+
values:(const int32_t *)values
812+
count:(uint32_t)valueCount
813+
enumVerifier:(GPBEnumValidationFunc)enumVerifier {
814+
return [self allocDescriptorForName:name
815+
valueNames:valueNames
816+
values:values
817+
count:valueCount
818+
enumVerifier:enumVerifier
819+
flags:GPBEnumDescriptorInitializationFlag_None];
820+
}
821+
822+
+ (instancetype)allocDescriptorForName:(NSString *)name
823+
valueNames:(const char *)valueNames
824+
values:(const int32_t *)values
825+
count:(uint32_t)valueCount
826+
enumVerifier:(GPBEnumValidationFunc)enumVerifier
827+
extraTextFormatInfo:(const char *)extraTextFormatInfo {
828+
return [self allocDescriptorForName:name
829+
valueNames:valueNames
830+
values:values
831+
count:valueCount
832+
enumVerifier:enumVerifier
833+
flags:GPBEnumDescriptorInitializationFlag_None
834+
extraTextFormatInfo:extraTextFormatInfo];
835+
}
836+
778837
- (instancetype)initWithName:(NSString *)name
779838
valueNames:(const char *)valueNames
780839
values:(const int32_t *)values
781840
count:(uint32_t)valueCount
782-
enumVerifier:(GPBEnumValidationFunc)enumVerifier {
841+
enumVerifier:(GPBEnumValidationFunc)enumVerifier
842+
flags:(GPBEnumDescriptorInitializationFlags)flags {
783843
if ((self = [super init])) {
784844
name_ = [name copy];
785845
valueNames_ = valueNames;
786846
values_ = values;
787847
valueCount_ = valueCount;
788848
enumVerifier_ = enumVerifier;
849+
flags_ = flags;
789850
}
790851
return self;
791852
}
@@ -796,6 +857,10 @@ - (void)dealloc {
796857
[super dealloc];
797858
}
798859

860+
- (BOOL)isClosed {
861+
return (flags_ & GPBEnumDescriptorInitializationFlag_IsClosed) != 0;
862+
}
863+
799864
- (void)calcValueNameOffsets {
800865
@synchronized(self) {
801866
if (nameOffsets_ != NULL) {

objectivec/GPBDescriptor_PackagePrivate.h

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ typedef NS_OPTIONS(uint16_t, GPBFieldFlags) {
7373
GPBFieldMapKeySFixed64 = 10 << 8,
7474
GPBFieldMapKeyBool = 11 << 8,
7575
GPBFieldMapKeyString = 12 << 8,
76+
77+
// If the enum for this field is "closed", meaning that it:
78+
// - Has a fixed set of named values.
79+
// - Encountering values not in this set causes them to be treated as unknown
80+
// fields.
81+
// - The first value (i.e., the default) may be nonzero.
82+
// NOTE: This could be tracked just on the GPBEnumDescriptor, but to support
83+
// previously generated code, there would be not data to get the behavior
84+
// correct, so instead it is tracked on the field. If old source compatibility
85+
// is removed, this could be removed and the GPBEnumDescription fetched from
86+
// the GPBFieldDescriptor instead.
87+
GPBFieldClosedEnum = 1 << 12,
7688
};
7789

7890
// NOTE: The structures defined here have their members ordered to minimize
@@ -173,6 +185,12 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) {
173185
// at startup. This allows older generated code to still work with the
174186
// current runtime library.
175187
GPBDescriptorInitializationFlag_Proto3OptionalKnown = 1 << 3,
188+
189+
// This flag is used to indicate that the generated sources already contain
190+
// the `GPBFieldCloseEnum` flag and it doesn't have to be computed at startup.
191+
// This allows the older generated code to still work with the current runtime
192+
// library.
193+
GPBDescriptorInitializationFlag_ClosedEnumSupportKnown = 1 << 4,
176194
};
177195

178196
@interface GPBDescriptor () {
@@ -236,14 +254,36 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) {
236254
}
237255
@end
238256

257+
typedef NS_OPTIONS(uint32_t, GPBEnumDescriptorInitializationFlags) {
258+
GPBEnumDescriptorInitializationFlag_None = 0,
259+
260+
// Marks this enum as a closed enum.
261+
GPBEnumDescriptorInitializationFlag_IsClosed = 1 << 1,
262+
};
263+
239264
@interface GPBEnumDescriptor ()
240265
// valueNames, values and extraTextFormatInfo have to be long lived, they are
241266
// held as raw pointers.
267+
+ (instancetype)allocDescriptorForName:(NSString *)name
268+
valueNames:(const char *)valueNames
269+
values:(const int32_t *)values
270+
count:(uint32_t)valueCount
271+
enumVerifier:(GPBEnumValidationFunc)enumVerifier
272+
flags:(GPBEnumDescriptorInitializationFlags)flags;
273+
+ (instancetype)allocDescriptorForName:(NSString *)name
274+
valueNames:(const char *)valueNames
275+
values:(const int32_t *)values
276+
count:(uint32_t)valueCount
277+
enumVerifier:(GPBEnumValidationFunc)enumVerifier
278+
flags:(GPBEnumDescriptorInitializationFlags)flags
279+
extraTextFormatInfo:(const char *)extraTextFormatInfo;
280+
// Deprecated. Calls above with `flags = 0`
242281
+ (instancetype)allocDescriptorForName:(NSString *)name
243282
valueNames:(const char *)valueNames
244283
values:(const int32_t *)values
245284
count:(uint32_t)valueCount
246285
enumVerifier:(GPBEnumValidationFunc)enumVerifier;
286+
// Deprecated. Calls above with `flags = 0`
247287
+ (instancetype)allocDescriptorForName:(NSString *)name
248288
valueNames:(const char *)valueNames
249289
values:(const int32_t *)values
@@ -297,6 +337,10 @@ GPB_INLINE uint32_t GPBFieldNumber(GPBFieldDescriptor *field) {
297337
return field->description_->number;
298338
}
299339

340+
GPB_INLINE BOOL GPBFieldIsClosedEnum(GPBFieldDescriptor *field) {
341+
return (field->description_->flags & GPBFieldClosedEnum) != 0;
342+
}
343+
300344
#pragma clang diagnostic pop
301345

302346
uint32_t GPBFieldTag(GPBFieldDescriptor *self);
@@ -307,10 +351,6 @@ uint32_t GPBFieldTag(GPBFieldDescriptor *self);
307351
// would be the wire type for packed.
308352
uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self);
309353

310-
GPB_INLINE BOOL GPBHasPreservingUnknownEnumSemantics(GPBFileSyntax syntax) {
311-
return syntax == GPBFileSyntaxProto3;
312-
}
313-
314354
GPB_INLINE BOOL GPBExtensionIsRepeated(GPBExtensionDescription *description) {
315355
return (description->options & GPBExtensionRepeated) != 0;
316356
}

objectivec/GPBDictionary.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,8 +501,7 @@ void GPBDictionaryReadEntry(id mapDictionary, GPBCodedInputStream *stream,
501501
[(NSMutableDictionary *)mapDictionary setObject:value.valueString forKey:key.valueString];
502502
} else {
503503
if (valueDataType == GPBDataTypeEnum) {
504-
if (GPBHasPreservingUnknownEnumSemantics([parentMessage descriptor].file.syntax) ||
505-
[field isValidEnumValue:value.valueEnum]) {
504+
if (!GPBFieldIsClosedEnum(field) || [field isValidEnumValue:value.valueEnum]) {
506505
[mapDictionary setGPBGenericValue:&value forGPBGenericValueKey:&key];
507506
} else {
508507
NSData *data = [mapDictionary serializedDataForUnknownValue:value.valueEnum

0 commit comments

Comments
 (0)