Skip to content

Commit 5fa8cd9

Browse files
committed
[ObjC] Update MessageSet Parsing.
- Follow upb and only accept the first value for `type_id` and `message` - Reflow some of the logic to hopefully make things a little easier to follow/clear. - Validate some more assertion about things the extensions for a MessageSet. PiperOrigin-RevId: 652545240
1 parent 3aa491c commit 5fa8cd9

File tree

5 files changed

+207
-79
lines changed

5 files changed

+207
-79
lines changed

objectivec/GPBDescriptor.m

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,13 +1146,15 @@ - (instancetype)initWithExtensionDescription:(GPBExtensionDescription *)desc
11461146
#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS)
11471147
NSAssert(usesClassRefs, @"Internal error: all extensions should have class refs");
11481148

1149-
// This is also checked by the generator.
1150-
// If the extension is a MessageSet extension, then it must be a message field.
1151-
NSAssert(
1152-
((desc->options & GPBExtensionSetWireFormat) == 0) || desc->dataType == GPBDataTypeMessage,
1153-
@"Internal error: If a MessageSet extension is set, the data type must be a message.");
1154-
// NOTE: Could also check that the exteneded class is a MessageSet, but that would force the ObjC
1155-
// runtime to start up that class and that isn't desirable here.
1149+
// These are also checked by the generator.
1150+
if ((desc->options & GPBExtensionSetWireFormat) != 0) {
1151+
NSAssert(desc->dataType == GPBDataTypeMessage,
1152+
@"Internal error: If a MessageSet extension is set, the data type must be a message.");
1153+
NSAssert((desc->options & GPBExtensionRepeated) == 0,
1154+
@"Internal Error: MessageSet extension can't be repeated.");
1155+
// NOTE: Could also check that the exteneded class is a MessageSet, but that would force the
1156+
// ObjC runtime to start up that class and that isn't desirable here.
1157+
}
11561158
#endif
11571159

11581160
if ((self = [super init])) {

objectivec/GPBMessage.m

Lines changed: 99 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -742,16 +742,12 @@ static void DecodeSingleValueFromInputStream(GPBExtensionDescriptor *extension,
742742
message:targetMessage
743743
extensionRegistry:extensionRegistry];
744744
} else {
745-
// description->dataType == GPBDataTypeMessage
746-
if (GPBExtensionIsWireFormat(description)) {
747-
// For MessageSet fields the message length will have already been
748-
// read.
749-
[targetMessage mergeFromCodedInputStream:input
750-
extensionRegistry:extensionRegistry
751-
endingTag:0];
752-
} else {
753-
[input readMessage:targetMessage extensionRegistry:extensionRegistry];
754-
}
745+
// description->dataType == GPBDataTypeMessage
746+
#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS)
747+
NSCAssert(!GPBExtensionIsWireFormat(description),
748+
@"Internal error: got a MessageSet extension when not expected.");
749+
#endif
750+
[input readMessage:targetMessage extensionRegistry:extensionRegistry];
755751
}
756752
// Nothing to add below since the caller provided the message (and added it).
757753
nsValue = nil;
@@ -2183,81 +2179,85 @@ - (void)parseMessageSet:(GPBCodedInputStream *)input
21832179
extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry {
21842180
uint32_t typeId = 0;
21852181
NSData *rawBytes = nil;
2186-
GPBExtensionDescriptor *extension = nil;
21872182
GPBCodedInputStreamState *state = &input->state_;
2183+
BOOL gotType = NO;
2184+
BOOL gotBytes = NO;
21882185
while (true) {
21892186
uint32_t tag = GPBCodedInputStreamReadTag(state);
2190-
if (tag == 0) {
2187+
if (tag == GPBWireFormatMessageSetItemEndTag || tag == 0) {
21912188
break;
21922189
}
21932190

21942191
if (tag == GPBWireFormatMessageSetTypeIdTag) {
2195-
typeId = GPBCodedInputStreamReadUInt32(state);
2196-
if (typeId != 0) {
2197-
extension = [extensionRegistry extensionForDescriptor:[self descriptor] fieldNumber:typeId];
2192+
uint32_t tmp = GPBCodedInputStreamReadUInt32(state);
2193+
// Spec says only use the first value.
2194+
if (!gotType) {
2195+
gotType = YES;
2196+
typeId = tmp;
21982197
}
21992198
} else if (tag == GPBWireFormatMessageSetMessageTag) {
2200-
rawBytes = [GPBCodedInputStreamReadRetainedBytesNoCopy(state) autorelease];
2199+
if (gotBytes) {
2200+
// Skip over the payload instead of collecting it.
2201+
[input skipField:tag];
2202+
} else {
2203+
rawBytes = [GPBCodedInputStreamReadRetainedBytesNoCopy(state) autorelease];
2204+
gotBytes = YES;
2205+
}
22012206
} else {
2207+
// Don't capture unknowns within the message set impl group.
22022208
if (![input skipField:tag]) {
22032209
break;
22042210
}
22052211
}
22062212
}
22072213

2208-
[input checkLastTagWas:GPBWireFormatMessageSetItemEndTag];
2214+
// If we get here because of end of input (tag zero) or the wrong end tag (within the skipField:),
2215+
// this will error.
2216+
GPBCodedInputStreamCheckLastTagWas(state, GPBWireFormatMessageSetItemEndTag);
22092217

2210-
if (rawBytes != nil && typeId != 0) {
2211-
if (extension != nil) {
2212-
GPBCodedInputStream *newInput = [[GPBCodedInputStream alloc] initWithData:rawBytes];
2213-
@try {
2214-
ExtensionMergeFromInputStream(extension, extension.packable, newInput, extensionRegistry,
2215-
self);
2216-
} @finally {
2217-
[newInput release];
2218-
}
2219-
} else {
2220-
GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
2221-
// rawBytes was created via a NoCopy, so it can be reusing a
2222-
// subrange of another NSData that might go out of scope as things
2223-
// unwind, so a copy is needed to ensure what is saved in the
2224-
// unknown fields stays valid.
2225-
NSData *cloned = [NSData dataWithData:rawBytes];
2226-
[unknownFields mergeMessageSetMessage:typeId data:cloned];
2227-
}
2218+
if (!gotType || !gotBytes) {
2219+
// upb_Decoder_DecodeMessageSetItem does't keep this partial as an unknown field, it just drops
2220+
// it, so do the same thing.
2221+
return;
22282222
}
2229-
}
22302223

2231-
- (void)parseUnknownField:(GPBCodedInputStream *)input
2232-
extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry
2233-
tag:(uint32_t)tag {
2234-
int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag);
2235-
GPBDescriptor *descriptor = [self descriptor];
2236-
GPBExtensionDescriptor *extension = [extensionRegistry extensionForDescriptor:descriptor
2237-
fieldNumber:fieldNumber];
2238-
if (extension == nil) {
2239-
if (descriptor.wireFormat && GPBWireFormatMessageSetItemTag == tag) {
2240-
[self parseMessageSet:input extensionRegistry:extensionRegistry];
2241-
return;
2224+
GPBExtensionDescriptor *extension = [extensionRegistry extensionForDescriptor:[self descriptor]
2225+
fieldNumber:typeId];
2226+
if (extension) {
2227+
#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS)
2228+
NSAssert(extension.dataType == GPBDataTypeMessage,
2229+
@"Internal Error: MessageSet extension must be a message field.");
2230+
NSAssert(GPBExtensionIsWireFormat(extension->description_),
2231+
@"Internal Error: MessageSet extension must have message_set_wire_format set.");
2232+
NSAssert(!GPBExtensionIsRepeated(extension->description_),
2233+
@"Internal Error: MessageSet extension can't be repeated.");
2234+
#endif
2235+
// Look up the existing one to merge to or create a new one.
2236+
GPBMessage *targetMessage = [self getExistingExtension:extension];
2237+
if (!targetMessage) {
2238+
GPBDescriptor *descriptor = [extension.msgClass descriptor];
2239+
targetMessage = [[descriptor.messageClass alloc] init];
2240+
[self setExtension:extension value:targetMessage];
2241+
[targetMessage release];
2242+
}
2243+
GPBCodedInputStream *newInput = [[GPBCodedInputStream alloc] initWithData:rawBytes];
2244+
@try {
2245+
[targetMessage mergeFromCodedInputStream:newInput
2246+
extensionRegistry:extensionRegistry
2247+
endingTag:0];
2248+
} @finally {
2249+
[newInput release];
22422250
}
22432251
} else {
2244-
GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag);
2245-
if (extension.wireType == wireType) {
2246-
ExtensionMergeFromInputStream(extension, extension.packable, input, extensionRegistry, self);
2247-
return;
2248-
}
2249-
// Primitive, repeated types can be packed on unpacked on the wire, and are
2250-
// parsed either way.
2251-
if ([extension isRepeated] && !GPBDataTypeIsObject(extension->description_->dataType) &&
2252-
(extension.alternateWireType == wireType)) {
2253-
ExtensionMergeFromInputStream(extension, !extension.packable, input, extensionRegistry, self);
2254-
return;
2255-
}
2256-
}
2257-
GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
2258-
if (![unknownFields mergeFieldFrom:tag input:input]) {
2259-
[NSException raise:NSInternalInconsistencyException
2260-
format:@"Internal Error: Unable to parse unknown field %u", tag];
2252+
// The extension isn't in the registry, but it was well formed, so the whole group structure
2253+
// get preserved as an unknown field.
2254+
GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
2255+
// rawBytes was created via a NoCopy, so it can be reusing a
2256+
// subrange of another NSData that might go out of scope as things
2257+
// unwind, so a copy is needed to ensure what is saved in the
2258+
// unknown fields stays valid.
2259+
NSData *cloned = [NSData dataWithData:rawBytes];
2260+
[unknownFields mergeMessageSetMessage:typeId data:cloned];
22612261
}
22622262
}
22632263

@@ -2475,6 +2475,7 @@ - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
24752475
uint32_t tag = 0;
24762476
NSUInteger startingIndex = 0;
24772477
NSArray *fields = descriptor->fields_;
2478+
BOOL isMessageSetWireFormat = descriptor.isWireFormat;
24782479
NSUInteger numFields = fields.count;
24792480
while (YES) {
24802481
BOOL merged = NO;
@@ -2551,7 +2552,40 @@ - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
25512552

25522553
if (merged) continue; // On to the next tag
25532554

2554-
[self parseUnknownField:input extensionRegistry:extensionRegistry tag:tag];
2555+
if (isMessageSetWireFormat) {
2556+
if (GPBWireFormatMessageSetItemTag == tag) {
2557+
[self parseMessageSet:input extensionRegistry:extensionRegistry];
2558+
continue; // On to the next tag
2559+
}
2560+
} else {
2561+
// ObjC Runtime currently doesn't track if a message supported extensions, so the check is
2562+
// always done.
2563+
GPBExtensionDescriptor *extension =
2564+
[extensionRegistry extensionForDescriptor:descriptor
2565+
fieldNumber:GPBWireFormatGetTagFieldNumber(tag)];
2566+
if (extension) {
2567+
GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag);
2568+
if (extension.wireType == wireType) {
2569+
ExtensionMergeFromInputStream(extension, extension.packable, input, extensionRegistry,
2570+
self);
2571+
continue; // On to the next tag
2572+
}
2573+
// Primitive, repeated types can be packed on unpacked on the wire, and are
2574+
// parsed either way.
2575+
if ([extension isRepeated] && !GPBDataTypeIsObject(extension->description_->dataType) &&
2576+
(extension.alternateWireType == wireType)) {
2577+
ExtensionMergeFromInputStream(extension, !extension.packable, input, extensionRegistry,
2578+
self);
2579+
continue; // On to the next tag
2580+
}
2581+
}
2582+
}
2583+
2584+
GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
2585+
if (![unknownFields mergeFieldFrom:tag input:input]) {
2586+
[NSException raise:NSInternalInconsistencyException
2587+
format:@"Internal Error: Unable to parse unknown field %u", tag];
2588+
}
25552589
} // while(YES)
25562590
}
25572591

objectivec/Tests/GPBWireFormatTests.m

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
// license that can be found in the LICENSE file or at
66
// https://developers.google.com/open-source/licenses/bsd
77

8-
#import "GPBTestUtilities.h"
9-
108
#import "GPBCodedInputStream.h"
119
#import "GPBMessage_PackagePrivate.h"
10+
#import "GPBTestUtilities.h"
11+
#import "GPBUnknownField.h"
1212
#import "GPBUnknownField_PackagePrivate.h"
13+
#import "GPBUnknownFields.h"
1314
#import "objectivec/Tests/Unittest.pbobjc.h"
1415
#import "objectivec/Tests/UnittestMset.pbobjc.h"
1516

@@ -197,6 +198,84 @@ - (void)testParseMessageSet {
197198
XCTAssertEqualObjects(unknownField.lengthDelimitedList[0], [NSData dataWithBytes:"bar" length:3]);
198199
}
199200

201+
- (void)testParseMessageSet_FirstValueSticks {
202+
MSetRawBreakableMessageSet* raw = [MSetRawBreakableMessageSet message];
203+
204+
{
205+
MSetRawBreakableMessageSet_Item* item = [MSetRawBreakableMessageSet_Item message];
206+
207+
[item.typeIdArray addValue:[MSetMessageExtension1 messageSetExtension].fieldNumber];
208+
MSetMessageExtension1* message1 = [MSetMessageExtension1 message];
209+
message1.i = 123;
210+
NSData* itemData = [message1 data];
211+
[item.messageArray addObject:itemData];
212+
213+
[item.typeIdArray addValue:[MSetMessageExtension2 messageSetExtension].fieldNumber];
214+
MSetMessageExtension2* message2 = [MSetMessageExtension2 message];
215+
message2.str = @"foo";
216+
itemData = [message2 data];
217+
[item.messageArray addObject:itemData];
218+
219+
[raw.itemArray addObject:item];
220+
}
221+
222+
NSData* data = [raw data];
223+
224+
// Parse as a MSetMessage and check the contents.
225+
NSError* err = nil;
226+
MSetMessage* messageSet = [MSetMessage parseFromData:data
227+
extensionRegistry:[MSetUnittestMsetRoot extensionRegistry]
228+
error:&err];
229+
XCTAssertNotNil(messageSet);
230+
XCTAssertNil(err);
231+
XCTAssertTrue([messageSet hasExtension:[MSetMessageExtension1 messageSetExtension]]);
232+
XCTAssertEqual([[messageSet getExtension:[MSetMessageExtension1 messageSetExtension]] i], 123);
233+
XCTAssertFalse([messageSet hasExtension:[MSetMessageExtension2 messageSetExtension]]);
234+
GPBUnknownFields* ufs = [[[GPBUnknownFields alloc] initFromMessage:messageSet] autorelease];
235+
XCTAssertTrue(ufs.empty);
236+
}
237+
238+
- (void)testParseMessageSet_PartialValuesDropped {
239+
MSetRawBreakableMessageSet* raw = [MSetRawBreakableMessageSet message];
240+
241+
{
242+
MSetRawBreakableMessageSet_Item* item = [MSetRawBreakableMessageSet_Item message];
243+
[item.typeIdArray addValue:[MSetMessageExtension1 messageSetExtension].fieldNumber];
244+
// No payload.
245+
[raw.itemArray addObject:item];
246+
}
247+
248+
{
249+
MSetRawBreakableMessageSet_Item* item = [MSetRawBreakableMessageSet_Item message];
250+
// No type ID.
251+
MSetMessageExtension2* message = [MSetMessageExtension2 message];
252+
message.str = @"foo";
253+
NSData* itemData = [message data];
254+
[item.messageArray addObject:itemData];
255+
[raw.itemArray addObject:item];
256+
}
257+
258+
{
259+
MSetRawBreakableMessageSet_Item* item = [MSetRawBreakableMessageSet_Item message];
260+
// Neither type ID nor payload.
261+
[raw.itemArray addObject:item];
262+
}
263+
264+
NSData* data = [raw data];
265+
266+
// Parse as a MSetMessage and check the contents.
267+
NSError* err = nil;
268+
MSetMessage* messageSet = [MSetMessage parseFromData:data
269+
extensionRegistry:[MSetUnittestMsetRoot extensionRegistry]
270+
error:&err];
271+
XCTAssertNotNil(messageSet);
272+
XCTAssertNil(err);
273+
XCTAssertEqual([messageSet extensionsCurrentlySet].count,
274+
(NSUInteger)0); // None because they were all partial and dropped.
275+
GPBUnknownFields* ufs = [[[GPBUnknownFields alloc] initFromMessage:messageSet] autorelease];
276+
XCTAssertTrue(ufs.empty);
277+
}
278+
200279
- (void)assertFieldsInOrder:(NSData*)data {
201280
GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data];
202281
int32_t previousTag = 0;

objectivec/Tests/unittest_mset.proto

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ option objc_class_prefix = "MSet";
1414
// A message with message_set_wire_format.
1515
message Message {
1616
option message_set_wire_format = true;
17+
1718
extensions 4 to max;
1819
}
1920

@@ -40,3 +41,12 @@ message RawMessageSet {
4041
required bytes message = 3;
4142
}
4243
}
44+
45+
// MessageSet wire format is equivalent to this but since the fields
46+
// are repeated they can be left off or over present to testing.
47+
message RawBreakableMessageSet {
48+
repeated group Item = 1 {
49+
repeated int32 type_id = 2;
50+
repeated bytes message = 3;
51+
}
52+
}

src/google/protobuf/compiler/objectivec/extension.cc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,14 @@ ExtensionGenerator::ExtensionGenerator(
3939
ABSL_CHECK(!descriptor->is_map())
4040
<< "error: Extension is a map<>!"
4141
<< " That used to be blocked by the compiler.";
42-
ABSL_CHECK(
43-
!descriptor->containing_type()->options().message_set_wire_format() ||
44-
descriptor->type() == FieldDescriptor::TYPE_MESSAGE)
45-
<< "error: Extension to a message_set_wire_format message and the type "
46-
"wasn't a message!";
42+
if (descriptor->containing_type()->options().message_set_wire_format()) {
43+
ABSL_CHECK(descriptor->type() == FieldDescriptor::TYPE_MESSAGE)
44+
<< "error: Extension to a message_set_wire_format message and the type "
45+
"wasn't a message!";
46+
ABSL_CHECK(!descriptor->is_repeated())
47+
<< "error: Extension to a message_set_wire_format message should not "
48+
"be repeated!";
49+
}
4750
}
4851

4952
void ExtensionGenerator::GenerateMembersHeader(io::Printer* printer) const {

0 commit comments

Comments
 (0)