blob: 845b3f31812e6a3f4c78e87d657238fa09501afb [file] [log] [blame]
//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "Service/Sources/EDOParameter.h"
#import "Service/Sources/EDOBlockObject.h"
#import "Service/Sources/EDOObject.h"
static NSString *const kObjCIdType = @"@";
static NSString *const kObjCSelectorType = @":";
static NSString *const kEDOParameterCoderValueKey = @"value";
static NSString *const kEDOParameterCoderTypeKey = @"type";
#pragma mark -
/** The placeholder type to save the NULL pointer that points to an object. */
@interface EDONull : NSObject <NSSecureCoding>
@end
@implementation EDONull
+ (BOOL)supportsSecureCoding {
return YES;
}
/// TODO(haowoo): This will not be needed if use EDOParameter.
- (void)encodeWithCoder:(NSCoder *)aCoder {
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
return self;
}
@end
#pragma mark -
@implementation EDOParameter
+ (BOOL)supportsSecureCoding {
return YES;
}
+ (instancetype)parameterWithValue:(id<NSCoding>)value objCType:(NSString *)objCType {
return [[self alloc] initWithValue:value objCType:objCType];
}
+ (instancetype)parameterWithBytes:(void *)bytes objCType:(const char *)objCType {
if (EDO_IS_OBJECT_OR_CLASS(objCType)) {
id<NSCoding> __strong *value = (id<NSCoding> __strong *)bytes;
return [self parameterWithValue:*(value) objCType:kObjCIdType];
} else if (EDO_IS_SELECTOR(objCType)) {
SEL selector = *(SEL *)bytes;
return [self parameterWithValue:NSStringFromSelector(selector) objCType:kObjCSelectorType];
} else {
NSUInteger typeSize = 0L;
NSGetSizeAndAlignment(objCType, &typeSize, NULL);
return [self parameterWithValue:(bytes ? [NSData dataWithBytes:bytes length:typeSize] : nil)
objCType:[NSString stringWithUTF8String:objCType]];
}
}
+ (instancetype)parameterWithObject:(id<NSCoding>)object {
return [self parameterWithValue:object objCType:@"@"];
}
+ (instancetype)parameterForNilValue {
static EDOParameter *kNilValue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
kNilValue = [self parameterWithValue:nil objCType:kObjCIdType];
});
return kNilValue;
}
+ (instancetype)parameterForDoublePointerNullValue {
static EDOParameter *kNullPointerValue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
kNullPointerValue = [self parameterWithValue:[[EDONull alloc] init] objCType:kObjCIdType];
});
return kNullPointerValue;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
// EDOParameter can carry any type of object as long as it's serializable, so it whitelists all
// the types inheriting from NSObject. EDOObject and EDOBlockObject are NSProxy's and need to be
// whitelisted as well.
NSSet *anyClasses =
[NSSet setWithObjects:[EDOBlockObject class], [EDOObject class], [NSObject class], nil];
_value = [aDecoder decodeObjectOfClasses:anyClasses forKey:kEDOParameterCoderValueKey];
_valueObjCType = [aDecoder decodeObjectOfClass:[NSString class]
forKey:kEDOParameterCoderTypeKey];
}
return self;
}
- (instancetype)initWithValue:(id<NSCoding>)value objCType:(NSString *)objCType {
self = [super init];
if (self) {
_value = value;
_valueObjCType = objCType;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.value forKey:kEDOParameterCoderValueKey];
[aCoder encodeObject:self.valueObjCType forKey:kEDOParameterCoderTypeKey];
}
- (char const *)objCType {
return self.valueObjCType.UTF8String;
}
- (void)getValue:(void *)buffer {
char const *ctype = self.objCType;
if (EDO_IS_OBJECT_OR_CLASS(ctype)) {
NSAssert(sizeof(id) == sizeof(Class), @"The buffer is not suitable for both Id and Class.");
memcpy(buffer, (void *)&_value, sizeof(id));
} else if (EDO_IS_SELECTOR(ctype)) {
SEL selector = NSSelectorFromString((NSString *)self.value);
memcpy(buffer, (void *)&selector, sizeof(SEL));
} else {
NSData *value = (NSData *)self.value;
NSUInteger typeSize = 0L;
NSGetSizeAndAlignment(ctype, &typeSize, NULL);
NSAssert(!value || typeSize == value.length, @"The buffer size is incorrect.");
[value getBytes:buffer length:value.length];
}
}
- (BOOL)isDoublePointerNullValue {
static Class kNullClass;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
kNullClass = [EDONull class];
});
id __unsafe_unretained value;
[self getValue:&value];
return [[value class] isEqual:kNullClass];
}
@end