// Protocol Buffers - Google's data interchange format // Copyright 2025 Google Inc. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd #import #import "GPBDescriptor_PackagePrivate.h" #import "GPBExtensionRegistry.h" #import "GPBMessage.h" #import "GPBProtocolBuffers_RuntimeSupport.h" #import "GPBRootObject_PackagePrivate.h" #import "GPBTestUtilities.h" // This is a fake version, so the ptr check will fail. static const int32_t FAKE_GOOGLE_PROTOBUF_OBJC_EXPECTED_GENCODE_VERSION_40311 = 40311; @interface MessageBadVersionFormatTest : GPBTestCase @end // clang-format off // NOLINTBEGIN // ------------------------------------------------------------------------------------------------- // // This is extracted from generated code with the 40311 format but then edited so the pass in // unknown values for the version support, the original proto was as follows: // // syntax = "proto2"; // // enum EnumBadVersion { // FOO = 0; // BAR = 1; // } // // message MessageBadVersion { // optional EnumBadVersion value = 1; // extensions 100 to max; // } // // extend MessageBadVersion { // optional MessageBadVersion other_m = 100; // optional EnumBadVersion other_e = 101; // } // // ------------------------------------------------------------------------------------------------- NS_ASSUME_NONNULL_BEGIN typedef GPB_ENUM(EnumBadVersion) { EnumBadVersion_Foo = 0, EnumBadVersion_Bar = 1, }; GPBEnumDescriptor *EnumBadVersion_EnumDescriptor(void); BOOL EnumBadVersion_IsValidValue(int32_t value); GPB_FINAL @interface TestBadVersionRoot : GPBRootObject @end @interface TestBadVersionRoot (DynamicMethods) + (GPBExtensionDescriptor *)otherM; + (GPBExtensionDescriptor *)otherE; @end typedef GPB_ENUM(MessageBadVersion_FieldNumber) { MessageBadVersion_FieldNumber_Value = 1, }; GPB_FINAL @interface MessageBadVersion : GPBMessage @property(nonatomic, readwrite) EnumBadVersion value; @property(nonatomic, readwrite) BOOL hasValue; @end NS_ASSUME_NONNULL_END #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" GPBObjCClassDeclaration(MessageBadVersion); @implementation TestBadVersionRoot + (GPBExtensionRegistry*)extensionRegistry { // This is called by +initialize so there is no need to worry // about thread safety and initialization of registry. static GPBExtensionRegistry* registry = nil; if (!registry) { registry = [[GPBExtensionRegistry alloc] init]; static GPBExtensionDescription descriptions[] = { { .defaultValue.valueMessage = nil, .singletonName = GPBStringifySymbol(TestBadVersionRoot) "_otherM", .extendedClass.clazz = GPBObjCClass(MessageBadVersion), .messageOrGroupClass.clazz = GPBObjCClass(MessageBadVersion), .enumDescriptorFunc = NULL, .fieldNumber = 100, .dataType = GPBDataTypeMessage, .options = GPBExtensionNone, }, { .defaultValue.valueEnum = EnumBadVersion_Foo, .singletonName = GPBStringifySymbol(TestBadVersionRoot) "_otherE", .extendedClass.clazz = GPBObjCClass(MessageBadVersion), .messageOrGroupClass.clazz = Nil, .enumDescriptorFunc = EnumBadVersion_EnumDescriptor, .fieldNumber = 101, .dataType = GPBDataTypeEnum, .options = GPBExtensionNone, }, }; for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) { GPBExtensionDescriptor *extension = [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i] runtimeSupport:&FAKE_GOOGLE_PROTOBUF_OBJC_EXPECTED_GENCODE_VERSION_40311]; [registry addExtension:extension]; [self globallyRegisterExtension:extension]; [extension release]; } // None of the imports (direct or indirect) defined extensions, so no need to add // them to this registry. } return registry; } @end static GPBFilePackageAndPrefix TestBadVersionRoot_FileDescription = { .package = NULL, .prefix = NULL }; GPBEnumDescriptor *EnumBadVersion_EnumDescriptor(void) { static _Atomic(GPBEnumDescriptor*) descriptor = nil; if (!descriptor) { static const char *valueNames = "Foo\000Bar\000"; static const int32_t values[] = { EnumBadVersion_Foo, EnumBadVersion_Bar, }; GPBEnumDescriptor *worker = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(EnumBadVersion) runtimeSupport:&FAKE_GOOGLE_PROTOBUF_OBJC_EXPECTED_GENCODE_VERSION_40311 valueNames:valueNames values:values count:(uint32_t)(sizeof(values) / sizeof(int32_t)) enumVerifier:EnumBadVersion_IsValidValue flags:GPBEnumDescriptorInitializationFlag_IsClosed]; GPBEnumDescriptor *expected = nil; if (!atomic_compare_exchange_strong(&descriptor, &expected, worker)) { [worker release]; } } return descriptor; } BOOL EnumBadVersion_IsValidValue(int32_t value__) { switch (value__) { case EnumBadVersion_Foo: case EnumBadVersion_Bar: return YES; default: return NO; } } #pragma mark - MessageBadVersion @implementation MessageBadVersion @dynamic hasValue, value; typedef struct MessageBadVersion__storage_ { uint32_t _has_storage_[1]; EnumBadVersion value; } MessageBadVersion__storage_; + (GPBDescriptor *)descriptor { static GPBDescriptor *descriptor = nil; if (!descriptor) { static GPBMessageFieldDescription fields[] = { { .name = "value", .dataTypeSpecific.enumDescFunc = EnumBadVersion_EnumDescriptor, .number = MessageBadVersion_FieldNumber_Value, .hasIndex = 0, .offset = (uint32_t)offsetof(MessageBadVersion__storage_, value), .flags = GPBFieldNone, .dataType = GPBDataTypeEnum, }, }; GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:GPBObjCClass(MessageBadVersion) messageName:@"MessageBadVersion" runtimeSupport:&FAKE_GOOGLE_PROTOBUF_OBJC_EXPECTED_GENCODE_VERSION_40311 fileDescription:&TestBadVersionRoot_FileDescription fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(MessageBadVersion__storage_) flags:GPBDescriptorInitializationFlag_None]; static const GPBExtensionRange ranges[] = { { .start = 100, .end = 536870912 }, }; [localDescriptor setupExtensionRanges:ranges count:(uint32_t)(sizeof(ranges) / sizeof(GPBExtensionRange))]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG descriptor = localDescriptor; } return descriptor; } @end #pragma clang diagnostic pop // NOLINTEND // clang-format on // ------------------------------------------------------------------------------------------------- @implementation MessageBadVersionFormatTest - (void)testMessageBadVersionFormat { // Calling each one should try to start it up and result in a throw for an unknown version marker. // Mostly this shouldn't happen as the symbol should be coming out of the runtime library so // things should result in a link error before getting to the runtime check; this is just an added // safety check. XCTAssertThrowsSpecificNamed(EnumBadVersion_EnumDescriptor(), NSException, NSInternalInconsistencyException); XCTAssertThrowsSpecificNamed([TestBadVersionRoot otherM], NSException, NSInternalInconsistencyException); XCTAssertThrowsSpecificNamed([MessageBadVersion class], NSException, NSInternalInconsistencyException); } @end