Commit 1dcc3294 authored by Thomas Van Lenten's avatar Thomas Van Lenten

Objective C Second Alpha Drop

- Style fixups in the code.
- map<> serialization fixes and more tests.
- Autocreation of map<> fields (to match repeated fields).
- @@protoc_insertion_point(global_scope|imports).
- Fixup proto2 syntax extension support.
- Move all startup code to +initialize so it happen on class usage and not app startup.
- Have generated headers use forward declarations and move imports into generated code, reduces what is need at compile time to speed up compiled and avoid pointless rippling of rebuilds.
parent c3480926
......@@ -198,9 +198,10 @@ javanano_EXTRA_DIST=
javanano/pom.xml
objectivec_EXTRA_DIST= \
objectivec/DevTools/generate_descriptors_proto.sh \
objectivec/DevTools/check_version_stamps.sh \
objectivec/DevTools/pddm.py \
objectivec/DevTools/pddm_tests.py \
objectivec/generate_descriptors_proto.sh \
objectivec/google/protobuf/Descriptor.pbobjc.h \
objectivec/google/protobuf/Descriptor.pbobjc.m \
objectivec/google/protobuf/Duration.pbobjc.h \
......@@ -227,7 +228,6 @@ objectivec_EXTRA_DIST= \
objectivec/GPBExtensionField_PackagePrivate.h \
objectivec/GPBExtensionRegistry.h \
objectivec/GPBExtensionRegistry.m \
objectivec/GPBExtensionRegistry_PackagePrivate.h \
objectivec/GPBField.h \
objectivec/GPBField.m \
objectivec/GPBField_PackagePrivate.h \
......
......@@ -23,7 +23,7 @@ AC_CONFIG_MACRO_DIR([m4])
AC_ARG_VAR(DIST_LANG, [language to include in the distribution package (i.e., make dist)])
case "$DIST_LANG" in
"") DIST_LANG=all ;;
all | cpp | java | python | javanano | ruby) ;;
all | cpp | java | python | javanano | objectivec | ruby) ;;
*) AC_MSG_FAILURE([unknown language: $DIST_LANG]) ;;
esac
AC_SUBST(DIST_LANG)
......
......@@ -29,7 +29,7 @@ readonly PluginVersion=$( \
)
if [[ -z "${PluginVersion}" ]] ; then
die "Failed to fine ${ConstantName} in the plugin source (${PluginSrc})."
die "Failed to find ${ConstantName} in the plugin source (${PluginSrc})."
fi
# Collect version from runtime sources.
......@@ -41,7 +41,7 @@ readonly RuntimeVersion=$( \
)
if [[ -z "${RuntimeVersion}" ]] ; then
die "Failed to fine ${ConstantName} in the runtime source (${RuntimeSrc})."
die "Failed to find ${ConstantName} in the runtime source (${RuntimeSrc})."
fi
# Compare them.
......
#!/bin/bash
#
# Helper to do build so you don't have to remember all the steps/args.
set -eu
# Some base locations.
readonly ScriptDir=$(dirname "$(echo $0 | sed -e "s,^\([^/]\),$(pwd)/\1,")")
readonly ProtoRootDir="${ScriptDir}/../.."
printUsage() {
NAME=$(basename "${0}")
cat << EOF
usage: ${NAME} [OPTIONS]
This script does the common build steps needed.
OPTIONS:
General:
-h, --help
Show this message
-c, --clean
Issue a clean before the normal build.
-a, --autogen
Start by rerunning autogen & configure.
-r, --regenerate-descriptors
The descriptor.proto is checked in generated, cause it to regenerate.
-j #, --jobs #
Force the number of parallel jobs (useful for debugging build issues).
--skip-xcode
Skip the invoke of Xcode to test the runtime on both iOS and OS X.
--skip-xcode-ios
Skip the invoke of Xcode to test the runtime on iOS.
--skip-xcode-osx
Skip the invoke of Xcode to test the runtime on OS X.
EOF
}
header() {
echo ""
echo "========================================================================"
echo " ${@}"
echo "========================================================================"
}
# Thanks to libtool, builds can fail in odd ways and since it eats some output
# it can be hard to spot, so force error output if make exits with a non zero.
wrapped_make() {
set +e # Don't stop if the command fails.
make $*
MAKE_EXIT_STATUS=$?
if [ ${MAKE_EXIT_STATUS} -ne 0 ]; then
echo "Error: 'make $*' exited with status ${MAKE_EXIT_STATUS}"
exit ${MAKE_EXIT_STATUS}
fi
set -e
}
NUM_MAKE_JOBS=$(/usr/sbin/sysctl -n hw.ncpu)
if [[ "${NUM_MAKE_JOBS}" -lt 4 ]] ; then
NUM_MAKE_JOBS=4
fi
DO_AUTOGEN=no
DO_CLEAN=no
REGEN_CPP_DESCRIPTORS=no
DO_XCODE_IOS_TESTS=yes
DO_XCODE_OSX_TESTS=yes
while [[ $# != 0 ]]; do
case "${1}" in
-h | --help )
printUsage
exit 0
;;
-c | --clean )
DO_CLEAN=yes
;;
-a | --autogen )
DO_AUTOGEN=yes
;;
-r | --regenerate-cpp-descriptors )
REGEN_CPP_DESCRIPTORS=yes
;;
-j | --jobs )
shift
NUM_MAKE_JOBS="${1}"
;;
--skip-xcode )
DO_XCODE_IOS_TESTS=no
DO_XCODE_OSX_TESTS=no
;;
--skip-xcode-ios )
DO_XCODE_IOS_TESTS=no
;;
--skip-xcode-osx )
DO_XCODE_OSX_TESTS=no
;;
-*)
echo "ERROR: Unknown option: ${1}" 1>&2
printUsage
exit 1
;;
*)
echo "ERROR: Unknown argument: ${1}" 1>&2
printUsage
exit 1
;;
esac
shift
done
# Into the proto dir.
pushd "${ProtoRootDir}"
# if no Makefile, force the autogen.
if [[ ! -f Makefile ]] ; then
DO_AUTOGEN=yes
fi
if [[ "${DO_AUTOGEN}" == "yes" ]] ; then
header "Running autogen & configure"
./autogen.sh
./configure CXXFLAGS="-mmacosx-version-min=10.9 -Wnon-virtual-dtor -Woverloaded-virtual -Wunused-const-variable -Wunused-function"
fi
if [[ "${DO_CLEAN}" == "yes" ]] ; then
header "Cleaning"
wrapped_make clean
if [[ "${DO_XCODE_IOS_TESTS}" == "yes" ]] ; then
XCODEBUILD_CLEAN_BASE_IOS=(
xcodebuild
-project objectivec/ProtocolBuffers_iOS.xcodeproj
-scheme ProtocolBuffers
)
"${XCODEBUILD_CLEAN_BASE_IOS[@]}" -configuration Debug clean
"${XCODEBUILD_CLEAN_BASE_IOS[@]}" -configuration Release clean
fi
if [[ "${DO_XCODE_OSX_TESTS}" == "yes" ]] ; then
XCODEBUILD_CLEAN_BASE_OSX=(
xcodebuild
-project objectivec/ProtocolBuffers_OSX.xcodeproj
-scheme ProtocolBuffers
)
"${XCODEBUILD_CLEAN_BASE_OSX[@]}" -configuration Debug clean
"${XCODEBUILD_CLEAN_BASE_OSX[@]}" -configuration Release clean
fi
fi
if [[ "${REGEN_CPP_DESCRIPTORS}" == "yes" ]] ; then
header "Regenerating the C++ descriptor sources."
./generate_descriptor_proto.sh -j "${NUM_MAKE_JOBS}"
fi
header "Building"
# Can't issue these together, when fully parallel, something sometimes chokes
# at random.
wrapped_make -j "${NUM_MAKE_JOBS}" all
wrapped_make -j "${NUM_MAKE_JOBS}" check
header "Ensuring the ObjC descriptors are current."
# Find the newest input file (protos, compiler, and this script).
# (these patterns catch some extra stuff, but better to over sample than under)
readonly NewestInput=$(find \
src/google/protobuf/*.proto \
src/.libs src/*.la src/protoc \
objectivec/generate_descriptors_proto.sh \
-type f -print0 \
| xargs -0 stat -f "%m %N" \
| sort -n | tail -n1 | cut -f2- -d" ")
# Find the oldest output file.
readonly OldestOutput=$(find \
"${ProtoRootDir}/objectivec/google" \
-type f -print0 \
| xargs -0 stat -f "%m %N" \
| sort -n -r | tail -n1 | cut -f2- -d" ")
# If the newest input is newer than the oldest output, regenerate.
if [[ "${NewestInput}" -nt "${OldestOutput}" ]] ; then
echo ">> Newest input is newer than oldest output, regenerating."
objectivec/generate_descriptors_proto.sh -j "${NUM_MAKE_JOBS}"
else
echo ">> Newest input is older than oldest output, no need to regenerating."
fi
header "Checking on the ObjC Runtime Code"
objectivec/DevTools/pddm_tests.py
if ! objectivec/DevTools/pddm.py --dry-run objectivec/*.[hm] objectivec/Tests/*.[hm] ; then
echo ""
echo "Update by running:"
echo " objectivec/DevTools/pddm.py objectivec/*.[hm] objectivec/Tests/*.[hm]"
exit 1
fi
if [[ "${DO_XCODE_IOS_TESTS}" == "yes" ]] ; then
XCODEBUILD_TEST_BASE_IOS=(
xcodebuild
-project objectivec/ProtocolBuffers_iOS.xcodeproj
-scheme ProtocolBuffers
# Don't need to worry about form factors or retina/non retina;
# just pick a mix of OS Versions and 32/64 bit.
-destination "platform=iOS Simulator,name=iPhone 4s,OS=7.1" # 32bit
-destination "platform=iOS Simulator,name=iPhone 6,OS=8.3" # 64bit
-destination "platform=iOS Simulator,name=iPad 2,OS=7.1" # 32bit
-destination "platform=iOS Simulator,name=iPad Air,OS=8.3" # 64bit
)
header "Doing Xcode iOS build/tests - Debug"
"${XCODEBUILD_TEST_BASE_IOS[@]}" -configuration Debug test
header "Doing Xcode iOS build/tests - Release"
"${XCODEBUILD_TEST_BASE_IOS[@]}" -configuration Release test
# Don't leave the simulator in the developer's face.
killall "iOS Simulator"
fi
if [[ "${DO_XCODE_OSX_TESTS}" == "yes" ]] ; then
XCODEBUILD_TEST_BASE_OSX=(
xcodebuild
-project objectivec/ProtocolBuffers_OSX.xcodeproj
-scheme ProtocolBuffers
# Since the ObjC 2.0 Runtime is required, 32bit OS X isn't supported.
-destination "platform=OS X,arch=x86_64" # 64bit
)
header "Doing Xcode OS X build/tests - Debug"
"${XCODEBUILD_TEST_BASE_OSX[@]}" -configuration Debug test
header "Doing Xcode OS X build/tests - Release"
"${XCODEBUILD_TEST_BASE_OSX[@]}" -configuration Release test
fi
This diff is collapsed.
......@@ -51,11 +51,11 @@
// the Swift bridge will have one where the names line up to support short
// names since they are scoped to the enum.
// https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-XID_11
#define GPB_ENUM(X) enum X##_ : int32_t X; typedef NS_ENUM(int32_t, X##_)
// GPB_ENUM_FWD_DECLARE is used for forward declaring enums ex:
#define GPB_ENUM(X) NS_ENUM(int32_t, X)
// GPB_ENUM_FWD_DECLARE is used for forward declaring enums, ex:
// GPB_ENUM_FWD_DECLARE(Foo_Enum)
// @property (nonatomic) Foo_Enum value;
#define GPB_ENUM_FWD_DECLARE(_name) enum _name : int32_t
#define GPB_ENUM_FWD_DECLARE(X) enum X : int32_t
// Based upon CF_INLINE. Forces inlining in release.
#if !defined(DEBUG)
......
......@@ -66,7 +66,9 @@
- (void)readMessage:(GPBMessage *)message
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
// Reads and discards a single field, given its tag value.
// Reads and discards a single field, given its tag value. Returns NO if the
// tag is an endgroup tag, in which case nothing is skipped. Otherwise,
// returns YES.
- (BOOL)skipField:(int32_t)tag;
// Reads and discards an entire message. This will read either until EOF
......@@ -74,8 +76,8 @@
- (void)skipMessage;
// Verifies that the last call to readTag() returned the given tag value.
// This is used to verify that a nested group ended with the correct
// end tag.
// This is used to verify that a nested group ended with the correct end tag.
// Throws NSParseErrorException if value does not match the last tag.
- (void)checkLastTagWas:(int32_t)value;
@end
......@@ -56,7 +56,6 @@ typedef NS_ENUM(NSInteger, GPBFieldType) {
@property(nonatomic, readonly, strong) NSArray *fields;
@property(nonatomic, readonly, strong) NSArray *oneofs;
@property(nonatomic, readonly, strong) NSArray *enums;
@property(nonatomic, readonly, strong) NSArray *extensions;
@property(nonatomic, readonly) const GPBExtensionRange *extensionRanges;
@property(nonatomic, readonly) NSUInteger extensionRangesCount;
@property(nonatomic, readonly, assign) GPBFileDescriptor *file;
......@@ -68,8 +67,6 @@ typedef NS_ENUM(NSInteger, GPBFieldType) {
- (GPBFieldDescriptor *)fieldWithName:(NSString *)name;
- (GPBOneofDescriptor *)oneofWithName:(NSString *)name;
- (GPBEnumDescriptor *)enumWithName:(NSString *)name;
- (GPBFieldDescriptor *)extensionWithNumber:(uint32_t)fieldNumber;
- (GPBFieldDescriptor *)extensionWithName:(NSString *)name;
@end
......
......@@ -93,7 +93,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
@implementation GPBDescriptor {
Class messageClass_;
NSArray *enums_;
NSArray *extensions_;
GPBFileDescriptor *file_;
BOOL wireFormat_;
}
......@@ -102,7 +101,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
@synthesize fields = fields_;
@synthesize oneofs = oneofs_;
@synthesize enums = enums_;
@synthesize extensions = extensions_;
@synthesize extensionRanges = extensionRanges_;
@synthesize extensionRangesCount = extensionRangesCount_;
@synthesize file = file_;
......@@ -161,13 +159,11 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
[enums addObject:enumDescriptor];
}
// TODO(dmaclach): Add support for extensions
GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass
file:file
fields:fields
oneofs:oneofs
enums:enums
extensions:nil
extensionRanges:ranges
extensionRangesCount:rangeCount
storageSize:storageSize
......@@ -226,7 +222,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
fields:(NSArray *)fields
oneofs:(NSArray *)oneofs
enums:(NSArray *)enums
extensions:(NSArray *)extensions
extensionRanges:(const GPBExtensionRange *)extensionRanges
extensionRangesCount:(NSUInteger)extensionRangesCount
storageSize:(size_t)storageSize
......@@ -237,7 +232,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
fields_ = [fields retain];
oneofs_ = [oneofs retain];
enums_ = [enums retain];
extensions_ = [extensions retain];
extensionRanges_ = extensionRanges;
extensionRangesCount_ = extensionRangesCount;
storageSize_ = storageSize;
......@@ -250,7 +244,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
[fields_ release];
[oneofs_ release];
[enums_ release];
[extensions_ release];
[super dealloc];
}
......@@ -299,24 +292,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
return nil;
}
- (GPBFieldDescriptor *)extensionWithNumber:(uint32_t)fieldNumber {
for (GPBFieldDescriptor *descriptor in extensions_) {
if (GPBFieldNumber(descriptor) == fieldNumber) {
return descriptor;
}
}
return nil;
}
- (GPBFieldDescriptor *)extensionWithName:(NSString *)name {
for (GPBFieldDescriptor *descriptor in extensions_) {
if ([descriptor.name isEqual:name]) {
return descriptor;
}
}
return nil;
}
@end
@implementation GPBFileDescriptor {
......@@ -366,7 +341,7 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
}
- (NSString *)name {
return [NSString stringWithUTF8String:oneofDescription_->name];
return @(oneofDescription_->name);
}
- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
......@@ -455,7 +430,8 @@ uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
freeWhenDone:NO];
GPBExtensionRegistry *registry = [rootClass extensionRegistry];
fieldOptions_ = [[GPBFieldOptions parseFromData:optionsData
extensionRegistry:registry] retain];
extensionRegistry:registry
error:NULL] retain];
}
}
......@@ -532,7 +508,7 @@ uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
}
- (NSString *)name {
return [NSString stringWithUTF8String:description_->name];
return @(description_->name);
}
- (BOOL)isRequired {
......@@ -809,7 +785,7 @@ uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
NSString *result = nil;
// Naming adds an underscore between enum name and value name, skip that also.
NSString *shortName = [NSString stringWithUTF8String:valueDescriptor->name];
NSString *shortName = @(valueDescriptor->name);
// See if it is in the map of special format handling.
if (extraTextFormatInfo_) {
......@@ -846,7 +822,7 @@ uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
}
- (NSString *)singletonName {
return [NSString stringWithUTF8String:description_->singletonName];
return @(description_->singletonName);
}
- (const char *)singletonNameC {
......
......@@ -186,7 +186,6 @@ typedef struct GPBExtensionDescription {
fields:(NSArray *)fields
oneofs:(NSArray *)oneofs
enums:(NSArray *)enums
extensions:(NSArray *)extensions
extensionRanges:(const GPBExtensionRange *)ranges
extensionRangesCount:(NSUInteger)rangeCount
storageSize:(size_t)storage
......
This diff is collapsed.
This diff is collapsed.
......@@ -38,7 +38,24 @@
// ExtensionRegistry in which you have registered any extensions that you want
// to be able to parse. Otherwise, those extensions will just be treated like
// unknown fields.
@interface GPBExtensionRegistry : NSObject
//
// The *Root classes provide +extensionRegistry for the extensions defined in a
// given file *and* all files it imports. You can also create a
// GPBExtensionRegistry, and merge those registries to handle parsing extensions
// defined from non overlapping files.
//
// GPBExtensionRegistry *registry =
// [[[MyProtoFileRoot extensionRegistry] copy] autorelease];
// [registry addExtension:[OtherMessage neededExtension]; // Not in MyProtoFile
// NSError *parseError = nil;
// MyMessage *msg = [MyMessage parseData:data
// extensionRegistry:registry
// error:&parseError];
//
@interface GPBExtensionRegistry : NSObject<NSCopying>
- (void)addExtension:(GPBExtensionField *)extension;
- (void)addExtensions:(GPBExtensionRegistry *)registry;
- (GPBExtensionField *)getExtension:(GPBDescriptor *)containingType
fieldNumber:(NSInteger)fieldNumber;
......
......@@ -28,7 +28,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import "GPBExtensionRegistry_PackagePrivate.h"
#import "GPBExtensionRegistry.h"
#import "GPBBootstrap.h"
#import "GPBDescriptor.h"
......@@ -52,6 +52,14 @@
[super dealloc];
}
- (instancetype)copyWithZone:(NSZone *)zone {
GPBExtensionRegistry *result = [[[self class] allocWithZone:zone] init];
if (result && mutableClassMap_.count) {
[result->mutableClassMap_ addEntriesFromDictionary:mutableClassMap_];
}
return result;
}
- (NSMutableDictionary *)extensionMapForContainingType:
(GPBDescriptor *)containingType {
NSMutableDictionary *extensionMap =
......
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/Foundation.h>
#import "GPBExtensionRegistry.h"
@interface GPBExtensionRegistry ()
- (void)addExtension:(GPBExtensionField *)extension;
- (void)addExtensions:(GPBExtensionRegistry *)registry;
@end
......@@ -28,15 +28,28 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import "GPBRootObject.h"
#import <Foundation/Foundation.h>
#import "GPBBootstrap.h"
@class GPBDescriptor;
@class GPBCodedInputStream;
@class GPBCodedOutputStream;
@class GPBExtensionField;
@class GPBExtensionRegistry;
@class GPBFieldDescriptor;
@class GPBUnknownFieldSet;
CF_EXTERN_C_BEGIN
// NSError domain used for errors.
extern NSString *const GPBMessageErrorDomain;
typedef NS_ENUM(NSInteger, GPBMessageErrorCode) {
GPBMessageErrorCodeMalformedData = -100,
GPBMessageErrorCodeMissingRequiredField = -101,
};
// In DEBUG ONLY, an NSException is thrown when a parsed message doesn't
// contain required fields. This key allows you to retrieve the parsed message
// from the exception's |userInfo| dictionary.
......@@ -44,12 +57,14 @@
extern NSString *const GPBExceptionMessageKey;
#endif // DEBUG
// NOTE:
// If you add a instance method/property to this class that may conflict with
// methods declared in protos, you need to update objective_helpers.cc.
CF_EXTERN_C_END
@interface GPBMessage : NSObject<NSSecureCoding, NSCopying>
// NOTE: If you add a instance method/property to this class that may conflict
// with methods declared in protos, you need to update objective_helpers.cc.
// The main cases are methods that take no arguments, or setFoo:/hasFoo: type
// methods.
@interface GPBMessage : GPBRootObject<NSCoding, NSCopying>
@property(nonatomic, readonly) GPBUnknownFieldSet *unknownFields;
......@@ -59,29 +74,38 @@ extern NSString *const GPBExceptionMessageKey;
// Returns an autoreleased instance.
+ (instancetype)message;
// Create a message based on a variety of inputs.
// In DEBUG ONLY
// @throws NSInternalInconsistencyException The message is missing one or more
// required fields (i.e. -[isInitialized] returns false). Use
// GGPBExceptionMessageKey to retrieve the message from |userInfo|.
+ (instancetype)parseFromData:(NSData *)data;
// Create a message based on a variety of inputs. If there is a data parse
// error, nil is returned and if not NULL, errorPtr is filled in.
// NOTE: In DEBUG ONLY, the message is also checked for all required field,
// if one is missing, the parse will fail (returning nil, filling in errorPtr).
+ (instancetype)parseFromData:(NSData *)data error:(NSError **)errorPtr;
+ (instancetype)parseFromData:(NSData *)data
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
error:(NSError **)errorPtr;
+ (instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input
extensionRegistry:
(GPBExtensionRegistry *)extensionRegistry;
(GPBExtensionRegistry *)extensionRegistry
error:(NSError **)errorPtr;
// Create a message based on delimited input.
// Create a message based on delimited input. If there is a data parse
// error, nil is returned and if not NULL, errorPtr is filled in.
+ (instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
extensionRegistry:
(GPBExtensionRegistry *)extensionRegistry;
- (instancetype)initWithData:(NSData *)data;
(GPBExtensionRegistry *)extensionRegistry
error:(NSError **)errorPtr;
// If there is a data parse error, nil is returned and if not NULL, errorPtr is
// filled in.
// NOTE: In DEBUG ONLY, the message is also checked for all required field,
// if one is missing, the parse will fail (returning nil, filling in errorPtr).
- (instancetype)initWithData:(NSData *)data error:(NSError **)errorPtr;
- (instancetype)initWithData:(NSData *)data
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
error:(NSError **)errorPtr;
- (instancetype)initWithCodedInputStream:(GPBCodedInputStream *)input
extensionRegistry:
(GPBExtensionRegistry *)extensionRegistry;
(GPBExtensionRegistry *)extensionRegistry
error:(NSError **)errorPtr;
// Serializes the message and writes it to output.
- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output;
......@@ -93,11 +117,10 @@ extern NSString *const GPBExceptionMessageKey;
- (void)writeDelimitedToOutputStream:(NSOutputStream *)output;
// Serializes the message to an NSData. Note that this value is not cached, so
// if you are using it repeatedly, cache it yourself.
// In DEBUG ONLY:
// @throws NSInternalInconsistencyException The message is missing one or more
// required fields (i.e. -[isInitialized] returns false). Use
// GPBExceptionMessageKey to retrieve the message from |userInfo|.
// if you are using it repeatedly, cache it yourself. If there is an error
// while generating the data, nil is returned.
// NOTE: In DEBUG ONLY, the message is also checked for all required field,
// if one is missing, nil will be returned.
- (NSData *)data;
// Same as -[data], except a delimiter is added to the start of the data
......@@ -106,16 +129,16 @@ extern NSString *const GPBExceptionMessageKey;
// Returns the size of the object if it were serialized.
// This is not a cached value. If you are following a pattern like this:
// size_t size = [aMsg serializedSize];
// NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
// [foo writeSize:size];
// [foo appendData:[aMsg data]];
// size_t size = [aMsg serializedSize];
// NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
// [foo writeSize:size];
// [foo appendData:[aMsg data]];
// you would be better doing:
// NSData *data = [aMsg data];
// NSUInteger size = [aMsg length];
// NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
// [foo writeSize:size];
// [foo appendData:data];
// NSData *data = [aMsg data];
// NSUInteger size = [aMsg length];
// NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
// [foo writeSize:size];
// [foo appendData:data];
- (size_t)serializedSize;
// Return the descriptor for the message
......@@ -123,8 +146,8 @@ extern NSString *const GPBExceptionMessageKey;
- (GPBDescriptor *)descriptor;
// Extensions use boxed values (NSNumbers) for PODs, NSMutableArrays for
// repeated. If the extension is a Message, just like fields, one will be
// auto created for you and returned.
// repeated. If the extension is a Message one will be auto created for you
// and returned similar to fields.
- (BOOL)hasExtension:(GPBExtensionField *)extension;
- (id)getExtension:(GPBExtensionField *)extension;
- (void)setExtension:(GPBExtensionField *)extension value:(id)value;
......@@ -141,6 +164,7 @@ extern NSString *const GPBExceptionMessageKey;
// Parses a message of this type from the input and merges it with this
// message.
// NOTE: This will throw if there is an error parsing the data.
- (void)mergeFromData:(NSData *)data
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
......
This diff is collapsed.
......@@ -82,6 +82,7 @@ typedef struct GPBMessage_Storage *GPBMessage_StoragePtr;
// -[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:(GPBExtensionRegistry *)extensionRegistry;
......@@ -113,9 +114,10 @@ BOOL GPBWasMessageAutocreatedBy(GPBMessage *message, GPBMessage *parent);
// visible to its autocreator.
void GPBBecomeVisibleToAutocreator(GPBMessage *self);
// Call this when an array is mutabled so the parent message that autocreated
// it can react.
// Call this when an array/dictionary is mutated so the parent message that
// autocreated it can react.
void GPBAutocreatedArrayModified(GPBMessage *self, id array);
void GPBAutocreatedDictionaryModified(GPBMessage *self, id dictionary);
// Clear the autocreator, if any. Asserts if the autocreator still has an
// autocreated reference to this message.
......
......@@ -35,7 +35,7 @@
#import "GPBDescriptor_PackagePrivate.h"
#import "GPBExtensionField_PackagePrivate.h"
#import "GPBExtensionRegistry_PackagePrivate.h"
#import "GPBExtensionRegistry.h"
#import "GPBMessage_PackagePrivate.h"
#import "GPBRootObject_PackagePrivate.h"
#import "GPBUtilities_PackagePrivate.h"
......@@ -31,6 +31,7 @@
#import "GPBRootObject_PackagePrivate.h"
#import <objc/runtime.h>
#import <libkern/OSAtomic.h>
#import <CoreFoundation/CoreFoundation.h>
......@@ -95,9 +96,11 @@ static CFHashCode GPBRootExtensionKeyHash(const void *value) {
return jenkins_one_at_a_time_hash(key);
}
static OSSpinLock gExtensionSingletonDictionaryLock_ = OS_SPINLOCK_INIT;
static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL;
+ (void)initialize {
// Ensure the global is started up.
if (!gExtensionSingletonDictionary) {
CFDictionaryKeyCallBacks keyCallBacks = {
// See description above for reason for using custom dictionary.
......@@ -112,6 +115,13 @@ static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL;
CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
if ([self superclass] == [GPBRootObject class]) {
// This is here to start up all the per file "Root" subclasses.
// This must be done in initialize to enforce thread safety of start up of
// the protocol buffer library.
[self extensionRegistry];
}
}
+ (GPBExtensionRegistry *)extensionRegistry {
......@@ -122,39 +132,54 @@ static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL;
+ (void)globallyRegisterExtension:(GPBExtensionField *)field {
const char *key = [field.descriptor singletonNameC];
// Register happens at startup, so there is no thread safety issue in
// modifying the dictionary.
OSSpinLockLock(&gExtensionSingletonDictionaryLock_);
CFDictionarySetValue(gExtensionSingletonDictionary, key, field);
OSSpinLockUnlock(&gExtensionSingletonDictionaryLock_);
}
static id ExtensionForName(id self, SEL _cmd) {
GPB_INLINE id ExtensionForName(id self, SEL _cmd) {
// Really fast way of doing "classname_selName".
// This came up as a hotspot (creation of NSString *) when accessing a
// lot of extensions.
const char *className = class_getName(self);
const char *selName = sel_getName(_cmd);
if (selName[0] == '_') {
return nil; // Apple internal selector.
}
size_t selNameLen = 0;
while (1) {
char c = selName[selNameLen];
if (c == '\0') { // String end.
break;
}
if (c == ':') {
return nil; // Selector took an arg, not one of the runtime methods.
}
++selNameLen;
}
const char *className = class_getName(self);
size_t classNameLen = strlen(className);
size_t selNameLen = strlen(selName);
char key[classNameLen + selNameLen + 2];
memcpy(key, className, classNameLen);
key[classNameLen] = '_';
memcpy(&key[classNameLen + 1], selName, selNameLen);
key[classNameLen + 1 + selNameLen] = '\0';
OSSpinLockLock(&gExtensionSingletonDictionaryLock_);
id extension = (id)CFDictionaryGetValue(gExtensionSingletonDictionary, key);
// We can't remove the key from the dictionary here (as an optimization),
// because resolveClassMethod can happen on any thread and we'd then need
// a lock.
if (extension) {
// The method is getting wired in to the class, so no need to keep it in
// the dictionary.
CFDictionaryRemoveValue(gExtensionSingletonDictionary, key);
}
OSSpinLockUnlock(&gExtensionSingletonDictionaryLock_);
return extension;
}
+ (BOOL)resolveClassMethod:(SEL)sel {
BOOL GPBResolveExtensionClassMethod(Class self, SEL sel) {
// Another option would be to register the extensions with the class at
// globallyRegisterExtension:
// Timing the two solutions, this solution turned out to be much faster
// and reduced startup time, and runtime memory.
// On an iPhone 5s:
// ResolveClassMethod: 1515583 nanos
// globallyRegisterExtension: 2453083 nanos
// The advantage to globallyRegisterExtension is that it would reduce the
// size of the protos somewhat because the singletonNameC wouldn't need
// to include the class name. For a class with a lot of extensions it
......@@ -169,7 +194,17 @@ static id ExtensionForName(id self, SEL _cmd) {
#pragma unused(obj)
return extension;
});
return class_addMethod(metaClass, sel, imp, encoding);
if (class_addMethod(metaClass, sel, imp, encoding)) {
return YES;
}
}
return NO;
}
+ (BOOL)resolveClassMethod:(SEL)sel {
if (GPBResolveExtensionClassMethod(self, sel)) {
return YES;
}
return [super resolveClassMethod:sel];
}
......
......@@ -40,3 +40,7 @@
+ (void)globallyRegisterExtension:(GPBExtensionField *)field;
@end
// Returns YES if the selector was resolved and added to the class,
// NO otherwise.
BOOL GPBResolveExtensionClassMethod(Class self, SEL sel);
This diff is collapsed.
......@@ -34,6 +34,20 @@
ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8BBEA4A5147C727100C4ADB7"
BuildableName = "UnitTests.xctest"
BlueprintName = "UnitTests"
ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
......
......@@ -34,6 +34,20 @@
ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8BBEA4A5147C727100C4ADB7"
BuildableName = "UnitTests.xctest"
BlueprintName = "UnitTests"
ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -33,12 +33,9 @@
#import "GPBDictionary.h"
#import "GPBTestUtilities.h"
#import "google/protobuf/UnittestRuntimeProto2.pbobjc.h"
#ifndef GPBARRAYSIZE
#define GPBARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))))
#endif // GPBARRAYSIZE
// Pull in the macros (using an external file because expanding all tests
// in a single file makes a file that is failing to work with within Xcode.
//%PDDM-IMPORT-DEFINES GPBDictionaryTests.pddm
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment