Add ObjC helpers for Any WKT.

- Capture the ObjC prefix used when generating the the file.
- Track the containing type on descriptors.
- Mark descriptors where the message class name got a suffix added to it.
- Expose a fullName property on Descriptors.
- Add helpers for packing/unpacking Any messages.
- Bump the ObjC runtime version number. Since we added methods and invoke them
  in the generated code, ensure the code is running against a matching version.
  Otherwise, someone could compile against headers, but run with a framework
  that is older and get unknown selector failures.  This should trip clearer
  messaging.

Fixes https://github.com/google/protobuf/issues/1674
pull/1970/head
Thomas Van Lenten 8 years ago
parent 4bc1657853
commit 337ec3065f
  1. 1
      objectivec/DevTools/compile_testing_protos.sh
  2. 2
      objectivec/GPBBootstrap.h
  3. 9
      objectivec/GPBDescriptor.h
  4. 114
      objectivec/GPBDescriptor.m
  5. 5
      objectivec/GPBDescriptor_PackagePrivate.h
  6. 125
      objectivec/GPBWellKnownTypes.h
  7. 135
      objectivec/GPBWellKnownTypes.m
  8. 29
      objectivec/Tests/GPBDescriptorTests.m
  9. 1
      objectivec/Tests/GPBUnittestProtos.m
  10. 56
      objectivec/Tests/GPBWellKnownTypesTest.m
  11. 2
      objectivec/google/protobuf/Any.pbobjc.h
  12. 1
      objectivec/google/protobuf/Any.pbobjc.m
  13. 2
      objectivec/google/protobuf/Api.pbobjc.h
  14. 1
      objectivec/google/protobuf/Api.pbobjc.m
  15. 2
      objectivec/google/protobuf/Duration.pbobjc.h
  16. 1
      objectivec/google/protobuf/Duration.pbobjc.m
  17. 2
      objectivec/google/protobuf/Empty.pbobjc.h
  18. 1
      objectivec/google/protobuf/Empty.pbobjc.m
  19. 2
      objectivec/google/protobuf/FieldMask.pbobjc.h
  20. 1
      objectivec/google/protobuf/FieldMask.pbobjc.m
  21. 2
      objectivec/google/protobuf/SourceContext.pbobjc.h
  22. 1
      objectivec/google/protobuf/SourceContext.pbobjc.m
  23. 2
      objectivec/google/protobuf/Struct.pbobjc.h
  24. 1
      objectivec/google/protobuf/Struct.pbobjc.m
  25. 2
      objectivec/google/protobuf/Timestamp.pbobjc.h
  26. 1
      objectivec/google/protobuf/Timestamp.pbobjc.m
  27. 2
      objectivec/google/protobuf/Type.pbobjc.h
  28. 1
      objectivec/google/protobuf/Type.pbobjc.m
  29. 2
      objectivec/google/protobuf/Wrappers.pbobjc.h
  30. 1
      objectivec/google/protobuf/Wrappers.pbobjc.m
  31. 37
      src/google/protobuf/compiler/objectivec/objectivec_file.cc
  32. 34
      src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
  33. 4
      src/google/protobuf/compiler/objectivec/objectivec_helpers.h
  34. 13
      src/google/protobuf/compiler/objectivec/objectivec_message.cc

@ -93,6 +93,7 @@ compile_protos() {
# sources can be generated from them.
CORE_PROTO_FILES=(
src/google/protobuf/any_test.proto
src/google/protobuf/unittest_arena.proto
src/google/protobuf/unittest_custom_options.proto
src/google/protobuf/unittest_enormous_descriptor.proto

@ -99,4 +99,4 @@
// regenerated.
//
// Meant to be used internally by generated code.
#define GOOGLE_PROTOBUF_OBJC_GEN_VERSION 30001
#define GOOGLE_PROTOBUF_OBJC_GEN_VERSION 30002

@ -81,6 +81,13 @@ typedef NS_ENUM(uint8_t, GPBFieldType) {
@property(nonatomic, readonly, getter=isWireFormat) BOOL wireFormat;
/** The class of this message. */
@property(nonatomic, readonly) Class messageClass;
/** Containing message descriptor if this message is nested, or nil otherwise. */
@property(readonly, nullable) GPBDescriptor *containingType;
/**
* Fully qualified name for this message (package.message). Can be nil if the
* value is unable to be computed.
*/
@property(readonly, nullable) NSString *fullName;
/**
* Gets the field for the given number.
@ -118,6 +125,8 @@ typedef NS_ENUM(uint8_t, GPBFieldType) {
/** The package declared in the proto file. */
@property(nonatomic, readonly, copy) NSString *package;
/** The objc prefix declared in the proto file. */
@property(nonatomic, readonly, copy, nullable) NSString *objcPrefix;
/** The syntax of the proto file. */
@property(nonatomic, readonly) GPBFileSyntax syntax;

@ -42,8 +42,10 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdirect-ivar-access"
// The address of this variable is used as a key for obj_getAssociatedObject.
// The addresses of these variables are used as keys for objc_getAssociatedObject.
static const char kTextFormatExtraValueKey = 0;
static const char kParentClassNameValueKey = 0;
static const char kClassNameSuffixKey = 0;
// Utility function to generate selectors on the fly.
static SEL SelFromStrings(const char *prefix, const char *middle,
@ -215,10 +217,102 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
extensionRangesCount_ = count;
}
- (void)setupContainingMessageClassName:(const char *)msgClassName {
// Note: Only fetch the class here, can't send messages to it because
// that could cause cycles back to this class within +initialize if
// two messages have each other in fields (i.e. - they build a graph).
NSAssert(objc_getClass(msgClassName), @"Class %s not defined", msgClassName);
NSValue *parentNameValue = [NSValue valueWithPointer:msgClassName];
objc_setAssociatedObject(self, &kParentClassNameValueKey,
parentNameValue,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setupMessageClassNameSuffix:(NSString *)suffix {
if (suffix.length) {
objc_setAssociatedObject(self, &kClassNameSuffixKey,
suffix,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
- (NSString *)name {
return NSStringFromClass(messageClass_);
}
- (GPBDescriptor *)containingType {
NSValue *parentNameValue =
objc_getAssociatedObject(self, &kParentClassNameValueKey);
if (!parentNameValue) {
return nil;
}
const char *parentName = [parentNameValue pointerValue];
Class parentClass = objc_getClass(parentName);
NSAssert(parentClass, @"Class %s not defined", parentName);
return [parentClass descriptor];
}
- (NSString *)fullName {
NSString *className = NSStringFromClass(self.messageClass);
GPBFileDescriptor *file = self.file;
NSString *objcPrefix = file.objcPrefix;
if (objcPrefix && ![className hasPrefix:objcPrefix]) {
NSAssert(0,
@"Class didn't have correct prefix? (%@ - %@)",
className, objcPrefix);
return nil;
}
GPBDescriptor *parent = self.containingType;
NSString *name = nil;
if (parent) {
NSString *parentClassName = NSStringFromClass(parent.messageClass);
// The generator will add _Class to avoid reserved words, drop it.
NSString *suffix = objc_getAssociatedObject(parent, &kClassNameSuffixKey);
if (suffix) {
if (![parentClassName hasSuffix:suffix]) {
NSAssert(0,
@"ParentMessage class didn't have correct suffix? (%@ - %@)",
className, suffix);
return nil;
}
parentClassName =
[parentClassName substringToIndex:(parentClassName.length - suffix.length)];
}
NSString *parentPrefix = [parentClassName stringByAppendingString:@"_"];
if (![className hasPrefix:parentPrefix]) {
NSAssert(0,
@"Class didn't have the correct parent name prefix? (%@ - %@)",
parentPrefix, className);
return nil;
}
name = [className substringFromIndex:parentPrefix.length];
} else {
name = [className substringFromIndex:objcPrefix.length];
}
// The generator will add _Class to avoid reserved words, drop it.
NSString *suffix = objc_getAssociatedObject(self, &kClassNameSuffixKey);
if (suffix) {
if (![name hasSuffix:suffix]) {
NSAssert(0,
@"Message class didn't have correct suffix? (%@ - %@)",
name, suffix);
return nil;
}
name = [name substringToIndex:(name.length - suffix.length)];
}
NSString *prefix = (parent != nil ? parent.fullName : file.package);
NSString *result;
if (prefix.length > 0) {
result = [NSString stringWithFormat:@"%@.%@", prefix, name];
} else {
result = name;
}
return result;
}
- (id)copyWithZone:(NSZone *)zone {
#pragma unused(zone)
return [self retain];
@ -255,12 +349,26 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
@implementation GPBFileDescriptor {
NSString *package_;
NSString *objcPrefix_;
GPBFileSyntax syntax_;
}
@synthesize package = package_;
@synthesize objcPrefix = objcPrefix_;
@synthesize syntax = syntax_;
- (instancetype)initWithPackage:(NSString *)package
objcPrefix:(NSString *)objcPrefix
syntax:(GPBFileSyntax)syntax {
self = [super init];
if (self) {
package_ = [package copy];
objcPrefix_ = [objcPrefix copy];
syntax_ = syntax;
}
return self;
}
- (instancetype)initWithPackage:(NSString *)package
syntax:(GPBFileSyntax)syntax {
self = [super init];
@ -273,6 +381,7 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
- (void)dealloc {
[package_ release];
[objcPrefix_ release];
[super dealloc];
}
@ -416,6 +525,9 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) {
// Extra type specific data.
if (isMessage) {
const char *className = coreDesc->dataTypeSpecific.className;
// Note: Only fetch the class here, can't send messages to it because
// that could cause cycles back to this class within +initialize if
// two messages have each other in fields (i.e. - they build a graph).
msgClass_ = objc_getClass(className);
NSAssert(msgClass_, @"Class %s not defined", className);
} else if (dataType == GPBDataTypeEnum) {

@ -165,10 +165,15 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) {
firstHasIndex:(int32_t)firstHasIndex;
- (void)setupExtraTextInfo:(const char *)extraTextFormatInfo;
- (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count;
- (void)setupContainingMessageClassName:(const char *)msgClassName;
- (void)setupMessageClassNameSuffix:(NSString *)suffix;
@end
@interface GPBFileDescriptor ()
- (instancetype)initWithPackage:(NSString *)package
objcPrefix:(NSString *)objcPrefix
syntax:(GPBFileSyntax)syntax;
- (instancetype)initWithPackage:(NSString *)package
syntax:(GPBFileSyntax)syntax;
@end

@ -37,15 +37,32 @@
#endif
#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
#import <Protobuf/Any.pbobjc.h>
#import <Protobuf/Duration.pbobjc.h>
#import <Protobuf/Timestamp.pbobjc.h>
#else
#import "google/protobuf/Any.pbobjc.h"
#import "google/protobuf/Duration.pbobjc.h"
#import "google/protobuf/Timestamp.pbobjc.h"
#endif
NS_ASSUME_NONNULL_BEGIN
#pragma mark - Errors
/** NSError domain used for errors. */
extern NSString *const GPBWellKnownTypesErrorDomain;
/** Error code for NSError with GPBWellKnownTypesErrorDomain. */
typedef NS_ENUM(NSInteger, GPBWellKnownTypesErrorCode) {
/** The type_url could not be computed for the requested GPBMessage class. */
GPBWellKnownTypesErrorCodeFailedToComputeTypeURL = -100,
/** type_url in a Any doesn’t match that of the requested GPBMessage class. */
GPBWellKnownTypesErrorCodeTypeURLMismatch = -101,
};
#pragma mark - GPBTimestamp
/**
* Category for GPBTimestamp to work with standard Foundation time/date types.
**/
@ -77,6 +94,8 @@ NS_ASSUME_NONNULL_BEGIN
@end
#pragma mark - GPBDuration
/**
* Category for GPBDuration to work with standard Foundation time type.
**/
@ -96,4 +115,110 @@ NS_ASSUME_NONNULL_BEGIN
@end
#pragma mark - GPBAny
/**
* Category for GPBAny to help work with the message within the object.
**/
@interface GPBAny (GBPWellKnownTypes)
/**
* Convenience method to create a GPBAny containing the serialized message.
* This uses type.googleapis.com/ as the type_url's prefix.
*
* @param message The message to be packed into the GPBAny.
* @param errorPtr Pointer to an error that will be populated if something goes
* wrong.
*
* @return A newly configured GPBAny with the given message, or nil on failure.
*/
+ (nullable instancetype)anyWithMessage:(nonnull GPBMessage *)message
error:(NSError **)errorPtr;
/**
* Convenience method to create a GPBAny containing the serialized message.
*
* @param message The message to be packed into the GPBAny.
* @param typeURLPrefix The URL prefix to apply for type_url.
* @param errorPtr Pointer to an error that will be populated if something
* goes wrong.
*
* @return A newly configured GPBAny with the given message, or nil on failure.
*/
+ (nullable instancetype)anyWithMessage:(nonnull GPBMessage *)message
typeURLPrefix:(nonnull NSString *)typeURLPrefix
error:(NSError **)errorPtr;
/**
* Initializes a GPBAny to contain the serialized message. This uses
* type.googleapis.com/ as the type_url's prefix.
*
* @param message The message to be packed into the GPBAny.
* @param errorPtr Pointer to an error that will be populated if something goes
* wrong.
*
* @return A newly configured GPBAny with the given message, or nil on failure.
*/
- (nullable instancetype)initWithMessage:(nonnull GPBMessage *)message
error:(NSError **)errorPtr;
/**
* Initializes a GPBAny to contain the serialized message.
*
* @param message The message to be packed into the GPBAny.
* @param typeURLPrefix The URL prefix to apply for type_url.
* @param errorPtr Pointer to an error that will be populated if something
* goes wrong.
*
* @return A newly configured GPBAny with the given message, or nil on failure.
*/
- (nullable instancetype)initWithMessage:(nonnull GPBMessage *)message
typeURLPrefix:(nonnull NSString *)typeURLPrefix
error:(NSError **)errorPtr;
/**
* Packs the serialized message into this GPBAny. This uses
* type.googleapis.com/ as the type_url's prefix.
*
* @param message The message to be packed into the GPBAny.
* @param errorPtr Pointer to an error that will be populated if something goes
* wrong.
*
* @return Whether the packing was successful or not.
*/
- (BOOL)packWithMessage:(nonnull GPBMessage *)message
error:(NSError **)errorPtr;
/**
* Packs the serialized message into this GPBAny.
*
* @param message The message to be packed into the GPBAny.
* @param typeURLPrefix The URL prefix to apply for type_url.
* @param errorPtr Pointer to an error that will be populated if something
* goes wrong.
*
* @return Whether the packing was successful or not.
*/
- (BOOL)packWithMessage:(nonnull GPBMessage *)message
typeURLPrefix:(nonnull NSString *)typeURLPrefix
error:(NSError **)errorPtr;
/**
* Unpacks the serialized message as if it was an instance of the given class.
*
* @note When checking type_url, the base URL is not checked, only the fully
* qualified name.
*
* @param messageClass The class to use to deserialize the contained message.
* @param errorPtr Pointer to an error that will be populated if something
* goes wrong.
*
* @return An instance of the given class populated with the contained data, or
* nil on failure.
*/
- (nullable GPBMessage *)unpackMessageClass:(Class)messageClass
error:(NSError **)errorPtr;
@end
NS_ASSUME_NONNULL_END

@ -34,6 +34,13 @@
#import "GPBWellKnownTypes.h"
#import "GPBUtilities_PackagePrivate.h"
NSString *const GPBWellKnownTypesErrorDomain =
GPBNSStringifySymbol(GPBWellKnownTypesErrorDomain);
static NSString *kTypePrefixGoogleApisCom = @"type.googleapis.com/";
static NSTimeInterval TimeIntervalSince1970FromSecondsAndNanos(int64_t seconds,
int32_t nanos) {
return seconds + (NSTimeInterval)nanos / 1e9;
@ -48,6 +55,30 @@ static int32_t SecondsAndNanosFromTimeIntervalSince1970(NSTimeInterval time,
return (int32_t)nanos;
}
static NSString *BuildTypeURL(NSString *typeURLPrefix, NSString *fullName) {
if (typeURLPrefix.length == 0) {
return fullName;
}
if ([typeURLPrefix hasSuffix:@"/"]) {
return [typeURLPrefix stringByAppendingString:fullName];
}
return [NSString stringWithFormat:@"%@/%@", typeURLPrefix, fullName];
}
static NSString *ParseTypeFromURL(NSString *typeURLString) {
NSRange range = [typeURLString rangeOfString:@"/" options:NSBackwardsSearch];
if ((range.location == NSNotFound) ||
(NSMaxRange(range) == typeURLString.length)) {
return nil;
}
NSString *result = [typeURLString substringFromIndex:range.location + 1];
return result;
}
#pragma mark - GPBTimestamp
@implementation GPBTimestamp (GBPWellKnownTypes)
- (instancetype)initWithDate:(NSDate *)date {
@ -87,6 +118,8 @@ static int32_t SecondsAndNanosFromTimeIntervalSince1970(NSTimeInterval time,
@end
#pragma mark - GPBDuration
@implementation GPBDuration (GBPWellKnownTypes)
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
@ -113,3 +146,105 @@ static int32_t SecondsAndNanosFromTimeIntervalSince1970(NSTimeInterval time,
}
@end
#pragma mark - GPBAny
@implementation GPBAny (GBPWellKnownTypes)
+ (instancetype)anyWithMessage:(GPBMessage *)message
error:(NSError **)errorPtr {
return [self anyWithMessage:message
typeURLPrefix:kTypePrefixGoogleApisCom
error:errorPtr];
}
+ (instancetype)anyWithMessage:(GPBMessage *)message
typeURLPrefix:(NSString *)typeURLPrefix
error:(NSError **)errorPtr {
return [[[self alloc] initWithMessage:message
typeURLPrefix:typeURLPrefix
error:errorPtr] autorelease];
}
- (instancetype)initWithMessage:(GPBMessage *)message
error:(NSError **)errorPtr {
return [self initWithMessage:message
typeURLPrefix:kTypePrefixGoogleApisCom
error:errorPtr];
}
- (instancetype)initWithMessage:(GPBMessage *)message
typeURLPrefix:(NSString *)typeURLPrefix
error:(NSError **)errorPtr {
self = [self init];
if (self) {
if (![self packWithMessage:message
typeURLPrefix:typeURLPrefix
error:errorPtr]) {
[self release];
self = nil;
}
}
return self;
}
- (BOOL)packWithMessage:(GPBMessage *)message
error:(NSError **)errorPtr {
return [self packWithMessage:message
typeURLPrefix:kTypePrefixGoogleApisCom
error:errorPtr];
}
- (BOOL)packWithMessage:(GPBMessage *)message
typeURLPrefix:(NSString *)typeURLPrefix
error:(NSError **)errorPtr {
NSString *fullName = [message descriptor].fullName;
if (fullName.length == 0) {
if (errorPtr) {
*errorPtr =
[NSError errorWithDomain:GPBWellKnownTypesErrorDomain
code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL
userInfo:nil];
}
return NO;
}
if (errorPtr) {
*errorPtr = nil;
}
self.typeURL = BuildTypeURL(typeURLPrefix, fullName);
self.value = message.data;
return YES;
}
- (GPBMessage *)unpackMessageClass:(Class)messageClass
error:(NSError **)errorPtr {
NSString *fullName = [messageClass descriptor].fullName;
if (fullName.length == 0) {
if (errorPtr) {
*errorPtr =
[NSError errorWithDomain:GPBWellKnownTypesErrorDomain
code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL
userInfo:nil];
}
return nil;
}
NSString *expectedFullName = ParseTypeFromURL(self.typeURL);
if ((expectedFullName == nil) || ![expectedFullName isEqual:fullName]) {
if (errorPtr) {
*errorPtr =
[NSError errorWithDomain:GPBWellKnownTypesErrorDomain
code:GPBWellKnownTypesErrorCodeTypeURLMismatch
userInfo:nil];
}
return nil;
}
// Any is proto3, which means no extensions, so this assumes anything put
// within an any also won't need extensions. A second helper could be added
// if needed.
return [messageClass parseFromData:self.value
error:errorPtr];
}
@end

@ -34,12 +34,41 @@
#import "GPBDescriptor.h"
#import "google/protobuf/Unittest.pbobjc.h"
#import "google/protobuf/UnittestObjc.pbobjc.h"
#import "google/protobuf/Descriptor.pbobjc.h"
@interface DescriptorTests : GPBTestCase
@end
@implementation DescriptorTests
- (void)testDescriptor_containingType {
GPBDescriptor *testAllTypesDesc = [TestAllTypes descriptor];
GPBDescriptor *nestedMessageDesc = [TestAllTypes_NestedMessage descriptor];
XCTAssertNil(testAllTypesDesc.containingType);
XCTAssertNotNil(nestedMessageDesc.containingType);
XCTAssertEqual(nestedMessageDesc.containingType, testAllTypesDesc); // Ptr comparison
}
- (void)testDescriptor_fullName {
GPBDescriptor *testAllTypesDesc = [TestAllTypes descriptor];
XCTAssertEqualObjects(testAllTypesDesc.fullName, @"protobuf_unittest.TestAllTypes");
GPBDescriptor *nestedMessageDesc = [TestAllTypes_NestedMessage descriptor];
XCTAssertEqualObjects(nestedMessageDesc.fullName, @"protobuf_unittest.TestAllTypes.NestedMessage");
// Prefixes removed.
GPBDescriptor *descDesc = [GPBDescriptorProto descriptor];
XCTAssertEqualObjects(descDesc.fullName, @"google.protobuf.DescriptorProto");
GPBDescriptor *descExtRngDesc = [GPBDescriptorProto_ExtensionRange descriptor];
XCTAssertEqualObjects(descExtRngDesc.fullName, @"google.protobuf.DescriptorProto.ExtensionRange");
// Things that get "_Class" added.
GPBDescriptor *pointDesc = [Point_Class descriptor];
XCTAssertEqualObjects(pointDesc.fullName, @"protobuf_unittest.Point");
GPBDescriptor *pointRectDesc = [Point_Rect descriptor];
XCTAssertEqualObjects(pointRectDesc.fullName, @"protobuf_unittest.Point.Rect");
}
- (void)testFieldDescriptor {
GPBDescriptor *descriptor = [TestAllTypes descriptor];

@ -36,6 +36,7 @@
// a descriptor as it doesn't use the classes/enums.
#import "google/protobuf/Descriptor.pbobjc.m"
#import "google/protobuf/AnyTest.pbobjc.m"
#import "google/protobuf/MapProto2Unittest.pbobjc.m"
#import "google/protobuf/MapUnittest.pbobjc.m"
#import "google/protobuf/Unittest.pbobjc.m"

@ -32,6 +32,8 @@
#import <XCTest/XCTest.h>
#import "google/protobuf/AnyTest.pbobjc.h"
// A basically random interval into the future for testing with.
static const NSTimeInterval kFutureOffsetInterval = 15000;
@ -99,4 +101,58 @@ static const NSTimeInterval kTimeAccuracy = 1e-9;
[duration2 release];
}
- (void)testAnyHelpers {
// Set and extract covers most of the code.
TestAny *subMessage = [TestAny message];
subMessage.int32Value = 12345;
TestAny *message = [TestAny message];
NSError *err = nil;
message.anyValue = [GPBAny anyWithMessage:subMessage error:&err];
XCTAssertNil(err);
NSData *data = message.data;
XCTAssertNotNil(data);
TestAny *message2 = [TestAny parseFromData:data error:&err];
XCTAssertNil(err);
XCTAssertNotNil(message2);
XCTAssertTrue(message2.hasAnyValue);
TestAny *subMessage2 =
(TestAny *)[message.anyValue unpackMessageClass:[TestAny class]
error:&err];
XCTAssertNil(err);
XCTAssertNotNil(subMessage2);
XCTAssertEqual(subMessage2.int32Value, 12345);
// NULL errorPtr in the two calls.
message.anyValue = [GPBAny anyWithMessage:subMessage error:NULL];
NSData *data2 = message.data;
XCTAssertEqualObjects(data2, data);
TestAny *subMessage3 =
(TestAny *)[message.anyValue unpackMessageClass:[TestAny class]
error:NULL];
XCTAssertNotNil(subMessage3);
XCTAssertEqualObjects(subMessage2, subMessage3);
// Try to extract wrong type.
GPBTimestamp *wrongMessage =
(GPBTimestamp *)[message.anyValue unpackMessageClass:[GPBTimestamp class]
error:&err];
XCTAssertNotNil(err);
XCTAssertNil(wrongMessage);
XCTAssertEqualObjects(err.domain, GPBWellKnownTypesErrorDomain);
XCTAssertEqual(err.code, GPBWellKnownTypesErrorCodeTypeURLMismatch);
wrongMessage =
(GPBTimestamp *)[message.anyValue unpackMessageClass:[GPBTimestamp class]
error:NULL];
XCTAssertNil(wrongMessage);
}
@end

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -41,6 +41,7 @@ static GPBFileDescriptor *GPBAnyRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -45,6 +45,7 @@ static GPBFileDescriptor *GPBApiRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -41,6 +41,7 @@ static GPBFileDescriptor *GPBDurationRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -41,6 +41,7 @@ static GPBFileDescriptor *GPBEmptyRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -41,6 +41,7 @@ static GPBFileDescriptor *GPBFieldMaskRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -41,6 +41,7 @@ static GPBFileDescriptor *GPBSourceContextRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -42,6 +42,7 @@ static GPBFileDescriptor *GPBStructRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -41,6 +41,7 @@ static GPBFileDescriptor *GPBTimestampRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -45,6 +45,7 @@ static GPBFileDescriptor *GPBTypeRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -13,7 +13,7 @@
#import "GPBProtocolBuffers.h"
#endif
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30002
#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif

@ -41,6 +41,7 @@ static GPBFileDescriptor *GPBWrappersRoot_FileDescriptor(void) {
if (!descriptor) {
GPBDebugCheckRuntimeVersion();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
objcPrefix:@"GPB"
syntax:GPBFileSyntaxProto3];
}
return descriptor;

@ -53,7 +53,7 @@ namespace {
// This is also found in GPBBootstrap.h, and needs to be kept in sync. It
// is the version check done to ensure generated code works with the current
// runtime being used.
const int32 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30001;
const int32 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30002;
const char* kHeaderExtension = ".pbobjc.h";
@ -463,19 +463,22 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
// File descriptor only needed if there are messages to use it.
if (message_generators_.size() > 0) {
string syntax;
map<string, string> vars;
vars["root_class_name"] = root_class_name_;
vars["package"] = file_->package();
vars["objc_prefix"] = FileClassPrefix(file_);
switch (file_->syntax()) {
case FileDescriptor::SYNTAX_UNKNOWN:
syntax = "GPBFileSyntaxUnknown";
vars["syntax"] = "GPBFileSyntaxUnknown";
break;
case FileDescriptor::SYNTAX_PROTO2:
syntax = "GPBFileSyntaxProto2";
vars["syntax"] = "GPBFileSyntaxProto2";
break;
case FileDescriptor::SYNTAX_PROTO3:
syntax = "GPBFileSyntaxProto3";
vars["syntax"] = "GPBFileSyntaxProto3";
break;
}
printer->Print(
printer->Print(vars,
"#pragma mark - $root_class_name$_FileDescriptor\n"
"\n"
"static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n"
@ -483,16 +486,24 @@ void FileGenerator::GenerateSource(io::Printer *printer) {
" // about thread safety of the singleton.\n"
" static GPBFileDescriptor *descriptor = NULL;\n"
" if (!descriptor) {\n"
" GPBDebugCheckRuntimeVersion();\n"
" descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
" syntax:$syntax$];\n"
" GPBDebugCheckRuntimeVersion();\n");
if (vars["objc_prefix"].size() > 0) {
printer->Print(
vars,
" descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
" objcPrefix:@\"$objc_prefix$\"\n"
" syntax:$syntax$];\n");
} else {
printer->Print(
vars,
" descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
" syntax:$syntax$];\n");
}
printer->Print(
" }\n"
" return descriptor;\n"
"}\n"
"\n",
"root_class_name", root_class_name_,
"package", file_->package(),
"syntax", syntax);
"\n");
}
for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin();

@ -210,10 +210,14 @@ const char* const kReservedWordList[] = {
hash_set<string> kReservedWords =
MakeWordsMap(kReservedWordList, GOOGLE_ARRAYSIZE(kReservedWordList));
string SanitizeNameForObjC(const string& input, const string& extension) {
string SanitizeNameForObjC(const string& input,
const string& extension,
string* out_suffix_added) {
if (kReservedWords.count(input) > 0) {
if (out_suffix_added) *out_suffix_added = extension;
return input + extension;
}
if (out_suffix_added) out_suffix_added->clear();
return input;
}
@ -308,6 +312,12 @@ string BaseFileName(const FileDescriptor* file) {
return basename;
}
string FileClassPrefix(const FileDescriptor* file) {
// Default is empty string, no need to check has_objc_class_prefix.
string result = file->options().objc_class_prefix();
return result;
}
string FilePath(const FileDescriptor* file) {
string output;
string basename;
@ -338,19 +348,13 @@ string FilePathBasename(const FileDescriptor* file) {
return output;
}
string FileClassPrefix(const FileDescriptor* file) {
// Default is empty string, no need to check has_objc_class_prefix.
string result = file->options().objc_class_prefix();
return result;
}
string FileClassName(const FileDescriptor* file) {
string name = FileClassPrefix(file);
name += UnderscoresToCamelCase(StripProto(BaseFileName(file)), true);
name += "Root";
// There aren't really any reserved words that end in "Root", but playing
// it safe and checking.
return SanitizeNameForObjC(name, "_RootClass");
return SanitizeNameForObjC(name, "_RootClass", NULL);
}
string ClassNameWorker(const Descriptor* descriptor) {
@ -372,11 +376,15 @@ string ClassNameWorker(const EnumDescriptor* descriptor) {
}
string ClassName(const Descriptor* descriptor) {
return ClassName(descriptor, NULL);
}
string ClassName(const Descriptor* descriptor, string* out_suffix_added) {
// 1. Message names are used as is (style calls for CamelCase, trust it).
// 2. Check for reserved word at the very end and then suffix things.
string prefix = FileClassPrefix(descriptor->file());
string name = ClassNameWorker(descriptor);
return SanitizeNameForObjC(prefix + name, "_Class");
return SanitizeNameForObjC(prefix + name, "_Class", out_suffix_added);
}
string EnumName(const EnumDescriptor* descriptor) {
@ -390,7 +398,7 @@ string EnumName(const EnumDescriptor* descriptor) {
// yields Fixed_Class, Fixed_Size.
string name = FileClassPrefix(descriptor->file());
name += ClassNameWorker(descriptor);
return SanitizeNameForObjC(name, "_Enum");
return SanitizeNameForObjC(name, "_Enum", NULL);
}
string EnumValueName(const EnumValueDescriptor* descriptor) {
@ -405,7 +413,7 @@ string EnumValueName(const EnumValueDescriptor* descriptor) {
const string& name = class_name + "_" + value_str;
// There aren't really any reserved words with an underscore and a leading
// capital letter, but playing it safe and checking.
return SanitizeNameForObjC(name, "_Value");
return SanitizeNameForObjC(name, "_Value", NULL);
}
string EnumValueShortName(const EnumValueDescriptor* descriptor) {
@ -442,7 +450,7 @@ string UnCamelCaseEnumShortName(const string& name) {
string ExtensionMethodName(const FieldDescriptor* descriptor) {
const string& name = NameFromFieldDescriptor(descriptor);
const string& result = UnderscoresToCamelCase(name, false);
return SanitizeNameForObjC(result, "_Extension");
return SanitizeNameForObjC(result, "_Extension", NULL);
}
string FieldName(const FieldDescriptor* field) {
@ -457,7 +465,7 @@ string FieldName(const FieldDescriptor* field) {
result += "_p";
}
}
return SanitizeNameForObjC(result, "_p");
return SanitizeNameForObjC(result, "_p", NULL);
}
string FieldNameCapitalized(const FieldDescriptor* field) {

@ -67,6 +67,9 @@ bool IsRetainedName(const string& name);
// handling under ARC.
bool IsInitName(const string& name);
// Gets the objc_class_prefix.
string FileClassPrefix(const FileDescriptor* file);
// Gets the path of the file we're going to generate (sans the .pb.h
// extension). The path will be dependent on the objectivec package
// declared in the proto package.
@ -83,6 +86,7 @@ string FileClassName(const FileDescriptor* file);
// These return the fully-qualified class name corresponding to the given
// descriptor.
string ClassName(const Descriptor* descriptor);
string ClassName(const Descriptor* descriptor, string* out_suffix_added);
string EnumName(const EnumDescriptor* descriptor);
// Returns the fully-qualified name of the enum value corresponding to the

@ -579,6 +579,19 @@ void MessageGenerator::GenerateSource(io::Printer* printer) {
" [localDescriptor setupExtensionRanges:ranges\n"
" count:(uint32_t)(sizeof(ranges) / sizeof(GPBExtensionRange))];\n");
}
if (descriptor_->containing_type() != NULL) {
string parent_class_name = ClassName(descriptor_->containing_type());
printer->Print(
" [localDescriptor setupContainingMessageClassName:GPBStringifySymbol($parent_name$)];\n",
"parent_name", parent_class_name);
}
string suffix_added;
ClassName(descriptor_, &suffix_added);
if (suffix_added.size() > 0) {
printer->Print(
" [localDescriptor setupMessageClassNameSuffix:@\"$suffix$\"];\n",
"suffix", suffix_added);
}
printer->Print(
" NSAssert(descriptor == nil, @\"Startup recursed!\");\n"
" descriptor = localDescriptor;\n"

Loading…
Cancel
Save