KWMessagePattern.m 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. //
  2. // Licensed under the terms in License.txt
  3. //
  4. // Copyright 2010 Allen Ding. All rights reserved.
  5. //
  6. #import "KWMessagePattern.h"
  7. #import "KWAny.h"
  8. #import "KWFormatter.h"
  9. #import "KWNull.h"
  10. #import "KWObjCUtilities.h"
  11. #import "KWValue.h"
  12. #import "NSInvocation+KiwiAdditions.h"
  13. #import "NSMethodSignature+KiwiAdditions.h"
  14. #import "KWGenericMatchEvaluator.h"
  15. @implementation KWMessagePattern
  16. #pragma mark - Initializing
  17. - (id)initWithSelector:(SEL)aSelector {
  18. return [self initWithSelector:aSelector argumentFilters:nil];
  19. }
  20. - (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray {
  21. self = [super init];
  22. if (self) {
  23. selector = aSelector;
  24. if ([anArray count] > 0)
  25. argumentFilters = [anArray copy];
  26. }
  27. return self;
  28. }
  29. - (id)initWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList {
  30. NSUInteger count = KWSelectorParameterCount(aSelector);
  31. NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
  32. [array addObject:(firstArgumentFilter != nil) ? firstArgumentFilter : [KWNull null]];
  33. for (NSUInteger i = 1; i < count; ++i)
  34. {
  35. id object = va_arg(argumentList, id);
  36. [array addObject:(object != nil) ? object : [KWNull null]];
  37. }
  38. va_end(argumentList);
  39. return [self initWithSelector:aSelector argumentFilters:array];
  40. }
  41. + (id)messagePatternWithSelector:(SEL)aSelector {
  42. return [self messagePatternWithSelector:aSelector argumentFilters:nil];
  43. }
  44. + (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray {
  45. return [[self alloc] initWithSelector:aSelector argumentFilters:anArray];
  46. }
  47. + (id)messagePatternWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList {
  48. return [[self alloc] initWithSelector:aSelector firstArgumentFilter:firstArgumentFilter argumentList:argumentList];
  49. }
  50. + (id)messagePatternFromInvocation:(NSInvocation *)anInvocation {
  51. NSMethodSignature *signature = [anInvocation methodSignature];
  52. NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments];
  53. NSMutableArray *argumentFilters = nil;
  54. if (numberOfMessageArguments > 0) {
  55. argumentFilters = [[NSMutableArray alloc] initWithCapacity:numberOfMessageArguments];
  56. for (NSUInteger i = 0; i < numberOfMessageArguments; ++i) {
  57. const char *type = [signature messageArgumentTypeAtIndex:i];
  58. void* argumentDataBuffer = malloc(KWObjCTypeLength(type));
  59. [anInvocation getMessageArgument:argumentDataBuffer atIndex:i];
  60. id object = nil;
  61. if(*(__unsafe_unretained id*)argumentDataBuffer != [KWAny any] && !KWObjCTypeIsObject(type)) {
  62. NSData *data = [anInvocation messageArgumentDataAtIndex:i];
  63. object = [KWValue valueWithBytes:[data bytes] objCType:type];
  64. } else {
  65. object = *(__unsafe_unretained id*)argumentDataBuffer;
  66. if (object != [KWAny any] && KWObjCTypeIsBlock(type)) {
  67. object = [object copy]; // Converting NSStackBlock to NSMallocBlock
  68. }
  69. }
  70. [argumentFilters addObject:(object != nil) ? object : [KWNull null]];
  71. free(argumentDataBuffer);
  72. }
  73. }
  74. return [self messagePatternWithSelector:[anInvocation selector] argumentFilters:argumentFilters];
  75. }
  76. #pragma mark - Properties
  77. @synthesize selector;
  78. @synthesize argumentFilters;
  79. #pragma mark - Matching Invocations
  80. - (BOOL)argumentFiltersMatchInvocationArguments:(NSInvocation *)anInvocation {
  81. if (self.argumentFilters == nil)
  82. return YES;
  83. NSMethodSignature *signature = [anInvocation methodSignature];
  84. NSUInteger numberOfArgumentFilters = [self.argumentFilters count];
  85. NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments];
  86. for (NSUInteger i = 0; i < numberOfMessageArguments && i < numberOfArgumentFilters; ++i) {
  87. const char *objCType = [signature messageArgumentTypeAtIndex:i];
  88. id __autoreleasing object = nil;
  89. // Extract message argument into object (wrapping values if neccesary)
  90. if (KWObjCTypeIsObject(objCType) || KWObjCTypeIsClass(objCType)) {
  91. [anInvocation getMessageArgument:&object atIndex:i];
  92. } else {
  93. NSData *data = [anInvocation messageArgumentDataAtIndex:i];
  94. object = [KWValue valueWithBytes:[data bytes] objCType:objCType];
  95. }
  96. // Match argument filter to object
  97. id argumentFilter = (self.argumentFilters)[i];
  98. if ([argumentFilter isEqual:[KWAny any]]) {
  99. continue;
  100. }
  101. if ([KWGenericMatchEvaluator isGenericMatcher:argumentFilter]) {
  102. id matcher = argumentFilter;
  103. if ([object isKindOfClass:[KWValue class]] && [object isNumeric]) {
  104. NSNumber *number = [object numberValue];
  105. if (![KWGenericMatchEvaluator genericMatcher:matcher matches:number]) {
  106. return NO;
  107. }
  108. } else if (![KWGenericMatchEvaluator genericMatcher:matcher matches:object]) {
  109. return NO;
  110. }
  111. } else if ([argumentFilter isEqual:[KWNull null]]) {
  112. if (!KWObjCTypeIsPointerLike(objCType)) {
  113. [NSException raise:@"KWMessagePatternException" format:@"nil was specified as an argument filter, but argument(%d) is not a pointer for @selector(%@)", (int)(i + 1), NSStringFromSelector([anInvocation selector])];
  114. }
  115. void *p = nil;
  116. [anInvocation getMessageArgument:&p atIndex:i];
  117. if (p != nil)
  118. return NO;
  119. } else if (![argumentFilter isEqual:object]) {
  120. return NO;
  121. }
  122. }
  123. return YES;
  124. }
  125. - (BOOL)matchesInvocation:(NSInvocation *)anInvocation {
  126. return self.selector == [anInvocation selector] && [self argumentFiltersMatchInvocationArguments:anInvocation];
  127. }
  128. #pragma mark - Comparing Message Patterns
  129. - (NSUInteger)hash {
  130. return [NSStringFromSelector(self.selector) hash];
  131. }
  132. - (BOOL)isEqual:(id)object {
  133. if (![object isKindOfClass:[KWMessagePattern class]])
  134. return NO;
  135. return [self isEqualToMessagePattern:object];
  136. }
  137. - (BOOL)isEqualToMessagePattern:(KWMessagePattern *)aMessagePattern {
  138. if (self.selector != aMessagePattern.selector)
  139. return NO;
  140. if (self.argumentFilters == nil && aMessagePattern.argumentFilters == nil)
  141. return YES;
  142. return [self.argumentFilters isEqualToArray:aMessagePattern.argumentFilters];
  143. }
  144. #pragma mark - Retrieving String Representations
  145. - (NSString *)selectorString {
  146. return NSStringFromSelector(self.selector);
  147. }
  148. - (NSString *)selectorAndArgumentFiltersString {
  149. NSMutableString *description = [[NSMutableString alloc] init];
  150. NSArray *components = [NSStringFromSelector(self.selector) componentsSeparatedByString:@":"];
  151. NSUInteger count = [components count] - 1;
  152. for (NSUInteger i = 0; i < count; ++i) {
  153. NSString *selectorComponent = components[i];
  154. NSString *argumentFilterString = [KWFormatter formatObject:(self.argumentFilters)[i]];
  155. [description appendFormat:@"%@:%@ ", selectorComponent, argumentFilterString];
  156. }
  157. return description;
  158. }
  159. - (NSString *)stringValue {
  160. if (self.argumentFilters == nil)
  161. return [self selectorString];
  162. else
  163. return [self selectorAndArgumentFiltersString];
  164. }
  165. #pragma mark - Debugging
  166. - (NSString *)description {
  167. return [NSString stringWithFormat:@"selector: %@\nargumentFilters: %@",
  168. NSStringFromSelector(self.selector),
  169. self.argumentFilters];
  170. }
  171. @end