diff --git a/conformance/failure_list_objc.txt b/conformance/failure_list_objc.txt index 463c3606b9..e34501ead7 100644 --- a/conformance/failure_list_objc.txt +++ b/conformance/failure_list_objc.txt @@ -1,7 +1,2 @@ # JSON input or output tests are skipped (in conformance_objc.m) as mobile # platforms don't support JSON wire format to avoid code bloat. - -Required.Editions_Proto2.ProtobufInput.UnknownOrdering.ProtobufOutput -Required.Editions_Proto3.ProtobufInput.UnknownOrdering.ProtobufOutput -Required.Proto2.ProtobufInput.UnknownOrdering.ProtobufOutput -Required.Proto3.ProtobufInput.UnknownOrdering.ProtobufOutput diff --git a/objectivec/GPBCodedInputStream.m b/objectivec/GPBCodedInputStream.m index ae1124cef0..4ee03ef580 100644 --- a/objectivec/GPBCodedInputStream.m +++ b/objectivec/GPBCodedInputStream.m @@ -13,6 +13,10 @@ #import "GPBUtilities_PackagePrivate.h" #import "GPBWireFormat.h" +// TODO: Consider using on other functions to reduce bloat when +// some compiler optimizations are enabled. +#define GPB_NOINLINE __attribute__((noinline)) + NSString *const GPBCodedInputStreamException = GPBNSStringifySymbol(GPBCodedInputStreamException); NSString *const GPBCodedInputStreamUnderlyingErrorKey = @@ -28,7 +32,8 @@ NSString *const GPBCodedInputStreamErrorDomain = // int CodedInputStream::default_recursion_limit_ = 100; static const NSUInteger kDefaultRecursionLimit = 100; -static void RaiseException(NSInteger code, NSString *reason) { +GPB_NOINLINE +void GPBRaiseStreamError(NSInteger code, NSString *reason) { NSDictionary *errorInfo = nil; if ([reason length]) { errorInfo = @{GPBErrorReasonKey : reason}; @@ -44,7 +49,7 @@ static void RaiseException(NSInteger code, NSString *reason) { GPB_INLINE void CheckRecursionLimit(GPBCodedInputStreamState *state) { if (state->recursionDepth >= kDefaultRecursionLimit) { - RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil); + GPBRaiseStreamError(GPBCodedInputStreamErrorRecursionDepthExceeded, nil); } } @@ -56,19 +61,19 @@ GPB_INLINE void CheckFieldSize(uint64_t size) { if (size > 0x7fffffff) { // TODO: Maybe a different error code for this, but adding one is a breaking // change so reuse an existing one. - RaiseException(GPBCodedInputStreamErrorInvalidSize, nil); + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidSize, nil); } } static void CheckSize(GPBCodedInputStreamState *state, size_t size) { size_t newSize = state->bufferPos + size; if (newSize > state->bufferSize) { - RaiseException(GPBCodedInputStreamErrorInvalidSize, nil); + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidSize, nil); } if (newSize > state->currentLimit) { // Fast forward to end of currentLimit; state->bufferPos = state->currentLimit; - RaiseException(GPBCodedInputStreamErrorSubsectionLimitReached, nil); + GPBRaiseStreamError(GPBCodedInputStreamErrorSubsectionLimitReached, nil); } } @@ -110,7 +115,7 @@ static int64_t ReadRawVarint64(GPBCodedInputStreamState *state) { } shift += 7; } - RaiseException(GPBCodedInputStreamErrorInvalidVarInt, @"Invalid VarInt64"); + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidVarInt, @"Invalid VarInt64"); return 0; } @@ -201,12 +206,12 @@ int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) { state->lastTag = ReadRawVarint32(state); // Tags have to include a valid wireformat. if (!GPBWireFormatIsValidTag(state->lastTag)) { - RaiseException(GPBCodedInputStreamErrorInvalidTag, @"Invalid wireformat in tag."); + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag, @"Invalid wireformat in tag."); } // Zero is not a valid field number. if (GPBWireFormatGetTagFieldNumber(state->lastTag) == 0) { - RaiseException(GPBCodedInputStreamErrorInvalidTag, - @"A zero field number on the wire is invalid."); + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag, + @"A zero field number on the wire is invalid."); } return state->lastTag; } @@ -231,7 +236,7 @@ NSString *GPBCodedInputStreamReadRetainedString(GPBCodedInputStreamState *state) NSLog(@"UTF-8 failure, is some field type 'string' when it should be " @"'bytes'?"); #endif - RaiseException(GPBCodedInputStreamErrorInvalidUTF8, nil); + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidUTF8, nil); } } return result; @@ -262,11 +267,65 @@ NSData *GPBCodedInputStreamReadRetainedBytesNoCopy(GPBCodedInputStreamState *sta return result; } +static void SkipToEndGroupInternal(GPBCodedInputStreamState *state, uint32_t endGroupTag) { + CheckRecursionLimit(state); + ++state->recursionDepth; + while (YES) { + uint32_t tag = GPBCodedInputStreamReadTag(state); + if (tag == endGroupTag || tag == 0) { + GPBCodedInputStreamCheckLastTagWas(state, endGroupTag); // Will fail for end of input. + --state->recursionDepth; + return; + } + switch (GPBWireFormatGetTagWireType(tag)) { + case GPBWireFormatVarint: + (void)ReadRawVarint64(state); + break; + case GPBWireFormatFixed64: + SkipRawData(state, sizeof(uint64_t)); + break; + case GPBWireFormatLengthDelimited: { + uint64_t size = ReadRawVarint64(state); + CheckFieldSize(size); + size_t size2 = (size_t)size; // Cast safe on 32bit because of CheckFieldSize() above. + SkipRawData(state, size2); + break; + } + case GPBWireFormatStartGroup: + SkipToEndGroupInternal(state, GPBWireFormatMakeTag(GPBWireFormatGetTagFieldNumber(tag), + GPBWireFormatEndGroup)); + break; + case GPBWireFormatEndGroup: + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag, @"Unmatched end group"); + break; + case GPBWireFormatFixed32: + SkipRawData(state, sizeof(uint32_t)); + break; + } + } +} + +// This doesn't include the start group, but it collects all bytes until the end group including +// the end group tag. +NSData *GPBCodedInputStreamReadRetainedBytesToEndGroupNoCopy(GPBCodedInputStreamState *state, + int32_t fieldNumber) { + // Better have just read the start of the group. + GPBCodedInputStreamCheckLastTagWas(state, + GPBWireFormatMakeTag(fieldNumber, GPBWireFormatStartGroup)); + const uint8_t *start = state->bytes + state->bufferPos; + SkipToEndGroupInternal(state, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)); + // This will be after the end group tag. + const uint8_t *end = state->bytes + state->bufferPos; + return [[NSData alloc] initWithBytesNoCopy:(void *)start + length:(NSUInteger)(end - start) + freeWhenDone:NO]; +} + size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state, size_t byteLimit) { byteLimit += state->bufferPos; size_t oldLimit = state->currentLimit; if (byteLimit > oldLimit) { - RaiseException(GPBCodedInputStreamErrorInvalidSubsectionLimit, nil); + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidSubsectionLimit, nil); } state->currentLimit = byteLimit; return oldLimit; @@ -286,7 +345,7 @@ BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state) { void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, int32_t value) { if (state->lastTag != value) { - RaiseException(GPBCodedInputStreamErrorInvalidTag, @"Unexpected tag read"); + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag, @"Unexpected tag read"); } } @@ -429,9 +488,9 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, int32_t extensionRegistry:(id)extensionRegistry { CheckRecursionLimit(&state_); ++state_.recursionDepth; - [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry]; - GPBCodedInputStreamCheckLastTagWas(&state_, - GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)); + [message mergeFromCodedInputStream:self + extensionRegistry:extensionRegistry + endingTag:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)]; --state_.recursionDepth; } @@ -452,8 +511,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, int32_t size_t length2 = (size_t)length; // Cast safe on 32bit because of CheckFieldSize() above. size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length2); ++state_.recursionDepth; - [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry]; - GPBCodedInputStreamCheckLastTagWas(&state_, 0); + [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry endingTag:0]; --state_.recursionDepth; GPBCodedInputStreamPopLimit(&state_, oldLimit); } diff --git a/objectivec/GPBCodedInputStream_PackagePrivate.h b/objectivec/GPBCodedInputStream_PackagePrivate.h index fbeecf130c..720bd60ee8 100644 --- a/objectivec/GPBCodedInputStream_PackagePrivate.h +++ b/objectivec/GPBCodedInputStream_PackagePrivate.h @@ -53,6 +53,7 @@ typedef struct GPBCodedInputStreamState { CF_EXTERN_C_BEGIN +void GPBRaiseStreamError(NSInteger code, NSString *reason); int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state); double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state); @@ -75,6 +76,9 @@ NSData *GPBCodedInputStreamReadRetainedBytes(GPBCodedInputStreamState *state) __attribute((ns_returns_retained)); NSData *GPBCodedInputStreamReadRetainedBytesNoCopy(GPBCodedInputStreamState *state) __attribute((ns_returns_retained)); +NSData *GPBCodedInputStreamReadRetainedBytesToEndGroupNoCopy(GPBCodedInputStreamState *state, + int32_t fieldNumber) + __attribute((ns_returns_retained)); size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state, size_t byteLimit); void GPBCodedInputStreamPopLimit(GPBCodedInputStreamState *state, size_t oldLimit); diff --git a/objectivec/GPBDescriptor.m b/objectivec/GPBDescriptor.m index 6f2fa9b590..05d7f75e82 100644 --- a/objectivec/GPBDescriptor.m +++ b/objectivec/GPBDescriptor.m @@ -267,6 +267,10 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField fields:(NSArray *)fields storageSize:(uint32_t)storageSize wireFormat:(BOOL)wireFormat { +#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS) + // This is also checked by the generator. + NSAssert(!wireFormat || fields.count == 0, @"Internal error: MessageSets should not have fields"); +#endif if ((self = [super init])) { messageClass_ = messageClass; messageName_ = [messageName copy]; @@ -1139,8 +1143,18 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { GPBRuntimeMatchFailure(); } -#if defined(DEBUG) && DEBUG +#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS) NSAssert(usesClassRefs, @"Internal error: all extensions should have class refs"); + + // These are also checked by the generator. + if ((desc->options & GPBExtensionSetWireFormat) != 0) { + NSAssert(desc->dataType == GPBDataTypeMessage, + @"Internal error: If a MessageSet extension is set, the data type must be a message."); + NSAssert((desc->options & GPBExtensionRepeated) == 0, + @"Internal Error: MessageSet extension can't be repeated."); + // NOTE: Could also check that the exteneded class is a MessageSet, but that would force the + // ObjC runtime to start up that class and that isn't desirable here. + } #endif if ((self = [super init])) { diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m index b2c3ba77c4..652c791ef0 100644 --- a/objectivec/GPBMessage.m +++ b/objectivec/GPBMessage.m @@ -5,9 +5,9 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#import -#import "GPBMessage_PackagePrivate.h" +#import "GPBMessage.h" +#import #import #import #import @@ -20,11 +20,18 @@ #import "GPBDictionary_PackagePrivate.h" #import "GPBExtensionInternals.h" #import "GPBExtensionRegistry.h" +#import "GPBMessage_PackagePrivate.h" #import "GPBRootObject_PackagePrivate.h" +#import "GPBUnknownField.h" +#import "GPBUnknownFieldSet.h" #import "GPBUnknownFieldSet_PackagePrivate.h" #import "GPBUnknownFields_PackagePrivate.h" #import "GPBUtilities_PackagePrivate.h" +// TODO: Consider using on other functions to reduce bloat when +// some compiler optimizations are enabled. +#define GPB_NOINLINE __attribute__((noinline)) + // Returns a new instance that was automatically created by |autocreator| for // its field |field|. static GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass, GPBMessage *autocreator, @@ -64,7 +71,16 @@ NSString *const GPBMessageExceptionMessageTooLarge = @interface GPBMessage () { @package + // Only one of these two is ever set, GPBUnknownFieldSet is being deprecated and will + // eventually be removed, but because that api support mutation, once the property is + // fetch it must continue to be used so any mutations will be honored in future operations + // on the message. + // Read only operations that access these two/cause things to migration between them should + // be protected with an @synchronized(self) block (that way the code also doesn't have to + // worry about throws). + NSMutableData *unknownFieldData_; GPBUnknownFieldSet *unknownFields_; + NSMutableDictionary *extensionMap_; // Readonly access to autocreatedExtensionMap_ is protected via readOnlyLock_. NSMutableDictionary *autocreatedExtensionMap_; @@ -103,7 +119,6 @@ static id GetOrCreateMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *fiel static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field); static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap, NSZone *zone) __attribute__((ns_returns_retained)); -static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self); #if defined(DEBUG) && DEBUG static NSError *MessageError(NSInteger code, NSDictionary *userInfo) { @@ -133,6 +148,178 @@ static NSError *ErrorFromException(NSException *exception) { return error; } +// Helper to encode varints onto the mutable data, the max size need is 10 bytes. +GPB_NOINLINE +static uint8_t *EncodeVarintU64(uint64_t val, uint8_t *ptr) { + do { + uint8_t byte = val & 0x7fU; + val >>= 7; + if (val) byte |= 0x80U; + *(ptr++) = byte; + } while (val); + return ptr; +} + +// Helper to encode varints onto the mutable data, the max size need is 5 bytes. +GPB_NOINLINE +static uint8_t *EncodeVarintU32(uint32_t val, uint8_t *ptr) { + do { + uint8_t byte = val & 0x7fU; + val >>= 7; + if (val) byte |= 0x80U; + *(ptr++) = byte; + } while (val); + return ptr; +} + +// Helper to encode signed int32 values as varints onto the mutable data, the max size need is 10 +// bytes. +GPB_NOINLINE +static uint8_t *EncodeVarintS32(int32_t val, uint8_t *ptr) { + if (val >= 0) { + return EncodeVarintU32((uint32_t)val, ptr); + } else { + // Must sign-extend + int64_t extended = val; + return EncodeVarintU64((uint64_t)extended, ptr); + } +} + +GPB_NOINLINE +static void AddUnknownFieldVarint32(GPBMessage *self, uint32_t fieldNumber, int32_t value) { + if (self->unknownFields_) { + [self->unknownFields_ mergeVarintField:fieldNumber value:value]; + return; + } + uint8_t buf[20]; + uint8_t *ptr = buf; + ptr = EncodeVarintU32(GPBWireFormatMakeTag(fieldNumber, GPBWireFormatVarint), ptr); + ptr = EncodeVarintS32(value, ptr); + + if (self->unknownFieldData_ == nil) { + self->unknownFieldData_ = [[NSMutableData alloc] initWithCapacity:ptr - buf]; + GPBBecomeVisibleToAutocreator(self); + } + [self->unknownFieldData_ appendBytes:buf length:ptr - buf]; +} + +GPB_NOINLINE +static void AddUnknownFieldLengthDelimited(GPBMessage *self, uint32_t fieldNumber, NSData *value) { + if (self->unknownFields_) { + [self->unknownFields_ mergeLengthDelimited:fieldNumber value:value]; + return; + } + uint8_t buf[20]; + uint8_t *ptr = buf; + ptr = EncodeVarintU32(GPBWireFormatMakeTag(fieldNumber, GPBWireFormatLengthDelimited), ptr); + ptr = EncodeVarintU64((uint64_t)value.length, ptr); + + if (self->unknownFieldData_ == nil) { + self->unknownFieldData_ = [[NSMutableData alloc] initWithCapacity:(ptr - buf) + value.length]; + GPBBecomeVisibleToAutocreator(self); + } + [self->unknownFieldData_ appendBytes:buf length:ptr - buf]; + [self->unknownFieldData_ appendData:value]; +} + +GPB_NOINLINE +static void AddUnknownMessageSetEntry(GPBMessage *self, uint32_t typeId, NSData *value) { + if (self->unknownFields_) { + // Legacy Set does this odd storage for MessageSet. + [self->unknownFields_ mergeLengthDelimited:typeId value:value]; + return; + } + + uint8_t buf[60]; + uint8_t *ptr = buf; + ptr = EncodeVarintU32(GPBWireFormatMessageSetItemTag, ptr); + ptr = EncodeVarintU32(GPBWireFormatMessageSetTypeIdTag, ptr); + ptr = EncodeVarintU32(typeId, ptr); + ptr = EncodeVarintU32(GPBWireFormatMessageSetMessageTag, ptr); + ptr = EncodeVarintU64((uint64_t)value.length, ptr); + uint8_t *split = ptr; + + ptr = EncodeVarintU32(GPBWireFormatMessageSetItemEndTag, ptr); + uint8_t *end = ptr; + + if (self->unknownFieldData_ == nil) { + self->unknownFieldData_ = [[NSMutableData alloc] initWithCapacity:(end - buf) + value.length]; + GPBBecomeVisibleToAutocreator(self); + } + [self->unknownFieldData_ appendBytes:buf length:split - buf]; + [self->unknownFieldData_ appendData:value]; + [self->unknownFieldData_ appendBytes:split length:end - split]; +} + +GPB_NOINLINE +static void ParseUnknownField(GPBMessage *self, uint32_t tag, GPBCodedInputStream *input) { + if (self->unknownFields_) { + if (![self->unknownFields_ mergeFieldFrom:tag input:input]) { + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag, @"Unexpected end-group tag"); + } + return; + } + + uint8_t buf[20]; + uint8_t *ptr = buf; + ptr = EncodeVarintU32(tag, ptr); // All will need the tag + NSData *bytesToAppend = nil; + + GPBCodedInputStreamState *state = &input->state_; + + switch (GPBWireFormatGetTagWireType(tag)) { + case GPBWireFormatVarint: { + ptr = EncodeVarintU64(GPBCodedInputStreamReadUInt64(state), ptr); + break; + } + case GPBWireFormatFixed64: { + uint64_t value = GPBCodedInputStreamReadFixed64(state); + *(ptr++) = (uint8_t)(value) & 0xFF; + *(ptr++) = (uint8_t)(value >> 8) & 0xFF; + *(ptr++) = (uint8_t)(value >> 16) & 0xFF; + *(ptr++) = (uint8_t)(value >> 24) & 0xFF; + *(ptr++) = (uint8_t)(value >> 32) & 0xFF; + *(ptr++) = (uint8_t)(value >> 40) & 0xFF; + *(ptr++) = (uint8_t)(value >> 48) & 0xFF; + *(ptr++) = (uint8_t)(value >> 56) & 0xFF; + break; + } + case GPBWireFormatLengthDelimited: { + bytesToAppend = GPBCodedInputStreamReadRetainedBytes(state); + ptr = EncodeVarintU64((uint64_t)bytesToAppend.length, ptr); + break; + } + case GPBWireFormatStartGroup: { + bytesToAppend = GPBCodedInputStreamReadRetainedBytesToEndGroupNoCopy( + state, GPBWireFormatGetTagFieldNumber(tag)); + break; + } + case GPBWireFormatEndGroup: + GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag, @"Unexpected end-group tag"); + break; + case GPBWireFormatFixed32: { + uint32_t value = GPBCodedInputStreamReadFixed32(state); + *(ptr++) = (uint8_t)(value) & 0xFF; + *(ptr++) = (uint8_t)(value >> 8) & 0xFF; + *(ptr++) = (uint8_t)(value >> 16) & 0xFF; + *(ptr++) = (uint8_t)(value >> 24) & 0xFF; + break; + } + } + + if (self->unknownFieldData_ == nil) { + self->unknownFieldData_ = + [[NSMutableData alloc] initWithCapacity:(ptr - buf) + bytesToAppend.length]; + GPBBecomeVisibleToAutocreator(self); + } + + [self->unknownFieldData_ appendBytes:buf length:ptr - buf]; + if (bytesToAppend) { + [self->unknownFieldData_ appendData:bytesToAppend]; + [bytesToAppend release]; + } +} + static void CheckExtension(GPBMessage *self, GPBExtensionDescriptor *extension) { if (![self isKindOfClass:extension.containingMessageClass]) { [NSException raise:NSInvalidArgumentException @@ -729,8 +916,7 @@ static void DecodeSingleValueFromInputStream(GPBExtensionDescriptor *extension, if (!enumDescriptor.isClosed || enumDescriptor.enumVerifier(val)) { nsValue = [[NSNumber alloc] initWithInt:val]; } else { - GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(messageToGetExtension); - [unknownFields mergeVarintField:extension->description_->fieldNumber value:val]; + AddUnknownFieldVarint32(messageToGetExtension, extension->description_->fieldNumber, val); nsValue = nil; } break; @@ -742,14 +928,12 @@ static void DecodeSingleValueFromInputStream(GPBExtensionDescriptor *extension, message:targetMessage extensionRegistry:extensionRegistry]; } else { - // description->dataType == GPBDataTypeMessage - if (GPBExtensionIsWireFormat(description)) { - // For MessageSet fields the message length will have already been - // read. - [targetMessage mergeFromCodedInputStream:input extensionRegistry:extensionRegistry]; - } else { - [input readMessage:targetMessage extensionRegistry:extensionRegistry]; - } +// description->dataType == GPBDataTypeMessage +#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS) + NSCAssert(!GPBExtensionIsWireFormat(description), + @"Internal error: got a MessageSet extension when not expected."); +#endif + [input readMessage:targetMessage extensionRegistry:extensionRegistry]; } // Nothing to add below since the caller provided the message (and added it). nsValue = nil; @@ -922,12 +1106,50 @@ void GPBClearMessageAutocreator(GPBMessage *self) { self->autocreatorExtension_ = nil; } -static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { - if (!self->unknownFields_) { - self->unknownFields_ = [[GPBUnknownFieldSet alloc] init]; - GPBBecomeVisibleToAutocreator(self); +GPB_NOINLINE +static void MergeUnknownFieldDataIntoFieldSet(GPBMessage *self, NSData *data, + GPBUnknownFieldSet *targetSet) { + GPBUnknownFieldSet *unknownFields = targetSet ? targetSet : self->unknownFields_; + +#if defined(DEBUG) && DEBUG + NSCAssert(unknownFields != nil, @"Internal error: unknown fields not initialized."); +#endif + + BOOL isMessageSet = self.descriptor.isWireFormat; + GPBUnknownFieldSet *decodeInto = isMessageSet ? [[GPBUnknownFieldSet alloc] init] : unknownFields; + + GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data]; + @try { + [decodeInto mergeFromCodedInputStream:input]; + } @catch (NSException *exception) { +#if defined(DEBUG) && DEBUG + NSLog(@"%@: Internal exception while parsing the unknown fields into a Set: %@", [self class], + exception); +#endif + } + [input release]; + + if (isMessageSet) { + // Need to transform the groups back into how Message feeds the data into a MessageSet when + // doing a full MessageSet based decode. + GPBUnknownField *groupField = [decodeInto getField:GPBWireFormatMessageSetItem]; + for (GPBUnknownFieldSet *group in groupField.groupList) { + GPBUnknownField *typeIdField = [group getField:GPBWireFormatMessageSetTypeId]; + GPBUnknownField *messageField = [group getField:GPBWireFormatMessageSetMessage]; + if (typeIdField.varintList.count != 1 || messageField.lengthDelimitedList.count != 1) { +#if defined(DEBUG) && DEBUG + NSCAssert(NO, @"Internal error: MessageSet group missing typeId or message."); +#endif + continue; + } + int32_t fieldNumber = (int32_t)[typeIdField.varintList valueAtIndex:0]; + GPBUnknownField *messageSetField = [[GPBUnknownField alloc] initWithNumber:fieldNumber]; + [messageSetField addLengthDelimited:messageField.lengthDelimitedList[0]]; + [unknownFields addField:messageSetField]; + [messageSetField release]; + } + [decodeInto release]; } - return self->unknownFields_; } @implementation GPBMessage @@ -1027,7 +1249,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { error:(NSError **)errorPtr { if ((self = [self init])) { @try { - [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry]; + [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry endingTag:0]; if (errorPtr) { *errorPtr = nil; } @@ -1164,8 +1386,12 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { GPBMessage *result = [[descriptor.messageClass allocWithZone:zone] init]; [self copyFieldsInto:result zone:zone descriptor:descriptor]; - // Make immutable copies of the extra bits. - result->unknownFields_ = [unknownFields_ copyWithZone:zone]; + + @synchronized(self) { + result->unknownFields_ = [unknownFields_ copyWithZone:zone]; + result->unknownFieldData_ = [unknownFieldData_ mutableCopyWithZone:zone]; + } + result->extensionMap_ = CloneExtensionMap(extensionMap_, zone); return result; } @@ -1239,6 +1465,8 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { [extensionMap_ release]; extensionMap_ = nil; + [unknownFieldData_ release]; + unknownFieldData_ = nil; [unknownFields_ release]; unknownFields_ = nil; @@ -1442,11 +1670,20 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { sortedExtensions:sortedExtensions]; } } - if (descriptor.isWireFormat) { - [unknownFields_ writeAsMessageSetTo:output]; - } else { - [unknownFields_ writeToCodedOutputStream:output]; - } + @synchronized(self) { + if (unknownFieldData_) { +#if defined(DEBUG) && DEBUG + NSAssert(unknownFields_ == nil, @"Internal error both unknown states were set"); +#endif + [output writeRawData:unknownFieldData_]; + } else { + if (descriptor.isWireFormat) { + [unknownFields_ writeAsMessageSetTo:output]; + } else { + [unknownFields_ writeToCodedOutputStream:output]; + } + } + } // @synchronized(self) } - (void)writeDelimitedToOutputStream:(NSOutputStream *)output { @@ -2078,8 +2315,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { - (void)mergeFromData:(NSData *)data extensionRegistry:(id)extensionRegistry { GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data]; @try { - [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry]; - [input checkLastTagWas:0]; + [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry endingTag:0]; } @finally { [input release]; } @@ -2088,9 +2324,10 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { - (BOOL)mergeFromData:(NSData *)data extensionRegistry:(nullable id)extensionRegistry error:(NSError **)errorPtr { + GPBBecomeVisibleToAutocreator(self); GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data]; @try { - [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry]; + [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry endingTag:0]; [input checkLastTagWas:0]; if (errorPtr) { *errorPtr = nil; @@ -2167,11 +2404,25 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { #pragma mark - Unknown Field Support - (GPBUnknownFieldSet *)unknownFields { - return unknownFields_; + @synchronized(self) { + if (unknownFieldData_) { +#if defined(DEBUG) && DEBUG + NSAssert(unknownFields_ == nil, @"Internal error both unknown states were set"); +#endif + unknownFields_ = [[GPBUnknownFieldSet alloc] init]; + MergeUnknownFieldDataIntoFieldSet(self, unknownFieldData_, nil); + [unknownFieldData_ release]; + unknownFieldData_ = nil; + } + return unknownFields_; + } // @synchronized(self) } - (void)setUnknownFields:(GPBUnknownFieldSet *)unknownFields { - if (unknownFields != unknownFields_) { + if (unknownFields != unknownFields_ || unknownFieldData_ != nil) { + // Changing sets or clearing. + [unknownFieldData_ release]; + unknownFieldData_ = nil; [unknownFields_ release]; unknownFields_ = [unknownFields copy]; GPBBecomeVisibleToAutocreator(self); @@ -2182,89 +2433,90 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { extensionRegistry:(id)extensionRegistry { uint32_t typeId = 0; NSData *rawBytes = nil; - GPBExtensionDescriptor *extension = nil; GPBCodedInputStreamState *state = &input->state_; + BOOL gotType = NO; + BOOL gotBytes = NO; while (true) { uint32_t tag = GPBCodedInputStreamReadTag(state); - if (tag == 0) { + if (tag == GPBWireFormatMessageSetItemEndTag || tag == 0) { break; } if (tag == GPBWireFormatMessageSetTypeIdTag) { - typeId = GPBCodedInputStreamReadUInt32(state); - if (typeId != 0) { - extension = [extensionRegistry extensionForDescriptor:[self descriptor] fieldNumber:typeId]; + uint32_t tmp = GPBCodedInputStreamReadUInt32(state); + // Spec says only use the first value. + if (!gotType) { + gotType = YES; + typeId = tmp; } } else if (tag == GPBWireFormatMessageSetMessageTag) { - rawBytes = [GPBCodedInputStreamReadRetainedBytesNoCopy(state) autorelease]; + if (gotBytes) { + // Skip over the payload instead of collecting it. + [input skipField:tag]; + } else { + rawBytes = [GPBCodedInputStreamReadRetainedBytesNoCopy(state) autorelease]; + gotBytes = YES; + } } else { + // Don't capture unknowns within the message set impl group. if (![input skipField:tag]) { break; } } } - [input checkLastTagWas:GPBWireFormatMessageSetItemEndTag]; + // If we get here because of end of input (tag zero) or the wrong end tag (within the skipField:), + // this will error. + GPBCodedInputStreamCheckLastTagWas(state, GPBWireFormatMessageSetItemEndTag); - if (rawBytes != nil && typeId != 0) { - if (extension != nil) { - GPBCodedInputStream *newInput = [[GPBCodedInputStream alloc] initWithData:rawBytes]; - @try { - ExtensionMergeFromInputStream(extension, extension.packable, newInput, extensionRegistry, - self); - } @finally { - [newInput release]; - } - } else { - GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); - // rawBytes was created via a NoCopy, so it can be reusing a - // subrange of another NSData that might go out of scope as things - // unwind, so a copy is needed to ensure what is saved in the - // unknown fields stays valid. - NSData *cloned = [NSData dataWithData:rawBytes]; - [unknownFields mergeMessageSetMessage:typeId data:cloned]; - } + if (!gotType || !gotBytes) { + // upb_Decoder_DecodeMessageSetItem does't keep this partial as an unknown field, it just drops + // it, so do the same thing. + return; } -} - -- (BOOL)parseUnknownField:(GPBCodedInputStream *)input - extensionRegistry:(id)extensionRegistry - tag:(uint32_t)tag { - GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag); - int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag); - GPBDescriptor *descriptor = [self descriptor]; - GPBExtensionDescriptor *extension = [extensionRegistry extensionForDescriptor:descriptor - fieldNumber:fieldNumber]; - if (extension == nil) { - if (descriptor.wireFormat && GPBWireFormatMessageSetItemTag == tag) { - [self parseMessageSet:input extensionRegistry:extensionRegistry]; - return YES; - } - } else { - if (extension.wireType == wireType) { - ExtensionMergeFromInputStream(extension, extension.packable, input, extensionRegistry, self); - return YES; - } - // Primitive, repeated types can be packed on unpacked on the wire, and are - // parsed either way. - if ([extension isRepeated] && !GPBDataTypeIsObject(extension->description_->dataType) && - (extension.alternateWireType == wireType)) { - ExtensionMergeFromInputStream(extension, !extension.packable, input, extensionRegistry, self); - return YES; + GPBExtensionDescriptor *extension = [extensionRegistry extensionForDescriptor:[self descriptor] + fieldNumber:typeId]; + if (extension) { +#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS) + NSAssert(extension.dataType == GPBDataTypeMessage, + @"Internal Error: MessageSet extension must be a message field."); + NSAssert(GPBExtensionIsWireFormat(extension->description_), + @"Internal Error: MessageSet extension must have message_set_wire_format set."); + NSAssert(!GPBExtensionIsRepeated(extension->description_), + @"Internal Error: MessageSet extension can't be repeated."); +#endif + // Look up the existing one to merge to or create a new one. + GPBMessage *targetMessage = [self getExistingExtension:extension]; + if (!targetMessage) { + GPBDescriptor *descriptor = [extension.msgClass descriptor]; + targetMessage = [[descriptor.messageClass alloc] init]; + [self setExtension:extension value:targetMessage]; + [targetMessage release]; + } + GPBCodedInputStream *newInput = [[GPBCodedInputStream alloc] initWithData:rawBytes]; + @try { + [targetMessage mergeFromCodedInputStream:newInput + extensionRegistry:extensionRegistry + endingTag:0]; + } @finally { + [newInput release]; } - } - if ([GPBUnknownFieldSet isFieldTag:tag]) { - GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); - return [unknownFields mergeFieldFrom:tag input:input]; } else { - return NO; + // The extension isn't in the registry, but it was well formed, so the whole group structure + // get preserved as an unknown field. + + // rawBytes was created via a NoCopy, so it can be reusing a + // subrange of another NSData that might go out of scope as things + // unwind, so a copy is needed to ensure what is saved in the + // unknown fields stays valid. + NSData *cloned = [NSData dataWithData:rawBytes]; + AddUnknownMessageSetEntry(self, typeId, cloned); } } - (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data { - GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); - [unknownFields addUnknownMapEntry:fieldNum value:data]; + AddUnknownFieldLengthDelimited(self, fieldNum, data); } #pragma mark - MergeFromCodedInputStream Support @@ -2337,8 +2589,7 @@ static void MergeSingleFieldFromCodedInputStream(GPBMessage *self, GPBFieldDescr if (!GPBFieldIsClosedEnum(field) || [field isValidEnumValue:val]) { GPBSetInt32IvarWithFieldPrivate(self, field, val); } else { - GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); - [unknownFields mergeVarintField:GPBFieldNumber(field) value:val]; + AddUnknownFieldVarint32(self, GPBFieldNumber(field), val); } } } // switch @@ -2387,8 +2638,7 @@ static void MergeRepeatedPackedFieldFromCodedInputStream(GPBMessage *self, if (!GPBFieldIsClosedEnum(field) || [field isValidEnumValue:val]) { [(GPBEnumArray *)genericArray addRawValue:val]; } else { - GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); - [unknownFields mergeVarintField:GPBFieldNumber(field) value:val]; + AddUnknownFieldVarint32(self, GPBFieldNumber(field), val); } break; } @@ -2456,8 +2706,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( if (!GPBFieldIsClosedEnum(field) || [field isValidEnumValue:val]) { [(GPBEnumArray *)genericArray addRawValue:val]; } else { - GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); - [unknownFields mergeVarintField:GPBFieldNumber(field) value:val]; + AddUnknownFieldVarint32(self, GPBFieldNumber(field), val); } break; } @@ -2465,18 +2714,27 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( } - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input - extensionRegistry:(id)extensionRegistry { + extensionRegistry:(id)extensionRegistry + endingTag:(uint32_t)endingTag { +#if defined(DEBUG) && DEBUG + NSAssert(endingTag == 0 || GPBWireFormatGetTagWireType(endingTag) == GPBWireFormatEndGroup, + @"endingTag should have been an endGroup tag"); +#endif // DEBUG GPBDescriptor *descriptor = [self descriptor]; GPBCodedInputStreamState *state = &input->state_; uint32_t tag = 0; NSUInteger startingIndex = 0; NSArray *fields = descriptor->fields_; + BOOL isMessageSetWireFormat = descriptor.isWireFormat; NSUInteger numFields = fields.count; while (YES) { BOOL merged = NO; tag = GPBCodedInputStreamReadTag(state); - if (tag == 0) { - break; // Reached end. + if (tag == endingTag || tag == 0) { + // If we got to the end (tag zero), when we were expecting the end group, this will + // raise the error. + GPBCodedInputStreamCheckLastTagWas(state, endingTag); + return; } for (NSUInteger i = 0; i < numFields; ++i) { if (startingIndex >= numFields) startingIndex = 0; @@ -2514,46 +2772,66 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( } } // for(i < numFields) - if (!merged && (tag != 0)) { - // Primitive, repeated types can be packed on unpacked on the wire, and - // are parsed either way. The above loop covered tag in the preferred - // for, so this need to check the alternate form. - for (NSUInteger i = 0; i < numFields; ++i) { - if (startingIndex >= numFields) startingIndex = 0; - GPBFieldDescriptor *fieldDescriptor = fields[startingIndex]; - if ((fieldDescriptor.fieldType == GPBFieldTypeRepeated) && - !GPBFieldDataTypeIsObject(fieldDescriptor) && - (GPBFieldAlternateTag(fieldDescriptor) == tag)) { - BOOL alternateIsPacked = !fieldDescriptor.isPackable; - if (alternateIsPacked) { - MergeRepeatedPackedFieldFromCodedInputStream(self, fieldDescriptor, input); - // Well formed protos will only have a repeated field that is - // packed once, advance the starting index to the next field. - startingIndex += 1; - } else { - MergeRepeatedNotPackedFieldFromCodedInputStream(self, fieldDescriptor, input, - extensionRegistry); - } - merged = YES; - break; - } else { + if (merged) continue; // On to the next tag + + // Primitive, repeated types can be packed or unpacked on the wire, and + // are parsed either way. The above loop covered tag in the preferred + // for, so this need to check the alternate form. + for (NSUInteger i = 0; i < numFields; ++i) { + if (startingIndex >= numFields) startingIndex = 0; + GPBFieldDescriptor *fieldDescriptor = fields[startingIndex]; + if ((fieldDescriptor.fieldType == GPBFieldTypeRepeated) && + !GPBFieldDataTypeIsObject(fieldDescriptor) && + (GPBFieldAlternateTag(fieldDescriptor) == tag)) { + BOOL alternateIsPacked = !fieldDescriptor.isPackable; + if (alternateIsPacked) { + MergeRepeatedPackedFieldFromCodedInputStream(self, fieldDescriptor, input); + // Well formed protos will only have a repeated field that is + // packed once, advance the starting index to the next field. startingIndex += 1; + } else { + MergeRepeatedNotPackedFieldFromCodedInputStream(self, fieldDescriptor, input, + extensionRegistry); } + merged = YES; + break; + } else { + startingIndex += 1; } } - if (!merged) { - if (tag == 0) { - // zero signals EOF / limit reached - return; - } else { - if (![self parseUnknownField:input extensionRegistry:extensionRegistry tag:tag]) { - // it's an endgroup tag - return; + if (merged) continue; // On to the next tag + + if (isMessageSetWireFormat) { + if (GPBWireFormatMessageSetItemTag == tag) { + [self parseMessageSet:input extensionRegistry:extensionRegistry]; + continue; // On to the next tag + } + } else { + // ObjC Runtime currently doesn't track if a message supported extensions, so the check is + // always done. + GPBExtensionDescriptor *extension = + [extensionRegistry extensionForDescriptor:descriptor + fieldNumber:GPBWireFormatGetTagFieldNumber(tag)]; + if (extension) { + GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag); + if (extension.wireType == wireType) { + ExtensionMergeFromInputStream(extension, extension.packable, input, extensionRegistry, + self); + continue; // On to the next tag + } + // Primitive, repeated types can be packed on unpacked on the wire, and are + // parsed either way. + if ([extension isRepeated] && !GPBDataTypeIsObject(extension->description_->dataType) && + (extension.alternateWireType == wireType)) { + ExtensionMergeFromInputStream(extension, !extension.packable, input, extensionRegistry, + self); + continue; // On to the next tag } } - } // if(!merged) + } + ParseUnknownField(self, tag, input); } // while(YES) } @@ -2675,10 +2953,29 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( } // for(fields) // Unknown fields. - if (!unknownFields_) { - [self setUnknownFields:other.unknownFields]; + if (unknownFields_) { +#if defined(DEBUG) && DEBUG + NSAssert(unknownFieldData_ == nil, @"Internal error both unknown states were set"); +#endif + @synchronized(other) { + if (other->unknownFields_) { +#if defined(DEBUG) && DEBUG + NSAssert(other->unknownFieldData_ == nil, @"Internal error both unknown states were set"); +#endif + [unknownFields_ mergeUnknownFields:other->unknownFields_]; + } else if (other->unknownFieldData_) { + MergeUnknownFieldDataIntoFieldSet(self, other->unknownFieldData_, nil); + } + } // @synchronized(other) } else { - [unknownFields_ mergeUnknownFields:other.unknownFields]; + NSData *otherData = GPBMessageUnknownFieldsData(other); + if (otherData) { + if (unknownFieldData_) { + [unknownFieldData_ appendData:otherData]; + } else { + unknownFieldData_ = [otherData mutableCopy]; + } + } } // Extensions @@ -2850,15 +3147,72 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( } } - // nil and empty are equal - GPBUnknownFieldSet *otherUnknowns = otherMsg->unknownFields_; - if ([unknownFields_ countOfFields] != 0 || [otherUnknowns countOfFields] != 0) { - if (![unknownFields_ isEqual:otherUnknowns]) { - return NO; + // Mutation while another thread is doing read only access is invalid, so the only thing we + // need to guard against is concurrent r/o access, so we can grab the values (and retain them) + // so we have a version to compare against safely incase the second access causes the transform + // between internal states. + GPBUnknownFieldSet *selfUnknownFields; + NSData *selfUnknownFieldData; + @synchronized(self) { + selfUnknownFields = [unknownFields_ retain]; + selfUnknownFieldData = [unknownFieldData_ retain]; + } + GPBUnknownFieldSet *otherUnknownFields; + NSData *otherUnknownFieldData; + @synchronized(otherMsg) { + otherUnknownFields = [otherMsg->unknownFields_ retain]; + otherUnknownFieldData = [otherMsg->unknownFieldData_ retain]; + } +#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS) + if (selfUnknownFields) { + NSAssert(selfUnknownFieldData == nil, @"Internal error both unknown states were set"); + } + if (otherUnknownFields) { + NSAssert(otherUnknownFieldData == nil, @"Internal error both unknown states were set"); + } +#endif + // Since a developer can set the legacy unknownFieldSet, treat nil and empty as the same. + if (selfUnknownFields && selfUnknownFields.countOfFields == 0) { + [selfUnknownFields release]; + selfUnknownFields = nil; + } + if (otherUnknownFields && otherUnknownFields.countOfFields == 0) { + [otherUnknownFields release]; + otherUnknownFields = nil; + } + + BOOL result = YES; + + if (selfUnknownFieldData && otherUnknownFieldData) { + // Both had data, compare it. + result = [selfUnknownFieldData isEqual:otherUnknownFieldData]; + } else if (selfUnknownFields && otherUnknownFields) { + // Both had fields set, compare them. + result = [selfUnknownFields isEqual:otherUnknownFields]; + } else { + // At this point, we're done to one have a set/nothing, and the other having data/nothing. + GPBUnknownFieldSet *theSet = selfUnknownFields ? selfUnknownFields : otherUnknownFields; + NSData *theData = selfUnknownFieldData ? selfUnknownFieldData : otherUnknownFieldData; + if (theSet) { + if (theData) { + GPBUnknownFieldSet *tempSet = [[GPBUnknownFieldSet alloc] init]; + MergeUnknownFieldDataIntoFieldSet(self, theData, tempSet); + result = [tempSet isEqual:theSet]; + [tempSet release]; + } else { + result = NO; + } + } else { + // It was a data/nothing and nothing, so they equal if the other didn't have data. + result = theData == nil; } } - return YES; + [selfUnknownFields release]; + [selfUnknownFieldData release]; + [otherUnknownFields release]; + [otherUnknownFieldData release]; + return result; } // It is very difficult to implement a generic hash for ProtoBuf messages that @@ -3121,11 +3475,20 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( } // for(fields) // Add any unknown fields. - if (descriptor.wireFormat) { - result += [unknownFields_ serializedSizeAsMessageSet]; - } else { - result += [unknownFields_ serializedSize]; - } + @synchronized(self) { + if (unknownFieldData_) { +#if defined(DEBUG) && DEBUG + NSAssert(unknownFields_ == nil, @"Internal error both unknown states were set"); +#endif + result += [unknownFieldData_ length]; + } else { + if (descriptor.wireFormat) { + result += [unknownFields_ serializedSizeAsMessageSet]; + } else { + result += [unknownFields_ serializedSize]; + } + } + } // @synchronized(self) // Add any extensions. for (GPBExtensionDescriptor *extension in extensionMap_) { @@ -3554,8 +3917,7 @@ GPB_INLINE BOOL GPBIsCaseOfSelForOneOf(const char *selName, size_t selNameLength if (data.length) { GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data]; @try { - [self mergeFromCodedInputStream:input extensionRegistry:nil]; - [input checkLastTagWas:0]; + [self mergeFromCodedInputStream:input extensionRegistry:nil endingTag:0]; } @finally { [input release]; } @@ -3652,4 +4014,32 @@ id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) { return expected; } +NSData *GPBMessageUnknownFieldsData(GPBMessage *self) { + NSData *result = nil; + @synchronized(self) { + GPBUnknownFieldSet *unknownFields = self->unknownFields_; + if (unknownFields) { +#if defined(DEBUG) && DEBUG + NSCAssert(self->unknownFieldData_ == nil, @"Internal error both unknown states were set"); +#endif + if (self.descriptor.isWireFormat) { + NSMutableData *mutableData = + [NSMutableData dataWithLength:unknownFields.serializedSizeAsMessageSet]; + GPBCodedOutputStream *output = [[GPBCodedOutputStream alloc] initWithData:mutableData]; + [unknownFields writeAsMessageSetTo:output]; + [output flush]; + [output release]; + result = mutableData; + } else { + result = [unknownFields data]; + } + } else { + // Internally we can borrow it without a copy since this is immediately used by callers + // and multithreaded access with any mutation is not allow on messages. + result = self->unknownFieldData_; + } + } // @synchronized(self) + return result; +} + #pragma clang diagnostic pop diff --git a/objectivec/GPBMessage_PackagePrivate.h b/objectivec/GPBMessage_PackagePrivate.h index e562e5dabb..58649c44f7 100644 --- a/objectivec/GPBMessage_PackagePrivate.h +++ b/objectivec/GPBMessage_PackagePrivate.h @@ -47,15 +47,15 @@ typedef struct GPBMessage_Storage *GPBMessage_StoragePtr; // Parses a message of this type from the input and merges it with this // message. // +// `endingTag` should be zero if expected to consume to the end of input, but if +// the input is supposed to be a Group, it should be the endgroup tag to look for. +// // Warning: This does not verify that all required fields are present in // the input message. -// Note: The caller should call -// -[CodedInputStream checkLastTagWas:] after calling this to -// verify that the last tag seen was the appropriate end-group tag, -// or zero for EOF. // NOTE: This will throw if there is an error while parsing. - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input - extensionRegistry:(id)extensionRegistry; + extensionRegistry:(id)extensionRegistry + endingTag:(uint32_t)endingTag; - (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data; @@ -81,4 +81,7 @@ void GPBAutocreatedDictionaryModified(GPBMessage *self, id dictionary); // autocreated reference to this message. void GPBClearMessageAutocreator(GPBMessage *self); +// The data (or null) from the unknown fields of a message; +NSData *GPBMessageUnknownFieldsData(GPBMessage *self); + CF_EXTERN_C_END diff --git a/objectivec/GPBUnknownFieldSet.m b/objectivec/GPBUnknownFieldSet.m index 768734070d..6ec70b8925 100644 --- a/objectivec/GPBUnknownFieldSet.m +++ b/objectivec/GPBUnknownFieldSet.m @@ -154,7 +154,10 @@ static void CopyWorker(__unused const void *key, const void *value, void *contex - (NSString *)description { NSMutableString *description = [NSMutableString stringWithFormat:@"<%@ %p>: TextFormat: {\n", [self class], self]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSString *textFormat = GPBTextFormatForUnknownFieldSet(self, @" "); +#pragma clang diagnostic pop [description appendString:textFormat]; [description appendString:@"}"]; return description; @@ -212,10 +215,6 @@ static void GPBUnknownFieldSetSerializedSizeAsMessageSet(__unused const void *ke return data; } -+ (BOOL)isFieldTag:(int32_t)tag { - return GPBWireFormatGetTagWireType(tag) != GPBWireFormatEndGroup; -} - - (void)addField:(GPBUnknownField *)field { int32_t number = [field number]; checkNumber(number); @@ -272,6 +271,11 @@ static void GPBUnknownFieldSetMergeUnknownFields(__unused const void *key, const [[self mutableFieldForNumber:number create:YES] addVarint:value]; } +- (void)mergeLengthDelimited:(int32_t)fieldNum value:(NSData *)value { + checkNumber(fieldNum); + [[self mutableFieldForNumber:fieldNum create:YES] addLengthDelimited:value]; +} + - (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input { NSAssert(GPBWireFormatIsValidTag(tag), @"Got passed an invalid tag"); int32_t number = GPBWireFormatGetTagFieldNumber(tag); @@ -314,15 +318,6 @@ static void GPBUnknownFieldSetMergeUnknownFields(__unused const void *key, const } } -- (void)mergeMessageSetMessage:(int32_t)number data:(NSData *)messageData { - [[self mutableFieldForNumber:number create:YES] addLengthDelimited:messageData]; -} - -- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data { - GPBUnknownField *field = [self mutableFieldForNumber:fieldNum create:YES]; - [field addLengthDelimited:data]; -} - - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input { while (YES) { int32_t tag = GPBCodedInputStreamReadTag(&input->state_); diff --git a/objectivec/GPBUnknownFieldSet_PackagePrivate.h b/objectivec/GPBUnknownFieldSet_PackagePrivate.h index 37cb61b3b7..1d5ff50f45 100644 --- a/objectivec/GPBUnknownFieldSet_PackagePrivate.h +++ b/objectivec/GPBUnknownFieldSet_PackagePrivate.h @@ -14,8 +14,6 @@ @interface GPBUnknownFieldSet () -+ (BOOL)isFieldTag:(int32_t)tag; - - (NSData *)data; - (size_t)serializedSize; @@ -29,9 +27,7 @@ - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input; - (void)mergeVarintField:(int32_t)number value:(int32_t)value; +- (void)mergeLengthDelimited:(int32_t)number value:(NSData *)value; - (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input; -- (void)mergeMessageSetMessage:(int32_t)number data:(NSData *)messageData; - -- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data; @end diff --git a/objectivec/GPBUnknownFields.m b/objectivec/GPBUnknownFields.m index e24e07b416..836e249f67 100644 --- a/objectivec/GPBUnknownFields.m +++ b/objectivec/GPBUnknownFields.m @@ -12,7 +12,9 @@ #import "GPBCodedInputStream_PackagePrivate.h" #import "GPBCodedOutputStream.h" #import "GPBCodedOutputStream_PackagePrivate.h" +#import "GPBDescriptor.h" #import "GPBMessage.h" +#import "GPBMessage_PackagePrivate.h" #import "GPBUnknownField.h" #import "GPBUnknownFieldSet_PackagePrivate.h" #import "GPBUnknownField_PackagePrivate.h" @@ -195,12 +197,9 @@ static BOOL MergeFromInputStream(GPBUnknownFields *self, GPBCodedInputStream *in self = [super init]; if (self) { fields_ = [[NSMutableArray alloc] init]; - // TODO: b/349146447 - Move off the legacy class and directly to the data once Message is - // updated. - GPBUnknownFieldSet *legacyUnknownFields = [message unknownFields]; - if (legacyUnknownFields) { - GPBCodedInputStream *input = - [[GPBCodedInputStream alloc] initWithData:[legacyUnknownFields data]]; + NSData *data = GPBMessageUnknownFieldsData(message); + if (data) { + GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data]; // Parse until the end of the data (tag will be zero). if (!MergeFromInputStream(self, input, 0)) { [input release]; diff --git a/objectivec/GPBUtilities.h b/objectivec/GPBUtilities.h index 9673aa4261..578545df05 100644 --- a/objectivec/GPBUtilities.h +++ b/objectivec/GPBUtilities.h @@ -40,7 +40,9 @@ NSString *GPBTextFormatForMessage(GPBMessage *message, NSString *__nullable line * @return An NSString with the TextFormat of the unknown field set. **/ NSString *GPBTextFormatForUnknownFieldSet(GPBUnknownFieldSet *__nullable unknownSet, - NSString *__nullable lineIndent); + NSString *__nullable lineIndent) + __attribute__((deprecated("Use GPBTextFormatForMessage will include the unknown fields, and " + "GPBUnknownFieldSet it going away."))); /** * Checks if the given field number is set on a message. diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m index ff309860cd..0764c96910 100644 --- a/objectivec/GPBUtilities.m +++ b/objectivec/GPBUtilities.m @@ -5,7 +5,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#import "GPBUtilities_PackagePrivate.h" +#import "GPBUtilities.h" #import @@ -15,6 +15,9 @@ #import "GPBMessage_PackagePrivate.h" #import "GPBUnknownField.h" #import "GPBUnknownFieldSet.h" +#import "GPBUnknownField_PackagePrivate.h" +#import "GPBUnknownFields.h" +#import "GPBUtilities_PackagePrivate.h" // Direct access is use for speed, to avoid even internally declaring things // read/write, etc. The warning is enabled in the project to ensure code calling @@ -1929,6 +1932,78 @@ static void AppendTextFormatForMessageExtensionRange(GPBMessage *message, NSArra } // for..in(activeExtensions) } +static void AppendTextFormatForUnknownFields(GPBUnknownFields *ufs, NSMutableString *toStr, + NSString *lineIndent) { +#if defined(DEBUG) && DEBUG + NSCAssert(!ufs.empty, @"Internal Error: No unknown fields to format."); +#endif + // Extract the fields and sort them by field number. Use a stable sort and sort just by the field + // numbers, that way the output will still show the order the different types were added as well + // as maintaining the order within a give number/type pair (i.e. - repeated fields say in order). + NSMutableArray *sortedFields = [[NSMutableArray alloc] initWithCapacity:ufs.count]; + for (GPBUnknownField *field in ufs) { + [sortedFields addObject:field]; + } + [sortedFields + sortWithOptions:NSSortStable + usingComparator:^NSComparisonResult(GPBUnknownField *field1, GPBUnknownField *field2) { + int32_t fieldNumber1 = field1->number_; + int32_t fieldNumber2 = field2->number_; + if (fieldNumber1 < fieldNumber2) { + return NSOrderedAscending; + } else if (fieldNumber1 > fieldNumber2) { + return NSOrderedDescending; + } else { + return NSOrderedSame; + } + }]; + + NSString *subIndent = nil; + + for (GPBUnknownField *field in sortedFields) { + int32_t fieldNumber = field->number_; + switch (field->type_) { + case GPBUnknownFieldTypeVarint: + [toStr appendFormat:@"%@%d: %llu\n", lineIndent, fieldNumber, field->storage_.intValue]; + break; + case GPBUnknownFieldTypeFixed32: + [toStr appendFormat:@"%@%d: 0x%X\n", lineIndent, fieldNumber, + (uint32_t)field->storage_.intValue]; + break; + case GPBUnknownFieldTypeFixed64: + [toStr appendFormat:@"%@%d: 0x%llX\n", lineIndent, fieldNumber, field->storage_.intValue]; + break; + case GPBUnknownFieldTypeLengthDelimited: + [toStr appendFormat:@"%@%d: ", lineIndent, fieldNumber]; + AppendBufferAsString(field->storage_.lengthDelimited, toStr); + [toStr appendString:@"\n"]; + break; + case GPBUnknownFieldTypeGroup: { + GPBUnknownFields *group = field->storage_.group; + if (group.empty) { + [toStr appendFormat:@"%@%d: {}\n", lineIndent, fieldNumber]; + } else { + [toStr appendFormat:@"%@%d: {\n", lineIndent, fieldNumber]; + if (subIndent == nil) { + subIndent = [lineIndent stringByAppendingString:@" "]; + } + AppendTextFormatForUnknownFields(group, toStr, subIndent); + [toStr appendFormat:@"%@}\n", lineIndent]; + } + } break; + case GPBUnknownFieldTypeLegacy: +#if defined(DEBUG) && DEBUG + NSCAssert( + NO, + @"Internal error: Shouldn't have gotten a legacy field type in the unknown fields."); +#endif + break; + } + } + [subIndent release]; + [sortedFields release]; +} + static void AppendTextFormatForMessage(GPBMessage *message, NSMutableString *toStr, NSString *lineIndent) { GPBDescriptor *descriptor = [message descriptor]; @@ -1951,11 +2026,12 @@ static void AppendTextFormatForMessage(GPBMessage *message, NSMutableString *toS } } - NSString *unknownFieldsStr = GPBTextFormatForUnknownFieldSet(message.unknownFields, lineIndent); - if ([unknownFieldsStr length] > 0) { + GPBUnknownFields *ufs = [[GPBUnknownFields alloc] initFromMessage:message]; + if (ufs.count > 0) { [toStr appendFormat:@"%@# --- Unknown fields ---\n", lineIndent]; - [toStr appendString:unknownFieldsStr]; + AppendTextFormatForUnknownFields(ufs, toStr, lineIndent); } + [ufs release]; } NSString *GPBTextFormatForMessage(GPBMessage *message, NSString *lineIndent) { diff --git a/objectivec/Tests/GPBCodedInputStreamTests.m b/objectivec/Tests/GPBCodedInputStreamTests.m index 3dae80d2ac..29802eb15a 100644 --- a/objectivec/Tests/GPBCodedInputStreamTests.m +++ b/objectivec/Tests/GPBCodedInputStreamTests.m @@ -6,12 +6,16 @@ // https://developers.google.com/open-source/licenses/bsd #import -#import "GPBTestUtilities.h" #import "GPBCodedInputStream.h" +#import "GPBCodedInputStream_PackagePrivate.h" #import "GPBCodedOutputStream.h" +#import "GPBTestUtilities.h" +#import "GPBUnknownField.h" #import "GPBUnknownFieldSet_PackagePrivate.h" +#import "GPBUnknownFields.h" #import "GPBUtilities_PackagePrivate.h" +#import "GPBWireFormat.h" #import "objectivec/Tests/Unittest.pbobjc.h" @interface CodedInputStreamTests : GPBTestCase @@ -266,20 +270,55 @@ TestAllTypes* message = [self allSetRepeatedCount:kGPBDefaultRepeatCount]; NSData* rawBytes = message.data; - // Create two parallel inputs. Parse one as unknown fields while using - // skipField() to skip each field on the other. Expect the same tags. + TestEmptyMessage* empty = [TestEmptyMessage parseFromData:rawBytes error:NULL]; + XCTAssertNotNil(empty); + GPBUnknownFields* ufs = [[[GPBUnknownFields alloc] initFromMessage:empty] autorelease]; + NSMutableArray* fieldNumbers = [NSMutableArray arrayWithCapacity:ufs.count]; + for (GPBUnknownField* field in ufs) { + GPBWireFormat wireFormat; + switch (field.type) { + case GPBUnknownFieldTypeFixed32: + wireFormat = GPBWireFormatFixed32; + break; + case GPBUnknownFieldTypeFixed64: + wireFormat = GPBWireFormatFixed64; + break; + case GPBUnknownFieldTypeVarint: + wireFormat = GPBWireFormatVarint; + break; + case GPBUnknownFieldTypeLengthDelimited: + wireFormat = GPBWireFormatLengthDelimited; + break; + case GPBUnknownFieldTypeGroup: + wireFormat = GPBWireFormatStartGroup; + break; + case GPBUnknownFieldTypeLegacy: + XCTFail(@"Legacy field type not expected"); + wireFormat = GPBWireFormatVarint; + break; + } + uint32_t tag = GPBWireFormatMakeTag(field.number, wireFormat); + [fieldNumbers addObject:@(tag)]; + } + + // Check the tags compared to what's in the UnknownFields to confirm the stream is + // skipping as expected (this covers the tags within a group also). GPBCodedInputStream* input1 = [GPBCodedInputStream streamWithData:rawBytes]; GPBCodedInputStream* input2 = [GPBCodedInputStream streamWithData:rawBytes]; GPBUnknownFieldSet* unknownFields = [[[GPBUnknownFieldSet alloc] init] autorelease]; + NSUInteger idx = 0; while (YES) { int32_t tag = [input1 readTag]; XCTAssertEqual(tag, [input2 readTag]); if (tag == 0) { + XCTAssertEqual(idx, fieldNumbers.count); break; } + XCTAssertEqual(tag, [fieldNumbers[idx] intValue]); [unknownFields mergeFieldFrom:tag input:input1]; [input2 skipField:tag]; + ++idx; } } @@ -444,4 +483,102 @@ } } +- (void)assertReadByteToEndGroupFails:(NSData*)data { + GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data]; + uint32_t tag = [input readTag]; + XCTAssertThrows(GPBCodedInputStreamReadRetainedBytesToEndGroupNoCopy( + &input->state_, GPBWireFormatGetTagFieldNumber(tag))); +} + +- (void)assertReadByteToEndGroup:(NSData*)data value:(NSData*)value { + GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data]; + uint32_t tag = [input readTag]; + NSData* readValue = GPBCodedInputStreamReadRetainedBytesToEndGroupNoCopy( + &input->state_, GPBWireFormatGetTagFieldNumber(tag)); + XCTAssertNotNil(readValue); + XCTAssertEqualObjects(readValue, value); + [readValue release]; +} + +static NSData* DataForGroupsOfDepth(NSUInteger depth) { + NSMutableData* data = [NSMutableData dataWithCapacity:0]; + + uint32_t byte = 35; // 35 = 0b100011 -> field 4/start group + for (NSUInteger i = 0; i < depth; ++i) { + [data appendBytes:&byte length:1]; + } + + byte = 8; // 8 = 0b1000, -> field 1/varint + [data appendBytes:&byte length:1]; + byte = 1; // 1 -> varint value of 1 + [data appendBytes:&byte length:1]; + + byte = 36; // 36 = 0b100100 -> field 4/end group + for (NSUInteger i = 0; i < depth; ++i) { + [data appendBytes:&byte length:1]; + } + return data; +} + +- (void)testBytesToEndGroup { + // 35 = 0b100011 -> field 4/start group + // 36 = 0b100100 -> field 4/end group + // 43 = 0b101011 -> field 5/end group + // 44 = 0b101100 -> field 5/end group + // 8 = 0b1000, 1 -> field 1/varint, value of 1 + // 21 = 0b10101, 0x78, 0x56, 0x34, 0x12 -> field 2/fixed32, value of 0x12345678 + // 25 = 0b11001, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12 -> field 3/fixed64, + // value of 0x123456789abcdef0LL + // 50 = 0b110010, 0x0 -> field 6/length delimited, length 0 + // 50 = 0b110010, 0x1, 42 -> field 6/length delimited, length 1, byte 42 + // 0 -> field 0 which is invalid/varint + // 15 = 0b1111 -> field 1, wire type 7 which is invalid + + [self assertReadByteToEndGroup:bytes(35, 36) value:bytes(36)]; // empty group + [self assertReadByteToEndGroup:bytes(35, 8, 1, 36) value:bytes(8, 1, 36)]; // varint + [self assertReadByteToEndGroup:bytes(35, 21, 0x78, 0x56, 0x34, 0x12, 36) // fixed32 + value:bytes(21, 0x78, 0x56, 0x34, 0x12, 36)]; + [self assertReadByteToEndGroup:bytes(35, 25, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, + 36) // fixed64 + value:bytes(25, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, 36)]; + [self assertReadByteToEndGroup:bytes(35, 50, 0, 36) + value:bytes(50, 0, 36)]; // length delimited, length 0 + [self assertReadByteToEndGroup:bytes(35, 50, 1, 42, 36) + value:bytes(50, 1, 42, 36)]; // length delimited, length 1, byte 42 + [self assertReadByteToEndGroup:bytes(35, 43, 44, 36) value:bytes(43, 44, 36)]; // Sub group + [self assertReadByteToEndGroup:bytes(35, 8, 1, 43, 8, 1, 44, + 36) // varint and sub group with varint + value:bytes(8, 1, 43, 8, 1, 44, 36)]; + + [self assertReadByteToEndGroupFails:bytes(35, 0, 36)]; // Invalid field number + [self assertReadByteToEndGroupFails:bytes(35, 15, 36)]; // Invalid wire type + [self assertReadByteToEndGroupFails:bytes(35, 21, 0x78, 0x56, 0x34)]; // truncated fixed32 + [self assertReadByteToEndGroupFails:bytes(35, 25, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, + 0x34)]; // truncated fixed64 + + // Mising end group + [self assertReadByteToEndGroupFails:bytes(35)]; + [self assertReadByteToEndGroupFails:bytes(35, 8, 1)]; + [self assertReadByteToEndGroupFails:bytes(35, 43)]; + [self assertReadByteToEndGroupFails:bytes(35, 43, 8, 1)]; + + // Wrong end group + [self assertReadByteToEndGroupFails:bytes(35, 44)]; + [self assertReadByteToEndGroupFails:bytes(35, 8, 1, 44)]; + [self assertReadByteToEndGroupFails:bytes(35, 43, 36)]; + [self assertReadByteToEndGroupFails:bytes(35, 43, 8, 1, 36)]; + [self assertReadByteToEndGroupFails:bytes(35, 43, 44, 44)]; + [self assertReadByteToEndGroupFails:bytes(35, 43, 8, 1, 44, 44)]; + + // This is the same limit as within GPBCodedInputStream. + const NSUInteger kDefaultRecursionLimit = 100; + // That depth parses. + NSData* testData = DataForGroupsOfDepth(kDefaultRecursionLimit); + [self assertReadByteToEndGroup:testData + value:[testData subdataWithRange:NSMakeRange(1, testData.length - 1)]]; + // One more level deep fails. + testData = DataForGroupsOfDepth(kDefaultRecursionLimit + 1); + [self assertReadByteToEndGroupFails:testData]; +} + @end diff --git a/objectivec/Tests/GPBMessageTests+Serialization.m b/objectivec/Tests/GPBMessageTests+Serialization.m index f3e8abce15..b42bfdd564 100644 --- a/objectivec/Tests/GPBMessageTests+Serialization.m +++ b/objectivec/Tests/GPBMessageTests+Serialization.m @@ -419,6 +419,17 @@ // All the values should be in unknown fields. + GPBUnknownFields *ufs = [[GPBUnknownFields alloc] initFromMessage:msg]; + XCTAssertEqual(ufs.count, 3U); + uint64_t varint; + XCTAssertTrue([ufs getFirst:Message2_FieldNumber_OptionalEnum varint:&varint]); + XCTAssertEqual(varint, (uint64_t)Message3_Enum_Extra3); + XCTAssertTrue([ufs getFirst:Message2_FieldNumber_RepeatedEnumArray varint:&varint]); + XCTAssertEqual(varint, (uint64_t)Message3_Enum_Extra3); + XCTAssertTrue([ufs getFirst:Message2_FieldNumber_OneofEnum varint:&varint]); + XCTAssertEqual(varint, (uint64_t)Message3_Enum_Extra3); + [ufs release]; + GPBUnknownFieldSet *unknownFields = msg.unknownFields; XCTAssertEqual([unknownFields countOfFields], 3U); @@ -1398,6 +1409,9 @@ int32_t val = -1; XCTAssertTrue([msg1.knownMapField getEnum:&val forKey:0]); XCTAssertEqual(val, Proto2MapEnum_Proto2MapEnumFoo); + GPBUnknownFields *ufs = [[GPBUnknownFields alloc] initFromMessage:msg1]; + XCTAssertEqual(ufs.count, 1U); + [ufs release]; XCTAssertEqual(msg1.unknownFields.countOfFields, 1U); data = [msg1 data]; @@ -1410,6 +1424,9 @@ XCTAssertEqual(msg2.unknownMapField.count, 1U); XCTAssertTrue([msg2.unknownMapField getEnum:&val forKey:0]); XCTAssertEqual(val, Proto2MapEnumPlusExtra_EProto2MapEnumExtra); + ufs = [[GPBUnknownFields alloc] initFromMessage:msg2]; + XCTAssertTrue(ufs.empty); + [ufs release]; XCTAssertEqual(msg2.unknownFields.countOfFields, 0U); XCTAssertEqualObjects(orig, msg2); diff --git a/objectivec/Tests/GPBMessageTests.m b/objectivec/Tests/GPBMessageTests.m index d598a48b60..f3272dbaf2 100644 --- a/objectivec/Tests/GPBMessageTests.m +++ b/objectivec/Tests/GPBMessageTests.m @@ -5,16 +5,18 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#import "GPBTestUtilities.h" - #import +#import "GPBArray.h" #import "GPBArray_PackagePrivate.h" #import "GPBDescriptor.h" #import "GPBDictionary_PackagePrivate.h" #import "GPBMessage_PackagePrivate.h" +#import "GPBTestUtilities.h" +#import "GPBUnknownField.h" #import "GPBUnknownFieldSet_PackagePrivate.h" #import "GPBUnknownField_PackagePrivate.h" +#import "GPBUnknownFields.h" #import "objectivec/Tests/Unittest.pbobjc.h" #import "objectivec/Tests/UnittestImport.pbobjc.h" #import "objectivec/Tests/UnittestObjc.pbobjc.h" @@ -501,6 +503,11 @@ [message setUnknownFields:unknownFields]; + GPBUnknownFields *ufs = [[[GPBUnknownFields alloc] init] autorelease]; + [ufs addFieldNumber:1234 fixed32:1234]; + [ufs addFieldNumber:2345 varint:54321]; + [message mergeUnknownFields:ufs extensionRegistry:nil]; + NSString *description = [message description]; XCTAssertGreaterThan([description length], 0U); @@ -985,6 +992,19 @@ XCTAssertFalse([message hasOptionalNestedMessage]); [message.optionalNestedMessage setUnknownFields:unknownFields]; XCTAssertTrue([message hasOptionalNestedMessage]); + + message.optionalNestedMessage = nil; + XCTAssertFalse([message hasOptionalNestedMessage]); + GPBUnknownFields *ufs = [[[GPBUnknownFields alloc] init] autorelease]; + [ufs addFieldNumber:1 varint:1]; + [message.optionalNestedMessage mergeUnknownFields:ufs extensionRegistry:nil]; + XCTAssertTrue([message hasOptionalNestedMessage]); + + message.optionalNestedMessage = nil; + XCTAssertFalse([message hasOptionalNestedMessage]); + [ufs clear]; // Also make sure merging zero length forces it to become visible. + [message.optionalNestedMessage mergeUnknownFields:ufs extensionRegistry:nil]; + XCTAssertTrue([message hasOptionalNestedMessage]); } - (void)testSetAutocreatedSubmessageToSelf { @@ -1481,6 +1501,19 @@ XCTAssertFalse([msg hasExtension:[UnittestRoot repeatedNestedEnumExtension]]); XCTAssertFalse([msg hasExtension:[UnittestRoot repeatedForeignEnumExtension]]); + GPBUnknownFields *ufs = [[[GPBUnknownFields alloc] initFromMessage:msg] autorelease]; + XCTAssertEqual(ufs.count, 3); + uint64_t varint; + XCTAssertTrue([ufs getFirst:[UnittestRoot optionalNestedEnumExtension].fieldNumber + varint:&varint]); + XCTAssertEqual(varint, 10); + XCTAssertTrue([ufs getFirst:[UnittestRoot repeatedNestedEnumExtension].fieldNumber + varint:&varint]); + XCTAssertEqual(varint, 11); + XCTAssertTrue([ufs getFirst:[UnittestRoot repeatedForeignEnumExtension].fieldNumber + varint:&varint]); + XCTAssertEqual(varint, 12); + GPBUnknownFieldSet *unknownFields = msg.unknownFields; GPBUnknownField *field = [unknownFields getField:[UnittestRoot optionalNestedEnumExtension].fieldNumber]; @@ -1523,6 +1556,18 @@ expected = @[ @4, @6 ]; XCTAssertEqualObjects([msg getExtension:[UnittestRoot repeatedForeignEnumExtension]], expected); + ufs = [[[GPBUnknownFields alloc] initFromMessage:msg] autorelease]; + XCTAssertEqual(ufs.count, 3); + XCTAssertTrue([ufs getFirst:[UnittestRoot optionalNestedEnumExtension].fieldNumber + varint:&varint]); + XCTAssertEqual(varint, 10); + XCTAssertTrue([ufs getFirst:[UnittestRoot repeatedNestedEnumExtension].fieldNumber + varint:&varint]); + XCTAssertEqual(varint, 11); + XCTAssertTrue([ufs getFirst:[UnittestRoot repeatedForeignEnumExtension].fieldNumber + varint:&varint]); + XCTAssertEqual(varint, 12); + unknownFields = msg.unknownFields; field = [unknownFields getField:[UnittestRoot optionalNestedEnumExtension].fieldNumber]; XCTAssertNotNil(field); @@ -1840,6 +1885,9 @@ [unknowns mergeVarintField:123 value:456]; GPBMessage *message = [GPBMessage message]; [message setUnknownFields:unknowns]; + GPBUnknownFields *ufs = [[[GPBUnknownFields alloc] init] autorelease]; + [ufs addFieldNumber:1234 varint:5678]; + [message mergeUnknownFields:ufs extensionRegistry:nil]; NSData *data = [message data]; GPBMessage *message2 = [GPBMessage parseFromData:data extensionRegistry:nil error:NULL]; XCTAssertEqualObjects(message, message2); @@ -1850,12 +1898,19 @@ [unknowns1 mergeVarintField:123 value:456]; GPBMessage *message1 = [GPBMessage message]; [message1 setUnknownFields:unknowns1]; + GPBUnknownFields *ufs1 = [[[GPBUnknownFields alloc] init] autorelease]; + [ufs1 addFieldNumber:1234 varint:5678]; + [message1 mergeUnknownFields:ufs1 extensionRegistry:nil]; GPBUnknownFieldSet *unknowns2 = [[[GPBUnknownFieldSet alloc] init] autorelease]; [unknowns2 mergeVarintField:789 value:987]; [unknowns2 mergeVarintField:654 value:321]; GPBMessage *message2 = [GPBMessage message]; [message2 setUnknownFields:unknowns2]; + GPBUnknownFields *ufs2 = [[[GPBUnknownFields alloc] init] autorelease]; + [ufs2 addFieldNumber:2345 fixed32:6789]; + [ufs2 addFieldNumber:3456 fixed32:7890]; + [message2 mergeUnknownFields:ufs2 extensionRegistry:nil]; NSMutableData *delimitedData = [NSMutableData data]; [delimitedData appendData:[message1 delimitedData]]; @@ -2022,6 +2077,54 @@ XCTAssertEqual([msgPrime.mumbleArray valueAtIndex:4], EnumTestMsg_MyEnum_NegTwo); } +- (void)testCloseEnumsValuesOutOfRange { + // The unknown values should all make it into the unknown fields. + EnumTestMsg *msg1 = [EnumTestMsg message]; + msg1.bar = EnumTestMsg_MyEnum_NegTwo; + msg1.baz = EnumTestMsg_MyEnum_Two; + [msg1.mumbleArray addValue:EnumTestMsg_MyEnum_Two]; + [msg1.mumbleArray addValue:EnumTestMsg_MyEnum_NegTwo]; + + NSData *data = [msg1 data]; + XCTAssertNotNil(data); + + EnumTestMsgPrime *msg2 = [EnumTestMsgPrime parseFromData:data error:NULL]; + XCTAssertNotNil(msg2); + XCTAssertEqualObjects(data, [msg2 data]); + XCTAssertFalse(msg2.hasBar); + XCTAssertFalse(msg2.hasBaz); + XCTAssertEqual(msg2.mumbleArray_Count, 0U); + + GPBUnknownFields *ufs = [[[GPBUnknownFields alloc] initFromMessage:msg2] autorelease]; + XCTAssertEqual(ufs.count, 4U); + uint64_t varint; + XCTAssertTrue([ufs getFirst:EnumTestMsg_FieldNumber_Bar varint:&varint]); + XCTAssertEqual(varint, (uint64_t)EnumTestMsg_MyEnum_NegTwo); + XCTAssertTrue([ufs getFirst:EnumTestMsg_FieldNumber_Baz varint:&varint]); + XCTAssertEqual(varint, (uint64_t)EnumTestMsg_MyEnum_Two); + NSArray *fields = [ufs fields:EnumTestMsg_FieldNumber_MumbleArray]; + XCTAssertEqual(fields.count, 2U); + XCTAssertEqual(fields[0].varint, (uint64_t)EnumTestMsg_MyEnum_Two); + XCTAssertEqual(fields[1].varint, (uint64_t)EnumTestMsg_MyEnum_NegTwo); + + GPBUnknownFieldSet *unknownFields = msg2.unknownFields; + XCTAssertNotNil(unknownFields); + XCTAssertEqual(unknownFields.countOfFields, 3U); + XCTAssertTrue([unknownFields hasField:EnumTestMsg_FieldNumber_Bar]); + XCTAssertTrue([unknownFields hasField:EnumTestMsg_FieldNumber_Baz]); + XCTAssertTrue([unknownFields hasField:EnumTestMsg_FieldNumber_MumbleArray]); + GPBUnknownField *field = [unknownFields getField:EnumTestMsg_FieldNumber_Bar]; + XCTAssertEqual(field.varintList.count, 1U); + XCTAssertEqual([field.varintList valueAtIndex:0], (uint64_t)EnumTestMsg_MyEnum_NegTwo); + field = [unknownFields getField:EnumTestMsg_FieldNumber_Baz]; + XCTAssertEqual(field.varintList.count, 1U); + XCTAssertEqual([field.varintList valueAtIndex:0], (uint64_t)EnumTestMsg_MyEnum_Two); + field = [unknownFields getField:EnumTestMsg_FieldNumber_MumbleArray]; + XCTAssertEqual(field.varintList.count, 2U); + XCTAssertEqual([field.varintList valueAtIndex:0], (uint64_t)EnumTestMsg_MyEnum_Two); + XCTAssertEqual([field.varintList valueAtIndex:1], (uint64_t)EnumTestMsg_MyEnum_NegTwo); +} + - (void)testReservedWordNaming { // names.cc has some special handing to make sure that some "reserved" objc // names get renamed in a way so they don't conflict. diff --git a/objectivec/Tests/GPBUnknownFieldSetTest.m b/objectivec/Tests/GPBUnknownFieldSetTest.m index 10478ac772..890ce8f4ad 100644 --- a/objectivec/Tests/GPBUnknownFieldSetTest.m +++ b/objectivec/Tests/GPBUnknownFieldSetTest.m @@ -6,6 +6,7 @@ // https://developers.google.com/open-source/licenses/bsd #import "GPBTestUtilities.h" +#import "GPBUnknownFieldSet.h" #import "GPBUnknownFieldSet_PackagePrivate.h" #import "GPBUnknownField_PackagePrivate.h" @@ -281,6 +282,10 @@ TestEmptyMessage* destination2 = [TestEmptyMessage message]; [destination2 mergeFrom:source3]; + XCTAssertEqualObjects(destination1.unknownFields, destination2.unknownFields); + XCTAssertEqualObjects(destination1.unknownFields, source3.unknownFields); + XCTAssertEqualObjects(destination2.unknownFields, source3.unknownFields); + XCTAssertEqualObjects(destination1.data, destination2.data); XCTAssertEqualObjects(destination1.data, source3.data); XCTAssertEqualObjects(destination2.data, source3.data); @@ -376,6 +381,180 @@ XCTAssertEqual(0x7FFFFFFFFFFFFFFFULL, [field2.varintList valueAtIndex:0]); } +static NSData* DataForGroupsOfDepth(NSUInteger depth) { + NSMutableData* data = [NSMutableData dataWithCapacity:0]; + + uint32_t byte = 35; // 35 = 0b100011 -> field 4/start group + for (NSUInteger i = 0; i < depth; ++i) { + [data appendBytes:&byte length:1]; + } + + byte = 8; // 8 = 0b1000, -> field 1/varint + [data appendBytes:&byte length:1]; + byte = 1; // 1 -> varint value of 1 + [data appendBytes:&byte length:1]; + + byte = 36; // 36 = 0b100100 -> field 4/end group + for (NSUInteger i = 0; i < depth; ++i) { + [data appendBytes:&byte length:1]; + } + return data; +} + +- (void)testParsingNestingGroupData { + // 35 = 0b100011 -> field 4/start group + // 36 = 0b100100 -> field 4/end group + // 43 = 0b101011 -> field 5/end group + // 44 = 0b101100 -> field 5/end group + // 8 = 0b1000, 1 -> field 1/varint, value of 1 + // 21 = 0b10101, 0x78, 0x56, 0x34, 0x12 -> field 2/fixed32, value of 0x12345678 + // 25 = 0b11001, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12 -> field 3/fixed64, + // value of 0x123456789abcdef0LL + // 50 = 0b110010, 0x0 -> field 6/length delimited, length 0 + // 50 = 0b110010, 0x1, 42 -> field 6/length delimited, length 1, byte 42 + // 0 -> field 0 which is invalid/varint + // 15 = 0b1111 -> field 1, wire type 7 which is invalid + + TestEmptyMessage* m = [TestEmptyMessage parseFromData:DataFromBytes(35, 36) + error:NULL]; // empty group + XCTAssertEqual(m.unknownFields.countOfFields, (NSUInteger)1); + GPBUnknownField* field = [m.unknownFields getField:4]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + GPBUnknownFieldSet* group = field.groupList[0]; + XCTAssertEqual(group.countOfFields, (NSUInteger)0); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 8, 1, 36) error:NULL]; // varint + XCTAssertEqual(m.unknownFields.countOfFields, (NSUInteger)1); + field = [m.unknownFields getField:4]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + field = [group getField:1]; + XCTAssertEqual(field.varintList.count, (NSUInteger)1); + XCTAssertEqual([field.varintList valueAtIndex:0], 1); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 21, 0x78, 0x56, 0x34, 0x12, 36) + error:NULL]; // fixed32 + XCTAssertEqual(m.unknownFields.countOfFields, (NSUInteger)1); + field = [m.unknownFields getField:4]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + field = [group getField:2]; + XCTAssertEqual(field.fixed32List.count, (NSUInteger)1); + XCTAssertEqual([field.fixed32List valueAtIndex:0], 0x12345678); + + m = [TestEmptyMessage + parseFromData:DataFromBytes(35, 25, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, + 36) + error:NULL]; // fixed64 + XCTAssertEqual(m.unknownFields.countOfFields, (NSUInteger)1); + field = [m.unknownFields getField:4]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + field = [group getField:3]; + XCTAssertEqual(field.fixed64List.count, (NSUInteger)1); + XCTAssertEqual([field.fixed64List valueAtIndex:0], 0x123456789abcdef0LL); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 50, 0, 36) + error:NULL]; // length delimited, length 0 + XCTAssertEqual(m.unknownFields.countOfFields, (NSUInteger)1); + field = [m.unknownFields getField:4]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + field = [group getField:6]; + XCTAssertEqual(field.lengthDelimitedList.count, (NSUInteger)1); + XCTAssertEqualObjects(field.lengthDelimitedList[0], [NSData data]); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 50, 1, 42, 36) + error:NULL]; // length delimited, length 1, byte 42 + XCTAssertEqual(m.unknownFields.countOfFields, (NSUInteger)1); + field = [m.unknownFields getField:4]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + field = [group getField:6]; + XCTAssertEqual(field.lengthDelimitedList.count, (NSUInteger)1); + XCTAssertEqualObjects(field.lengthDelimitedList[0], DataFromBytes(42)); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 43, 44, 36) error:NULL]; // Sub group + field = [m.unknownFields getField:4]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + XCTAssertEqual(group.countOfFields, (NSUInteger)1); + field = [group getField:5]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + XCTAssertEqual(group.countOfFields, (NSUInteger)0); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 8, 1, 43, 8, 2, 44, 36) + error:NULL]; // varint and sub group with varint + XCTAssertEqual(m.unknownFields.countOfFields, (NSUInteger)1); + field = [m.unknownFields getField:4]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + XCTAssertEqual(group.countOfFields, (NSUInteger)2); + field = [group getField:1]; + XCTAssertEqual(field.varintList.count, (NSUInteger)1); + XCTAssertEqual([field.varintList valueAtIndex:0], 1); + field = [group getField:5]; + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + field = [group getField:1]; + XCTAssertEqual(field.varintList.count, (NSUInteger)1); + XCTAssertEqual([field.varintList valueAtIndex:0], 2); + + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 0, 36) + error:NULL]); // Invalid field number + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 15, 36) + error:NULL]); // Invalid wire type + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 21, 0x78, 0x56, 0x34) + error:NULL]); // truncated fixed32 + XCTAssertNil([TestEmptyMessage + parseFromData:DataFromBytes(35, 25, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, + 0x34) + error:NULL]); // truncated fixed64 + + // Mising end group + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 8, 1) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 8, 1) error:NULL]); + + // Wrong end group + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 44) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 8, 1, 44) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 36) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 8, 1, 36) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 44, 44) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 8, 1, 44, 44) error:NULL]); + + // This is the same limit as within GPBCodedInputStream. + const NSUInteger kDefaultRecursionLimit = 100; + // That depth parses. + NSData* testData = DataForGroupsOfDepth(kDefaultRecursionLimit); + m = [TestEmptyMessage parseFromData:testData error:NULL]; + XCTAssertEqual(m.unknownFields.countOfFields, (NSUInteger)1); + field = [m.unknownFields getField:4]; + for (NSUInteger i = 0; i < kDefaultRecursionLimit; ++i) { + XCTAssertEqual(field.varintList.count, (NSUInteger)0); + XCTAssertEqual(field.fixed32List.count, (NSUInteger)0); + XCTAssertEqual(field.fixed64List.count, (NSUInteger)0); + XCTAssertEqual(field.lengthDelimitedList.count, (NSUInteger)0); + XCTAssertEqual(field.groupList.count, (NSUInteger)1); + group = field.groupList[0]; + XCTAssertEqual(group.countOfFields, (NSUInteger)1); + field = [group getField:(i < (kDefaultRecursionLimit - 1) ? 4 : 1)]; + } + // field is of the inner most group + XCTAssertEqual(field.varintList.count, (NSUInteger)1); + XCTAssertEqual([field.varintList valueAtIndex:0], (NSUInteger)1); + XCTAssertEqual(field.fixed32List.count, (NSUInteger)0); + XCTAssertEqual(field.fixed64List.count, (NSUInteger)0); + XCTAssertEqual(field.lengthDelimitedList.count, (NSUInteger)0); + XCTAssertEqual(field.groupList.count, (NSUInteger)0); + // One more level deep fails. + testData = DataForGroupsOfDepth(kDefaultRecursionLimit + 1); + XCTAssertNil([TestEmptyMessage parseFromData:testData error:NULL]); +} + #pragma mark - Field tests // Some tests directly on fields since the dictionary in FieldSet can gate // testing some of these. diff --git a/objectivec/Tests/GPBUnknownFieldsTest.m b/objectivec/Tests/GPBUnknownFieldsTest.m index 9726d8361f..8c2adcf32c 100644 --- a/objectivec/Tests/GPBUnknownFieldsTest.m +++ b/objectivec/Tests/GPBUnknownFieldsTest.m @@ -847,4 +847,163 @@ XCTAssertEqual(varint, 0x7FFFFFFFFFFFFFFFL); } +static NSData* DataForGroupsOfDepth(NSUInteger depth) { + NSMutableData* data = [NSMutableData dataWithCapacity:0]; + + uint32_t byte = 35; // 35 = 0b100011 -> field 4/start group + for (NSUInteger i = 0; i < depth; ++i) { + [data appendBytes:&byte length:1]; + } + + byte = 8; // 8 = 0b1000, -> field 1/varint + [data appendBytes:&byte length:1]; + byte = 1; // 1 -> varint value of 1 + [data appendBytes:&byte length:1]; + + byte = 36; // 36 = 0b100100 -> field 4/end group + for (NSUInteger i = 0; i < depth; ++i) { + [data appendBytes:&byte length:1]; + } + return data; +} + +- (void)testParsingNestingGroupData { + // 35 = 0b100011 -> field 4/start group + // 36 = 0b100100 -> field 4/end group + // 43 = 0b101011 -> field 5/end group + // 44 = 0b101100 -> field 5/end group + // 8 = 0b1000, 1 -> field 1/varint, value of 1 + // 21 = 0b10101, 0x78, 0x56, 0x34, 0x12 -> field 2/fixed32, value of 0x12345678 + // 25 = 0b11001, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12 -> field 3/fixed64, + // value of 0x123456789abcdef0LL + // 50 = 0b110010, 0x0 -> field 6/length delimited, length 0 + // 50 = 0b110010, 0x1, 42 -> field 6/length delimited, length 1, byte 42 + // 0 -> field 0 which is invalid/varint + // 15 = 0b1111 -> field 1, wire type 7 which is invalid + + TestEmptyMessage* m = [TestEmptyMessage parseFromData:DataFromBytes(35, 36) + error:NULL]; // empty group + GPBUnknownFields* ufs = [[[GPBUnknownFields alloc] initFromMessage:m] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + GPBUnknownFields* group = [ufs firstGroup:4]; + XCTAssertTrue(group.empty); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 8, 1, 36) error:NULL]; // varint + ufs = [[[GPBUnknownFields alloc] initFromMessage:m] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + group = [ufs firstGroup:4]; + XCTAssertEqual(group.count, (NSUInteger)1); + uint64_t varint = 0; + XCTAssertTrue([group getFirst:1 varint:&varint]); + XCTAssertEqual(varint, 1); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 21, 0x78, 0x56, 0x34, 0x12, 36) + error:NULL]; // fixed32 + ufs = [[[GPBUnknownFields alloc] initFromMessage:m] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + group = [ufs firstGroup:4]; + XCTAssertEqual(group.count, (NSUInteger)1); + uint32_t fixed32 = 0; + XCTAssertTrue([group getFirst:2 fixed32:&fixed32]); + XCTAssertEqual(fixed32, 0x12345678); + + m = [TestEmptyMessage + parseFromData:DataFromBytes(35, 25, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, + 36) + error:NULL]; // fixed64 + ufs = [[[GPBUnknownFields alloc] initFromMessage:m] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + group = [ufs firstGroup:4]; + XCTAssertEqual(group.count, (NSUInteger)1); + uint64_t fixed64 = 0; + XCTAssertTrue([group getFirst:3 fixed64:&fixed64]); + XCTAssertEqual(fixed64, 0x123456789abcdef0LL); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 50, 0, 36) + error:NULL]; // length delimited, length 0 + ufs = [[[GPBUnknownFields alloc] initFromMessage:m] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + group = [ufs firstGroup:4]; + XCTAssertEqual(group.count, (NSUInteger)1); + NSData* lengthDelimited = [group firstLengthDelimited:6]; + XCTAssertEqualObjects(lengthDelimited, [NSData data]); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 50, 1, 42, 36) + error:NULL]; // length delimited, length 1, byte 42 + ufs = [[[GPBUnknownFields alloc] initFromMessage:m] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + group = [ufs firstGroup:4]; + XCTAssertEqual(group.count, (NSUInteger)1); + lengthDelimited = [group firstLengthDelimited:6]; + XCTAssertEqualObjects(lengthDelimited, DataFromBytes(42)); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 43, 44, 36) error:NULL]; // Sub group + ufs = [[[GPBUnknownFields alloc] initFromMessage:m] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + group = [ufs firstGroup:4]; + XCTAssertEqual(group.count, (NSUInteger)1); + group = [group firstGroup:5]; + XCTAssertTrue(group.empty); + + m = [TestEmptyMessage parseFromData:DataFromBytes(35, 8, 1, 43, 8, 2, 44, 36) + error:NULL]; // varint and sub group with varint + ufs = [[[GPBUnknownFields alloc] initFromMessage:m] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + group = [ufs firstGroup:4]; + XCTAssertEqual(group.count, (NSUInteger)2); + varint = 0; + XCTAssertTrue([group getFirst:1 varint:&varint]); + XCTAssertEqual(varint, 1); + group = [group firstGroup:5]; + XCTAssertEqual(group.count, (NSUInteger)1); + XCTAssertTrue([group getFirst:1 varint:&varint]); + XCTAssertEqual(varint, 2); + + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 0, 36) + error:NULL]); // Invalid field number + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 15, 36) + error:NULL]); // Invalid wire type + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 21, 0x78, 0x56, 0x34) + error:NULL]); // truncated fixed32 + XCTAssertNil([TestEmptyMessage + parseFromData:DataFromBytes(35, 25, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, + 0x34) + error:NULL]); // truncated fixed64 + + // Mising end group + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 8, 1) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 8, 1) error:NULL]); + + // Wrong end group + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 44) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 8, 1, 44) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 36) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 8, 1, 36) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 44, 44) error:NULL]); + XCTAssertNil([TestEmptyMessage parseFromData:DataFromBytes(35, 43, 8, 1, 44, 44) error:NULL]); + + // This is the same limit as within GPBCodedInputStream. + const NSUInteger kDefaultRecursionLimit = 100; + // That depth parses. + NSData* testData = DataForGroupsOfDepth(kDefaultRecursionLimit); + m = [TestEmptyMessage parseFromData:testData error:NULL]; + ufs = [[[GPBUnknownFields alloc] initFromMessage:m] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + group = [ufs firstGroup:4]; + for (NSUInteger i = 1; i < kDefaultRecursionLimit; ++i) { + XCTAssertEqual(group.count, (NSUInteger)1); + group = [group firstGroup:4]; + } + // group is now the inner most group. + XCTAssertEqual(group.count, (NSUInteger)1); + varint = 0; + XCTAssertTrue([group getFirst:1 varint:&varint]); + XCTAssertEqual(varint, 1); + // One more level deep fails. + testData = DataForGroupsOfDepth(kDefaultRecursionLimit + 1); + XCTAssertNil([TestEmptyMessage parseFromData:testData error:NULL]); +} + @end diff --git a/objectivec/Tests/GPBUtilitiesTests.m b/objectivec/Tests/GPBUtilitiesTests.m index e32fccda46..fb0175ecca 100644 --- a/objectivec/Tests/GPBUtilitiesTests.m +++ b/objectivec/Tests/GPBUtilitiesTests.m @@ -6,17 +6,16 @@ // https://developers.google.com/open-source/licenses/bsd #import - -#import "GPBUtilities_PackagePrivate.h" - #import -#import "GPBTestUtilities.h" - #import "GPBDescriptor.h" #import "GPBDescriptor_PackagePrivate.h" #import "GPBMessage.h" +#import "GPBTestUtilities.h" +#import "GPBUnknownField.h" #import "GPBUnknownField_PackagePrivate.h" +#import "GPBUtilities.h" +#import "GPBUtilities_PackagePrivate.h" #import "objectivec/Tests/MapUnittest.pbobjc.h" #import "objectivec/Tests/Unittest.pbobjc.h" @@ -168,6 +167,54 @@ [expected release]; } +- (void)testTextFormatUnknownFields { + GPBUnknownFields *ufs = [[[GPBUnknownFields alloc] init] autorelease]; + [ufs addFieldNumber:100 varint:5]; + [ufs addFieldNumber:100 varint:4]; + [ufs addFieldNumber:10 varint:1]; + [ufs addFieldNumber:300 fixed32:0x50]; + [ufs addFieldNumber:300 fixed32:0x40]; + [ufs addFieldNumber:10 fixed32:0x10]; + [ufs addFieldNumber:200 fixed64:0x5000]; + [ufs addFieldNumber:200 fixed64:0x4000]; + [ufs addFieldNumber:10 fixed64:0x1000]; + [ufs addFieldNumber:10 lengthDelimited:DataFromCStr("foo")]; + [ufs addFieldNumber:10 lengthDelimited:DataFromCStr("bar")]; + GPBUnknownFields *group = [ufs addGroupWithFieldNumber:150]; + [group addFieldNumber:2 varint:2]; + [group addFieldNumber:1 varint:1]; + group = [ufs addGroupWithFieldNumber:150]; + [group addFieldNumber:1 varint:1]; + [group addFieldNumber:3 fixed32:0x3]; + [group addFieldNumber:2 fixed64:0x2]; + TestEmptyMessage *message = [TestEmptyMessage message]; + [message mergeUnknownFields:ufs extensionRegistry:nil]; + + NSString *expected = @"# --- Unknown fields ---\n" + @"10: 1\n" + @"10: 0x10\n" + @"10: 0x1000\n" + @"10: \"foo\"\n" + @"10: \"bar\"\n" + @"100: 5\n" + @"100: 4\n" + @"150: {\n" + @" 1: 1\n" + @" 2: 2\n" + @"}\n" + @"150: {\n" + @" 1: 1\n" + @" 2: 0x2\n" + @" 3: 0x3\n" + @"}\n" + @"200: 0x5000\n" + @"200: 0x4000\n" + @"300: 0x50\n" + @"300: 0x40\n"; + NSString *result = GPBTextFormatForMessage(message, nil); + XCTAssertEqualObjects(expected, result); +} + - (void)testSetRepeatedFields { TestAllTypes *message = [TestAllTypes message]; @@ -204,11 +251,27 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { return result; } +// Helper to add an unknown field data to messages. +static void AddUnknownFields(GPBMessage *message, int num) { + GPBUnknownFields *ufs = [[GPBUnknownFields alloc] init]; + [ufs addFieldNumber:num varint:num]; + [message mergeUnknownFields:ufs extensionRegistry:nil]; + [ufs release]; +} + +static BOOL HasUnknownFields(GPBMessage *message) { + GPBUnknownFields *ufs = [[GPBUnknownFields alloc] initFromMessage:message]; + BOOL result = !ufs.empty; + [ufs release]; + return result; +} + - (void)testDropMessageUnknownFieldsRecursively { TestAllExtensions *message = [TestAllExtensions message]; // Give it unknownFields. message.unknownFields = UnknownFieldsSetHelper(777); + AddUnknownFields(message, 1777); // Given it extensions that include a message with unknown fields of its own. { @@ -219,18 +282,21 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { OptionalGroup_extension *optionalGroup = [OptionalGroup_extension message]; optionalGroup.a = 123; optionalGroup.unknownFields = UnknownFieldsSetHelper(779); + AddUnknownFields(optionalGroup, 1779); [message setExtension:[UnittestRoot optionalGroupExtension] value:optionalGroup]; // Message TestAllTypes_NestedMessage *nestedMessage = [TestAllTypes_NestedMessage message]; nestedMessage.bb = 456; nestedMessage.unknownFields = UnknownFieldsSetHelper(778); + AddUnknownFields(nestedMessage, 1778); [message setExtension:[UnittestRoot optionalNestedMessageExtension] value:nestedMessage]; // Repeated Group RepeatedGroup_extension *repeatedGroup = [[RepeatedGroup_extension alloc] init]; repeatedGroup.a = 567; repeatedGroup.unknownFields = UnknownFieldsSetHelper(780); + AddUnknownFields(repeatedGroup, 1780); [message addExtension:[UnittestRoot repeatedGroupExtension] value:repeatedGroup]; [repeatedGroup release]; @@ -238,6 +304,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { nestedMessage = [[TestAllTypes_NestedMessage alloc] init]; nestedMessage.bb = 678; nestedMessage.unknownFields = UnknownFieldsSetHelper(781); + AddUnknownFields(nestedMessage, 1781); [message addExtension:[UnittestRoot repeatedNestedMessageExtension] value:nestedMessage]; [nestedMessage release]; } @@ -245,6 +312,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { // Confirm everything is there. XCTAssertNotNil(message); + XCTAssertTrue(HasUnknownFields(message)); XCTAssertNotNil(message.unknownFields); XCTAssertTrue([message hasExtension:[UnittestRoot optionalInt32Extension]]); @@ -254,6 +322,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { [message getExtension:[UnittestRoot optionalGroupExtension]]; XCTAssertNotNil(optionalGroup); XCTAssertEqual(optionalGroup.a, 123); + XCTAssertTrue(HasUnknownFields(optionalGroup)); XCTAssertNotNil(optionalGroup.unknownFields); } @@ -263,6 +332,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { [message getExtension:[UnittestRoot optionalNestedMessageExtension]]; XCTAssertNotNil(nestedMessage); XCTAssertEqual(nestedMessage.bb, 456); + XCTAssertTrue(HasUnknownFields(nestedMessage)); XCTAssertNotNil(nestedMessage.unknownFields); } @@ -273,6 +343,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { RepeatedGroup_extension *repeatedGroup = repeatedGroups.firstObject; XCTAssertNotNil(repeatedGroup); XCTAssertEqual(repeatedGroup.a, 567); + XCTAssertTrue(HasUnknownFields(repeatedGroup)); XCTAssertNotNil(repeatedGroup.unknownFields); } @@ -284,6 +355,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { TestAllTypes_NestedMessage *repeatedNestedMessage = repeatedNestedMessages.firstObject; XCTAssertNotNil(repeatedNestedMessage); XCTAssertEqual(repeatedNestedMessage.bb, 678); + XCTAssertTrue(HasUnknownFields(repeatedNestedMessage)); XCTAssertNotNil(repeatedNestedMessage.unknownFields); } @@ -293,6 +365,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { // Confirm unknowns are gone from within the messages. XCTAssertNotNil(message); + XCTAssertFalse(HasUnknownFields(message)); XCTAssertNil(message.unknownFields); XCTAssertTrue([message hasExtension:[UnittestRoot optionalInt32Extension]]); @@ -302,6 +375,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { [message getExtension:[UnittestRoot optionalGroupExtension]]; XCTAssertNotNil(optionalGroup); XCTAssertEqual(optionalGroup.a, 123); + XCTAssertFalse(HasUnknownFields(optionalGroup)); XCTAssertNil(optionalGroup.unknownFields); } @@ -311,6 +385,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { [message getExtension:[UnittestRoot optionalNestedMessageExtension]]; XCTAssertNotNil(nestedMessage); XCTAssertEqual(nestedMessage.bb, 456); + XCTAssertFalse(HasUnknownFields(nestedMessage)); XCTAssertNil(nestedMessage.unknownFields); } @@ -321,6 +396,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { RepeatedGroup_extension *repeatedGroup = repeatedGroups.firstObject; XCTAssertNotNil(repeatedGroup); XCTAssertEqual(repeatedGroup.a, 567); + XCTAssertFalse(HasUnknownFields(repeatedGroup)); XCTAssertNil(repeatedGroup.unknownFields); } @@ -332,6 +408,7 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { TestAllTypes_NestedMessage *repeatedNestedMessage = repeatedNestedMessages.firstObject; XCTAssertNotNil(repeatedNestedMessage); XCTAssertEqual(repeatedNestedMessage.bb, 678); + XCTAssertFalse(HasUnknownFields(repeatedNestedMessage)); XCTAssertNil(repeatedNestedMessage.unknownFields); } } @@ -342,10 +419,12 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { { ForeignMessage *foreignMessage = [ForeignMessage message]; foreignMessage.unknownFields = UnknownFieldsSetHelper(100); + AddUnknownFields(foreignMessage, 1000); [message.mapInt32ForeignMessage setObject:foreignMessage forKey:100]; foreignMessage = [ForeignMessage message]; foreignMessage.unknownFields = UnknownFieldsSetHelper(101); + AddUnknownFields(foreignMessage, 1001); [message.mapStringForeignMessage setObject:foreignMessage forKey:@"101"]; } @@ -356,12 +435,14 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { { ForeignMessage *foreignMessage = [message.mapInt32ForeignMessage objectForKey:100]; XCTAssertNotNil(foreignMessage); + XCTAssertTrue(HasUnknownFields(foreignMessage)); XCTAssertNotNil(foreignMessage.unknownFields); } { ForeignMessage *foreignMessage = [message.mapStringForeignMessage objectForKey:@"101"]; XCTAssertNotNil(foreignMessage); + XCTAssertTrue(HasUnknownFields(foreignMessage)); XCTAssertNotNil(foreignMessage.unknownFields); } @@ -374,12 +455,14 @@ static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { { ForeignMessage *foreignMessage = [message.mapInt32ForeignMessage objectForKey:100]; XCTAssertNotNil(foreignMessage); + XCTAssertFalse(HasUnknownFields(foreignMessage)); XCTAssertNil(foreignMessage.unknownFields); } { ForeignMessage *foreignMessage = [message.mapStringForeignMessage objectForKey:@"101"]; XCTAssertNotNil(foreignMessage); + XCTAssertFalse(HasUnknownFields(foreignMessage)); XCTAssertNil(foreignMessage.unknownFields); } } diff --git a/objectivec/Tests/GPBWireFormatTests.m b/objectivec/Tests/GPBWireFormatTests.m index 313efc3ee8..3d32112f17 100644 --- a/objectivec/Tests/GPBWireFormatTests.m +++ b/objectivec/Tests/GPBWireFormatTests.m @@ -5,11 +5,13 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#import "GPBTestUtilities.h" - #import "GPBCodedInputStream.h" #import "GPBMessage_PackagePrivate.h" +#import "GPBTestUtilities.h" +#import "GPBUnknownField.h" #import "GPBUnknownField_PackagePrivate.h" +#import "GPBUnknownFields.h" +#import "GPBWireFormat.h" #import "objectivec/Tests/Unittest.pbobjc.h" #import "objectivec/Tests/UnittestMset.pbobjc.h" @@ -110,32 +112,43 @@ } const int kUnknownTypeId = 1550055; +const int kUnknownTypeId2 = 1550056; - (void)testSerializeMessageSet { // Set up a MSetMessage with two known messages and an unknown one. MSetMessage* message_set = [MSetMessage message]; [[message_set getExtension:[MSetMessageExtension1 messageSetExtension]] setI:123]; [[message_set getExtension:[MSetMessageExtension2 messageSetExtension]] setStr:@"foo"]; + GPBUnknownField* unknownField = [[[GPBUnknownField alloc] initWithNumber:kUnknownTypeId] autorelease]; - [unknownField addLengthDelimited:[NSData dataWithBytes:"bar" length:3]]; + [unknownField addLengthDelimited:DataFromCStr("bar")]; GPBUnknownFieldSet* unknownFieldSet = [[[GPBUnknownFieldSet alloc] init] autorelease]; [unknownFieldSet addField:unknownField]; [message_set setUnknownFields:unknownFieldSet]; + GPBUnknownFields* ufs = [[[GPBUnknownFields alloc] init] autorelease]; + GPBUnknownFields* group = [ufs addGroupWithFieldNumber:GPBWireFormatMessageSetItem]; + [group addFieldNumber:GPBWireFormatMessageSetTypeId varint:kUnknownTypeId2]; + [group addFieldNumber:GPBWireFormatMessageSetMessage lengthDelimited:DataFromCStr("baz")]; + [message_set mergeUnknownFields:ufs extensionRegistry:[MSetUnittestMsetRoot extensionRegistry]]; + NSData* data = [message_set data]; // Parse back using MSetRawMessageSet and check the contents. MSetRawMessageSet* raw = [MSetRawMessageSet parseFromData:data error:NULL]; + GPBUnknownFields* ufs2 = [[[GPBUnknownFields alloc] initFromMessage:raw] autorelease]; + XCTAssertTrue(ufs2.empty); XCTAssertEqual([raw.unknownFields countOfFields], (NSUInteger)0); - XCTAssertEqual(raw.itemArray.count, (NSUInteger)3); + XCTAssertEqual(raw.itemArray.count, (NSUInteger)4); XCTAssertEqual((uint32_t)[raw.itemArray[0] typeId], [MSetMessageExtension1 messageSetExtension].fieldNumber); XCTAssertEqual((uint32_t)[raw.itemArray[1] typeId], [MSetMessageExtension2 messageSetExtension].fieldNumber); XCTAssertEqual([raw.itemArray[2] typeId], kUnknownTypeId); + XCTAssertEqual([raw.itemArray[3] typeId], kUnknownTypeId2); MSetMessageExtension1* message1 = [MSetMessageExtension1 parseFromData:[((MSetRawMessageSet_Item*)raw.itemArray[0]) message] @@ -147,7 +160,8 @@ const int kUnknownTypeId = 1550055; error:NULL]; XCTAssertEqualObjects(message2.str, @"foo"); - XCTAssertEqualObjects([raw.itemArray[2] message], [NSData dataWithBytes:"bar" length:3]); + XCTAssertEqualObjects([raw.itemArray[2] message], DataFromCStr("bar")); + XCTAssertEqualObjects([raw.itemArray[3] message], DataFromCStr("baz")); } - (void)testParseMessageSet { @@ -175,7 +189,7 @@ const int kUnknownTypeId = 1550055; { MSetRawMessageSet_Item* item = [MSetRawMessageSet_Item message]; item.typeId = kUnknownTypeId; - item.message = [NSData dataWithBytes:"bar" length:3]; + item.message = DataFromCStr("bar"); [raw.itemArray addObject:item]; } @@ -190,11 +204,100 @@ const int kUnknownTypeId = 1550055; XCTAssertEqualObjects([[messageSet getExtension:[MSetMessageExtension2 messageSetExtension]] str], @"foo"); + GPBUnknownFields* ufs = [[[GPBUnknownFields alloc] initFromMessage:messageSet] autorelease]; + XCTAssertEqual(ufs.count, (NSUInteger)1); + GPBUnknownFields* group = [ufs firstGroup:GPBWireFormatMessageSetItem]; + XCTAssertNotNil(group); + XCTAssertEqual(group.count, (NSUInteger)2); + uint64_t varint = 0; + XCTAssertTrue([group getFirst:GPBWireFormatMessageSetTypeId varint:&varint]); + XCTAssertEqual(varint, kUnknownTypeId); + XCTAssertEqualObjects([group firstLengthDelimited:GPBWireFormatMessageSetMessage], + DataFromCStr("bar")); + XCTAssertEqual([messageSet.unknownFields countOfFields], (NSUInteger)1); GPBUnknownField* unknownField = [messageSet.unknownFields getField:kUnknownTypeId]; XCTAssertNotNil(unknownField); XCTAssertEqual(unknownField.lengthDelimitedList.count, (NSUInteger)1); - XCTAssertEqualObjects(unknownField.lengthDelimitedList[0], [NSData dataWithBytes:"bar" length:3]); + XCTAssertEqualObjects(unknownField.lengthDelimitedList[0], DataFromCStr("bar")); +} + +- (void)testParseMessageSet_FirstValueSticks { + MSetRawBreakableMessageSet* raw = [MSetRawBreakableMessageSet message]; + + { + MSetRawBreakableMessageSet_Item* item = [MSetRawBreakableMessageSet_Item message]; + + [item.typeIdArray addValue:[MSetMessageExtension1 messageSetExtension].fieldNumber]; + MSetMessageExtension1* message1 = [MSetMessageExtension1 message]; + message1.i = 123; + NSData* itemData = [message1 data]; + [item.messageArray addObject:itemData]; + + [item.typeIdArray addValue:[MSetMessageExtension2 messageSetExtension].fieldNumber]; + MSetMessageExtension2* message2 = [MSetMessageExtension2 message]; + message2.str = @"foo"; + itemData = [message2 data]; + [item.messageArray addObject:itemData]; + + [raw.itemArray addObject:item]; + } + + NSData* data = [raw data]; + + // Parse as a MSetMessage and check the contents. + NSError* err = nil; + MSetMessage* messageSet = [MSetMessage parseFromData:data + extensionRegistry:[MSetUnittestMsetRoot extensionRegistry] + error:&err]; + XCTAssertNotNil(messageSet); + XCTAssertNil(err); + XCTAssertTrue([messageSet hasExtension:[MSetMessageExtension1 messageSetExtension]]); + XCTAssertEqual([[messageSet getExtension:[MSetMessageExtension1 messageSetExtension]] i], 123); + XCTAssertFalse([messageSet hasExtension:[MSetMessageExtension2 messageSetExtension]]); + GPBUnknownFields* ufs = [[[GPBUnknownFields alloc] initFromMessage:messageSet] autorelease]; + XCTAssertTrue(ufs.empty); +} + +- (void)testParseMessageSet_PartialValuesDropped { + MSetRawBreakableMessageSet* raw = [MSetRawBreakableMessageSet message]; + + { + MSetRawBreakableMessageSet_Item* item = [MSetRawBreakableMessageSet_Item message]; + [item.typeIdArray addValue:[MSetMessageExtension1 messageSetExtension].fieldNumber]; + // No payload. + [raw.itemArray addObject:item]; + } + + { + MSetRawBreakableMessageSet_Item* item = [MSetRawBreakableMessageSet_Item message]; + // No type ID. + MSetMessageExtension2* message = [MSetMessageExtension2 message]; + message.str = @"foo"; + NSData* itemData = [message data]; + [item.messageArray addObject:itemData]; + [raw.itemArray addObject:item]; + } + + { + MSetRawBreakableMessageSet_Item* item = [MSetRawBreakableMessageSet_Item message]; + // Neither type ID nor payload. + [raw.itemArray addObject:item]; + } + + NSData* data = [raw data]; + + // Parse as a MSetMessage and check the contents. + NSError* err = nil; + MSetMessage* messageSet = [MSetMessage parseFromData:data + extensionRegistry:[MSetUnittestMsetRoot extensionRegistry] + error:&err]; + XCTAssertNotNil(messageSet); + XCTAssertNil(err); + XCTAssertEqual([messageSet extensionsCurrentlySet].count, + (NSUInteger)0); // None because they were all partial and dropped. + GPBUnknownFields* ufs = [[[GPBUnknownFields alloc] initFromMessage:messageSet] autorelease]; + XCTAssertTrue(ufs.empty); } - (void)assertFieldsInOrder:(NSData*)data { diff --git a/objectivec/Tests/unittest_mset.proto b/objectivec/Tests/unittest_mset.proto index 9c6a78fb1a..222348a003 100644 --- a/objectivec/Tests/unittest_mset.proto +++ b/objectivec/Tests/unittest_mset.proto @@ -14,6 +14,7 @@ option objc_class_prefix = "MSet"; // A message with message_set_wire_format. message Message { option message_set_wire_format = true; + extensions 4 to max; } @@ -40,3 +41,12 @@ message RawMessageSet { required bytes message = 3; } } + +// MessageSet wire format is equivalent to this but since the fields +// are repeated they can be left off or over present to testing. +message RawBreakableMessageSet { + repeated group Item = 1 { + repeated int32 type_id = 2; + repeated bytes message = 3; + } +} diff --git a/objectivec/Tests/unittest_objc.proto b/objectivec/Tests/unittest_objc.proto index 4f0e348569..b7b67fc372 100644 --- a/objectivec/Tests/unittest_objc.proto +++ b/objectivec/Tests/unittest_objc.proto @@ -7,11 +7,11 @@ syntax = "proto2"; +package objc.protobuf.tests; + import "google/protobuf/any.proto"; import "objectivec/Tests/unittest.proto"; -package objc.protobuf.tests; - // Explicit empty prefix, tests some validations code paths also. option objc_class_prefix = ""; @@ -78,143 +78,142 @@ message self { } enum autorelease { - retain = 1; - release = 2; + retain = 1; + release = 2; retainCount = 3; } // Singular // Objective C Keywords - optional bool id = 1; - optional bool _cmd = 2; + optional bool id = 1; + optional bool _cmd = 2; // super is used as submessage above - optional bool in = 4; - optional bool out = 5; - optional bool inout = 6; - optional bool bycopy = 7; - optional bool byref = 8; - optional bool oneway = 9; - optional bool self = 10; - optional bool instancetype = 11; - optional bool nullable = 12; - optional bool nonnull = 13; - optional bool nil = 14; + optional bool in = 4; + optional bool out = 5; + optional bool inout = 6; + optional bool bycopy = 7; + optional bool byref = 8; + optional bool oneway = 9; + optional bool self = 10; + optional bool instancetype = 11; + optional bool nullable = 12; + optional bool nonnull = 13; + optional bool nil = 14; // Nil and nil can't be in the same message - optional bool YES = 16; - optional bool NO = 17; - optional bool weak = 18; + optional bool YES = 16; + optional bool NO = 17; + optional bool weak = 18; // Some C/C++ Keywords - optional bool case = 30; - optional bool if = 31; - optional bool and_eq = 32; - optional bool public = 33; - optional bool private = 34; - optional bool typename = 35; - optional bool static_cast = 36; - optional bool typeof = 37; - optional bool restrict = 38; - optional bool NULL = 39; + optional bool case = 30; + optional bool if = 31; + optional bool and_eq = 32; + optional bool public = 33; + optional bool private = 34; + optional bool typename = 35; + optional bool static_cast = 36; + optional bool typeof = 37; + optional bool restrict = 38; + optional bool NULL = 39; // Some NSObject Methods - optional bool dealloc = 110; - optional bool isProxy = 111; - optional bool copy = 112; - optional bool description = 113; - optional bool zone = 114; - optional bool className = 115; - optional bool __retain_OA = 116; - optional bool CAMLType = 117; - optional bool isNSDictionary__ = 118; + optional bool dealloc = 110; + optional bool isProxy = 111; + optional bool copy = 112; + optional bool description = 113; + optional bool zone = 114; + optional bool className = 115; + optional bool __retain_OA = 116; + optional bool CAMLType = 117; + optional bool isNSDictionary__ = 118; optional bool accessibilityLabel = 119; // Some Objc "keywords" that we shouldn't // have to worry about because they // can only appear in specialized areas. - optional bool assign = 200; - optional bool getter = 201; - optional bool setter = 202; - optional bool atomic = 203; - optional bool nonatomic = 204; - optional bool strong = 205; - optional bool null_resettable = 206; - optional bool readonly = 207; + optional bool assign = 200; + optional bool getter = 201; + optional bool setter = 202; + optional bool atomic = 203; + optional bool nonatomic = 204; + optional bool strong = 205; + optional bool null_resettable = 206; + optional bool readonly = 207; // Some GPBMessage methods - optional bool clear = 300; - optional bool data = 301; - optional bool descriptor = 302; - optional bool delimitedData = 303; + optional bool clear = 300; + optional bool data = 301; + optional bool descriptor = 302; + optional bool delimitedData = 303; // Some MacTypes - optional bool Fixed = 400; - optional bool Point = 401; - optional bool FixedPoint = 402; - optional bool Style = 403; + optional bool Fixed = 400; + optional bool Point = 401; + optional bool FixedPoint = 402; + optional bool Style = 403; // C/C++ reserved identifiers - optional bool _Generic = 500; - optional bool __block = 501; + optional bool _Generic = 500; + optional bool __block = 501; // Try a keyword as a type - optional autorelease SubEnum = 1000; + optional autorelease SubEnum = 1000; optional group New = 2000 { - optional string copy = 1; + optional string copy = 1; } optional group MutableCopy = 2001 { optional int32 extensionRegistry = 1; } extensions 3000 to 3999; - } enum retain { - count = 4; - initialized = 5; + count = 4; + initialized = 5; serializedSize = 6; } message ObjCPropertyNaming { // Test that the properties properly get things all caps. - optional string url = 1; + optional string url = 1; optional string thumbnail_url = 2; - optional string url_foo = 3; + optional string url_foo = 3; optional string some_url_blah = 4; - optional string http = 5; - optional string https = 6; + optional string http = 5; + optional string https = 6; // This one doesn't. - repeated string urls = 7; + repeated string urls = 7; } // EnumValueShortName: The short names shouldn't get suffixes/prefixes. enum Foo { SERIALIZED_SIZE = 1; - SIZE = 2; - OTHER = 3; + SIZE = 2; + OTHER = 3; } // EnumValueShortName: The enum name gets a prefix. enum Category { - RED = 1; + RED = 1; BLUE = 2; } // EnumValueShortName: Twist case, full name gets PB, but the short names // should still end up correct. enum Time { - BASE = 1; - RECORD = 2; - SOMETHING_ELSE = 3; + BASE = 1; + RECORD = 2; + SOMETHING_ELSE = 3; } extend self { - repeated int32 debugDescription = 3000 [packed = true]; - repeated int64 finalize = 3001 [packed = true]; - repeated uint32 hash = 3002 [packed = true]; - repeated uint64 classForCoder = 3003 [packed = true]; - repeated sint32 byref = 3004 [packed = true]; + repeated int32 debugDescription = 3000 [packed = true]; + repeated int64 finalize = 3001 [packed = true]; + repeated uint32 hash = 3002 [packed = true]; + repeated uint64 classForCoder = 3003 [packed = true]; + repeated sint32 byref = 3004 [packed = true]; } // Test handing of fields that start with init*. @@ -700,13 +699,17 @@ message JustToScopeExtensions { repeated string mutableCopy_val_lower_complex_repeated = 2711; repeated string mutableCopy_Val_upper_complex_repeated = 2712; - repeated string mutableCopyvalue_lower_no_underscore_complex_repeated = 2713; - repeated string mutableCopyValue_upper_no_underscore_complex_repeated = 2714; + repeated string mutableCopyvalue_lower_no_underscore_complex_repeated = + 2713; + repeated string mutableCopyValue_upper_no_underscore_complex_repeated = + 2714; repeated int32 mutableCopy_val_lower_primitive_repeated = 2715; repeated int32 mutableCopy_Val_upper_primitive_repeated = 2716; - repeated int32 mutableCopyvalue_lower_no_underscore_primitive_repeated = 2717; - repeated int32 mutableCopyValue_upper_no_underscore_primitive_repeated = 2718; + repeated int32 mutableCopyvalue_lower_no_underscore_primitive_repeated = + 2717; + repeated int32 mutableCopyValue_upper_no_underscore_primitive_repeated = + 2718; repeated self mutableCopy_val_lower_message_repeated = 2719; repeated self mutableCopy_Val_upper_message_repeated = 2720; @@ -781,9 +784,9 @@ message ObjcWeirdDefaults { // Used to confirm negative enum values work as expected. message EnumTestMsg { enum MyEnum { - ZERO = 0; - ONE = 1; - TWO = 2; + ZERO = 0; + ONE = 1; + TWO = 2; NEG_ONE = -1; NEG_TWO = -2; } @@ -794,6 +797,20 @@ message EnumTestMsg { repeated MyEnum mumble = 4; } +message EnumTestMsgPrime { + enum MyEnumPrime { + ZERO = 0; + ONE = 1; + NEG_ONE = -1; + // Lacks 2, -2. + } + optional MyEnumPrime foo = 1; + optional MyEnumPrime bar = 2 [default = ONE]; + optional MyEnumPrime baz = 3 [default = NEG_ONE]; + + repeated MyEnumPrime mumble = 4; +} + // Test case for https://github.com/protocolbuffers/protobuf/issues/1453 // Message with no explicit defaults, but a non zero default for an enum. message MessageWithOneBasedEnum { diff --git a/src/google/protobuf/compiler/objectivec/extension.cc b/src/google/protobuf/compiler/objectivec/extension.cc index 44a7505934..b733518e9a 100644 --- a/src/google/protobuf/compiler/objectivec/extension.cc +++ b/src/google/protobuf/compiler/objectivec/extension.cc @@ -39,6 +39,14 @@ ExtensionGenerator::ExtensionGenerator( ABSL_CHECK(!descriptor->is_map()) << "error: Extension is a map<>!" << " That used to be blocked by the compiler."; + if (descriptor->containing_type()->options().message_set_wire_format()) { + ABSL_CHECK(descriptor->type() == FieldDescriptor::TYPE_MESSAGE) + << "error: Extension to a message_set_wire_format message and the type " + "wasn't a message!"; + ABSL_CHECK(!descriptor->is_repeated()) + << "error: Extension to a message_set_wire_format message should not " + "be repeated!"; + } } void ExtensionGenerator::GenerateMembersHeader(io::Printer* printer) const { diff --git a/src/google/protobuf/compiler/objectivec/message.cc b/src/google/protobuf/compiler/objectivec/message.cc index cde92f60d0..984376b249 100644 --- a/src/google/protobuf/compiler/objectivec/message.cc +++ b/src/google/protobuf/compiler/objectivec/message.cc @@ -133,7 +133,7 @@ struct ExtensionRangeOrdering { // This is a reduced case of Descriptor::ExtensionRange with just start and end. struct SimpleExtensionRange { - SimpleExtensionRange(int start, int end) : start(start), end(end){}; + SimpleExtensionRange(int start, int end) : start(start), end(end) {}; int start; // inclusive int end; // exclusive @@ -202,8 +202,12 @@ MessageGenerator::MessageGenerator(const std::string& file_description_name, class_name_(ClassName(descriptor_)), deprecated_attribute_( GetOptionalDeprecatedAttribute(descriptor, descriptor->file())) { - ABSL_DCHECK(!descriptor->options().map_entry()) + ABSL_CHECK(!descriptor->options().map_entry()) << "error: MessageGenerator create of a map<>!"; + ABSL_CHECK(!descriptor->options().message_set_wire_format() || + descriptor->field_count() == 0) + << "error: MessageGenerator message_set_wire_format should never have " + "fields!"; for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) { oneof_generators_.push_back(std::make_unique( descriptor_->real_oneof_decl(i), generation_options));