[ObjC] Put out of range closed enum extension values in unknown fields.

For normal fields, closed enums get their out of range values put into unknown
fields, but that wasn't happening for extension fields, this fixes that by
adding the validation during parsing.

Also document on the getExtension API what happens with enums.

Add tests to confirm expected behaviors.

PiperOrigin-RevId: 491356730
pull/11041/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent 7a7bdde799
commit 903639c328
  1. 6
      objectivec/GPBMessage.h
  2. 42
      objectivec/GPBMessage.m
  3. 76
      objectivec/Tests/GPBMessageTests.m

@ -410,6 +410,12 @@ CF_EXTERN_C_END
* repeated fields. If the extension is a Message one will be auto created for * repeated fields. If the extension is a Message one will be auto created for
* you and returned similar to fields. * you and returned similar to fields.
* *
* NOTE: For Enum extensions, if the enum was _closed_, then unknown values
* were parsed into the message's unknown fields instead of ending up in the
* extensions, just like what happens with singular/repeated fields. For open
* enums, the _raw_ value will be in the NSNumber, meaning if one does a
* `switch` on the values, a `default` case should also be included.
*
* @param extension The extension descriptor of the extension to fetch. * @param extension The extension descriptor of the extension to fetch.
* *
* @return The extension matching the given descriptor, or nil if none found. * @return The extension matching the given descriptor, or nil if none found.

@ -108,6 +108,7 @@ static id GetOrCreateMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *fiel
static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field); static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field);
static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap, NSZone *zone) static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap, NSZone *zone)
__attribute__((ns_returns_retained)); __attribute__((ns_returns_retained));
static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self);
#ifdef DEBUG #ifdef DEBUG
static NSError *MessageError(NSInteger code, NSDictionary *userInfo) { static NSError *MessageError(NSInteger code, NSDictionary *userInfo) {
@ -638,6 +639,7 @@ static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
#endif // !defined(__clang_analyzer__) #endif // !defined(__clang_analyzer__)
static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension, static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
GPBMessage *messageToGetExtension,
GPBCodedInputStream *input, GPBCodedInputStream *input,
id<GPBExtensionRegistry> extensionRegistry, id<GPBExtensionRegistry> extensionRegistry,
GPBMessage *existingValue) GPBMessage *existingValue)
@ -645,6 +647,7 @@ static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
// Note that this returns a retained value intentionally. // Note that this returns a retained value intentionally.
static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension, static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
GPBMessage *messageToGetExtension,
GPBCodedInputStream *input, GPBCodedInputStream *input,
id<GPBExtensionRegistry> extensionRegistry, id<GPBExtensionRegistry> extensionRegistry,
GPBMessage *existingValue) { GPBMessage *existingValue) {
@ -681,8 +684,20 @@ static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
return GPBCodedInputStreamReadRetainedBytes(state); return GPBCodedInputStreamReadRetainedBytes(state);
case GPBDataTypeString: case GPBDataTypeString:
return GPBCodedInputStreamReadRetainedString(state); return GPBCodedInputStreamReadRetainedString(state);
case GPBDataTypeEnum: case GPBDataTypeEnum: {
return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadEnum(state)]; int32_t val = GPBCodedInputStreamReadEnum(&input->state_);
GPBEnumDescriptor *enumDescriptor = extension.enumDescriptor;
// If run with source generated before the closed enum support, all enums
// will be considers not closed, so casing to the enum type for a switch
// could cause things to fall off the end of a switch.
if (!enumDescriptor.isClosed || enumDescriptor.enumVerifier(val)) {
return [[NSNumber alloc] initWithInt:val];
} else {
GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(messageToGetExtension);
[unknownFields mergeVarintField:extension->description_->fieldNumber value:val];
return nil;
}
}
case GPBDataTypeGroup: case GPBDataTypeGroup:
case GPBDataTypeMessage: { case GPBDataTypeMessage: {
GPBMessage *message; GPBMessage *message;
@ -726,9 +741,11 @@ static void ExtensionMergeFromInputStream(GPBExtensionDescriptor *extension, BOO
int32_t length = GPBCodedInputStreamReadInt32(state); int32_t length = GPBCodedInputStreamReadInt32(state);
size_t limit = GPBCodedInputStreamPushLimit(state, length); size_t limit = GPBCodedInputStreamPushLimit(state, length);
while (GPBCodedInputStreamBytesUntilLimit(state) > 0) { while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
id value = NewSingleValueFromInputStream(extension, input, extensionRegistry, nil); id value = NewSingleValueFromInputStream(extension, message, input, extensionRegistry, nil);
[message addExtension:extension value:value]; if (value) {
[value release]; [message addExtension:extension value:value];
[value release];
}
} }
GPBCodedInputStreamPopLimit(state, limit); GPBCodedInputStreamPopLimit(state, limit);
} else { } else {
@ -737,13 +754,16 @@ static void ExtensionMergeFromInputStream(GPBExtensionDescriptor *extension, BOO
if (!isRepeated && GPBDataTypeIsMessage(description->dataType)) { if (!isRepeated && GPBDataTypeIsMessage(description->dataType)) {
existingValue = [message getExistingExtension:extension]; existingValue = [message getExistingExtension:extension];
} }
id value = NewSingleValueFromInputStream(extension, input, extensionRegistry, existingValue); id value =
if (isRepeated) { NewSingleValueFromInputStream(extension, message, input, extensionRegistry, existingValue);
[message addExtension:extension value:value]; if (value) {
} else { if (isRepeated) {
[message setExtension:extension value:value]; [message addExtension:extension value:value];
} else {
[message setExtension:extension value:value];
}
[value release];
} }
[value release];
} }
} }

@ -1481,6 +1481,82 @@
XCTAssertEqualObjects(message, message2); XCTAssertEqualObjects(message, message2);
} }
- (void)testClosedEnumsInExtensions {
// Only unknown values.
NSData *data =
DataFromCStr("\xA8\x01\x0A" // optional_nested_enum_extension set to 10
"\x98\x03\x0B" // repeated_nested_enum_extension set to 11
"\xA2\x03\x01\x0C" // repeated_foreign_enum_extension set to 12 (packed)
);
NSError *error = nil;
TestAllExtensions *msg = [TestAllExtensions parseFromData:data
extensionRegistry:[self extensionRegistry]
error:&error];
XCTAssertNil(error);
XCTAssertFalse([msg hasExtension:[UnittestRoot optionalNestedEnumExtension]]);
XCTAssertFalse([msg hasExtension:[UnittestRoot repeatedNestedEnumExtension]]);
XCTAssertFalse([msg hasExtension:[UnittestRoot repeatedForeignEnumExtension]]);
GPBUnknownFieldSet *unknownFields = msg.unknownFields;
GPBUnknownField *field =
[unknownFields getField:[UnittestRoot optionalNestedEnumExtension].fieldNumber];
XCTAssertNotNil(field);
XCTAssertEqual(field.varintList.count, 1);
XCTAssertEqual([field.varintList valueAtIndex:0], 10);
field = [unknownFields getField:[UnittestRoot repeatedNestedEnumExtension].fieldNumber];
XCTAssertNotNil(field);
XCTAssertEqual(field.varintList.count, 1);
XCTAssertEqual([field.varintList valueAtIndex:0], 11);
field = [unknownFields getField:[UnittestRoot repeatedForeignEnumExtension].fieldNumber];
XCTAssertNotNil(field);
XCTAssertEqual(field.varintList.count, 1);
XCTAssertEqual([field.varintList valueAtIndex:0], 12);
// Unknown and known, the known come though an unknown go to unknown fields.
data = DataFromCStr(
"\xA8\x01\x01" // optional_nested_enum_extension set to 1
"\xA8\x01\x0A" // optional_nested_enum_extension set to 10
"\xA8\x01\x02" // optional_nested_enum_extension set to 2
"\x98\x03\x02" // repeated_nested_enum_extension set to 2
"\x98\x03\x0B" // repeated_nested_enum_extension set to 11
"\x98\x03\x03" // repeated_nested_enum_extension set to 3
"\xA2\x03\x03\x04\x0C\x06" // repeated_foreign_enum_extension set to 4, 12, 6 (packed)
);
error = nil;
msg = [TestAllExtensions parseFromData:data
extensionRegistry:[self extensionRegistry]
error:&error];
XCTAssertNil(error);
XCTAssertTrue([msg hasExtension:[UnittestRoot optionalNestedEnumExtension]]);
XCTAssertEqualObjects([msg getExtension:[UnittestRoot optionalNestedEnumExtension]], @2);
XCTAssertTrue([msg hasExtension:[UnittestRoot repeatedNestedEnumExtension]]);
id expected = @[ @2, @3 ];
XCTAssertEqualObjects([msg getExtension:[UnittestRoot repeatedNestedEnumExtension]], expected);
XCTAssertTrue([msg hasExtension:[UnittestRoot repeatedForeignEnumExtension]]);
expected = @[ @4, @6 ];
XCTAssertEqualObjects([msg getExtension:[UnittestRoot repeatedForeignEnumExtension]], expected);
unknownFields = msg.unknownFields;
field = [unknownFields getField:[UnittestRoot optionalNestedEnumExtension].fieldNumber];
XCTAssertNotNil(field);
XCTAssertEqual(field.varintList.count, 1);
XCTAssertEqual([field.varintList valueAtIndex:0], 10);
field = [unknownFields getField:[UnittestRoot repeatedNestedEnumExtension].fieldNumber];
XCTAssertNotNil(field);
XCTAssertEqual(field.varintList.count, 1);
XCTAssertEqual([field.varintList valueAtIndex:0], 11);
field = [unknownFields getField:[UnittestRoot repeatedForeignEnumExtension].fieldNumber];
XCTAssertNotNil(field);
XCTAssertEqual(field.varintList.count, 1);
XCTAssertEqual([field.varintList valueAtIndex:0], 12);
}
- (void)testDefaultingExtensionMessages { - (void)testDefaultingExtensionMessages {
TestAllExtensions *message = [TestAllExtensions message]; TestAllExtensions *message = [TestAllExtensions message];

Loading…
Cancel
Save