libObjCAttr/Reflection/RFMethodInfo.m (115 lines of code) (raw):
//
// RFMethodInfo.m
// libObjCAttr
//
// Copyright (c) 2014 EPAM Systems, Inc. All rights reserved.
//
// 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 the EPAM Systems, 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 HOLDER 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.
//
// See the NOTICE file and the LICENSE file distributed with this work
// for additional information regarding copyright ownership and licensing
#import "RFMethodInfo.h"
#import <objc/runtime.h>
#import "ROADAttribute.h"
#import "RFTypeDecoder.h"
// The number hidden of method arguments: self and _cmd
static NSUInteger const kRFMethodArgumentOffset = 2;
@interface RFMethodInfo () {
NSString * _name;
NSString * _className;
Class _hostClass;
NSUInteger _numberOfArguments;
NSString * _returnType;
BOOL _classMethod;
NSArray * _argumentTypes;
Method _method;
}
@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *className;
@property (assign, nonatomic) Class hostClass;
@property (assign, nonatomic) NSUInteger numberOfArguments;
@property (copy, nonatomic) NSString *returnType;
@property (assign, nonatomic, getter = isClassMethod) BOOL classMethod;
@end
@implementation RFMethodInfo
@dynamic attributes;
#pragma mark - Initialization
+ (NSArray *)methodsOfClass:(Class)aClass {
NSMutableArray *result = [[NSMutableArray alloc] init];
unsigned int numberOfInstanceMethods = 0;
Method *instanceMethods = class_copyMethodList(aClass, &numberOfInstanceMethods);
[result addObjectsFromArray:[self methodInfoList:instanceMethods count:numberOfInstanceMethods ofClass:aClass areClassMethods:NO]];
free(instanceMethods);
unsigned int numberOfClassMethods = 0;
Method *classMethods = class_copyMethodList(object_getClass(aClass), &numberOfClassMethods);
[result addObjectsFromArray:[self methodInfoList:classMethods count:numberOfClassMethods ofClass:aClass areClassMethods:YES]];
free(classMethods);
return result;
}
+ (NSArray *)methodInfoList:(const Method *)methods count:(unsigned int)numberOfMethods ofClass:(Class)aClass areClassMethods:(const BOOL)areClassMethods {
NSMutableArray * const result = [[NSMutableArray alloc] init];
RFMethodInfo *info;
for (unsigned int index = 0; index < numberOfMethods; index++) {
info = [self methodInfo:methods[index] forClass:aClass];
info.classMethod = areClassMethods;
[result addObject:info];
}
return result;
}
+ (RFMethodInfo *)instanceMethodNamed:(NSString *)methodName forClass:(Class)aClass {
Method method = class_getInstanceMethod(aClass, NSSelectorFromString(methodName));
RFMethodInfo *info = [self methodInfo:method forClass:aClass];
info.classMethod = NO;
return info;
}
+ (RFMethodInfo *)classMethodNamed:(NSString *)methodName forClass:(Class)aClass {
Method method = class_getClassMethod(aClass, NSSelectorFromString(methodName));
RFMethodInfo *info = [self methodInfo:method forClass:aClass];
info.classMethod = YES;
return info;
}
+ (RFMethodInfo *)methodInfo:(Method)method forClass:(Class)aClass {
RFMethodInfo *info = [[RFMethodInfo alloc] initWithMethod:method];
info.hostClass = aClass;
info.name = NSStringFromSelector(method_getName(method));
info.numberOfArguments = (NSUInteger)method_getNumberOfArguments(method) - kRFMethodArgumentOffset;
return info;
}
+ (NSArray *)argumentsTypeNamesOfMethod:(Method)method numberOfArguments:(NSUInteger)numberOfArguments {
NSMutableArray * const array = [[NSMutableArray alloc] init];
for (unsigned int index = kRFMethodArgumentOffset; index < numberOfArguments + kRFMethodArgumentOffset; index++) {
char *argEncoding = method_copyArgumentType(method, index);
[array addObject:[RFTypeDecoder nameFromTypeEncoding:@(argEncoding)]];
free(argEncoding);
}
return array;
}
+ (NSString *)returnTypeNameOfMethod:(Method)method {
char *returnTypeEncoding = method_copyReturnType(method);
NSString * const result = @(returnTypeEncoding);
free(returnTypeEncoding);
return [RFTypeDecoder nameFromTypeEncoding:result];
}
- (id)initWithMethod:(Method)method {
self = [super init];
if (self) {
_method = method;
}
return self;
}
- (NSString *)typeOfArgumentAtIndex:(const NSUInteger)anIndex {
if (!_argumentTypes) {
_argumentTypes = [[self class] argumentsTypeNamesOfMethod:_method numberOfArguments:_numberOfArguments];
}
return _argumentTypes[anIndex];
}
#pragma mark - Specifiers
- (NSString *)className {
if (!_className) {
_className = NSStringFromClass(_hostClass);
}
return _className;
}
- (NSString *)returnType {
if (!_returnType) {
_returnType = [[self class] returnTypeNameOfMethod:_method];
}
return _returnType;
}
#pragma mark - Attributes
- (NSArray *)attributes {
return [self.hostClass RF_attributesForMethod:self.name];
}
- (id)attributeWithType:(Class)requiredClassOfAttribute {
return [self.hostClass RF_attributeForMethod:self.name withAttributeType:requiredClassOfAttribute];
}
@end