// // Licensed under the terms in License.txt // // Copyright 2010 Allen Ding. All rights reserved. // #import "KWMessagePattern.h" #import "KWAny.h" #import "KWFormatter.h" #import "KWNull.h" #import "KWObjCUtilities.h" #import "KWValue.h" #import "NSInvocation+KiwiAdditions.h" #import "NSMethodSignature+KiwiAdditions.h" #import "KWGenericMatchEvaluator.h" @implementation KWMessagePattern #pragma mark - Initializing - (id)initWithSelector:(SEL)aSelector { return [self initWithSelector:aSelector argumentFilters:nil]; } - (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray { self = [super init]; if (self) { selector = aSelector; if ([anArray count] > 0) argumentFilters = [anArray copy]; } return self; } - (id)initWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList { NSUInteger count = KWSelectorParameterCount(aSelector); NSMutableArray *array = [NSMutableArray arrayWithCapacity:count]; [array addObject:(firstArgumentFilter != nil) ? firstArgumentFilter : [KWNull null]]; for (NSUInteger i = 1; i < count; ++i) { id object = va_arg(argumentList, id); [array addObject:(object != nil) ? object : [KWNull null]]; } va_end(argumentList); return [self initWithSelector:aSelector argumentFilters:array]; } + (id)messagePatternWithSelector:(SEL)aSelector { return [self messagePatternWithSelector:aSelector argumentFilters:nil]; } + (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray { return [[self alloc] initWithSelector:aSelector argumentFilters:anArray]; } + (id)messagePatternWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList { return [[self alloc] initWithSelector:aSelector firstArgumentFilter:firstArgumentFilter argumentList:argumentList]; } + (id)messagePatternFromInvocation:(NSInvocation *)anInvocation { NSMethodSignature *signature = [anInvocation methodSignature]; NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments]; NSMutableArray *argumentFilters = nil; if (numberOfMessageArguments > 0) { argumentFilters = [[NSMutableArray alloc] initWithCapacity:numberOfMessageArguments]; for (NSUInteger i = 0; i < numberOfMessageArguments; ++i) { const char *type = [signature messageArgumentTypeAtIndex:i]; void* argumentDataBuffer = malloc(KWObjCTypeLength(type)); [anInvocation getMessageArgument:argumentDataBuffer atIndex:i]; id object = nil; if(*(__unsafe_unretained id*)argumentDataBuffer != [KWAny any] && !KWObjCTypeIsObject(type)) { NSData *data = [anInvocation messageArgumentDataAtIndex:i]; object = [KWValue valueWithBytes:[data bytes] objCType:type]; } else { object = *(__unsafe_unretained id*)argumentDataBuffer; if (object != [KWAny any] && KWObjCTypeIsBlock(type)) { object = [object copy]; // Converting NSStackBlock to NSMallocBlock } } [argumentFilters addObject:(object != nil) ? object : [KWNull null]]; free(argumentDataBuffer); } } return [self messagePatternWithSelector:[anInvocation selector] argumentFilters:argumentFilters]; } #pragma mark - Properties @synthesize selector; @synthesize argumentFilters; #pragma mark - Matching Invocations - (BOOL)argumentFiltersMatchInvocationArguments:(NSInvocation *)anInvocation { if (self.argumentFilters == nil) return YES; NSMethodSignature *signature = [anInvocation methodSignature]; NSUInteger numberOfArgumentFilters = [self.argumentFilters count]; NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments]; for (NSUInteger i = 0; i < numberOfMessageArguments && i < numberOfArgumentFilters; ++i) { const char *objCType = [signature messageArgumentTypeAtIndex:i]; id __autoreleasing object = nil; // Extract message argument into object (wrapping values if neccesary) if (KWObjCTypeIsObject(objCType) || KWObjCTypeIsClass(objCType)) { [anInvocation getMessageArgument:&object atIndex:i]; } else { NSData *data = [anInvocation messageArgumentDataAtIndex:i]; object = [KWValue valueWithBytes:[data bytes] objCType:objCType]; } // Match argument filter to object id argumentFilter = (self.argumentFilters)[i]; if ([argumentFilter isEqual:[KWAny any]]) { continue; } if ([KWGenericMatchEvaluator isGenericMatcher:argumentFilter]) { id matcher = argumentFilter; if ([object isKindOfClass:[KWValue class]] && [object isNumeric]) { NSNumber *number = [object numberValue]; if (![KWGenericMatchEvaluator genericMatcher:matcher matches:number]) { return NO; } } else if (![KWGenericMatchEvaluator genericMatcher:matcher matches:object]) { return NO; } } else if ([argumentFilter isEqual:[KWNull null]]) { if (!KWObjCTypeIsPointerLike(objCType)) { [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])]; } void *p = nil; [anInvocation getMessageArgument:&p atIndex:i]; if (p != nil) return NO; } else if (![argumentFilter isEqual:object]) { return NO; } } return YES; } - (BOOL)matchesInvocation:(NSInvocation *)anInvocation { return self.selector == [anInvocation selector] && [self argumentFiltersMatchInvocationArguments:anInvocation]; } #pragma mark - Comparing Message Patterns - (NSUInteger)hash { return [NSStringFromSelector(self.selector) hash]; } - (BOOL)isEqual:(id)object { if (![object isKindOfClass:[KWMessagePattern class]]) return NO; return [self isEqualToMessagePattern:object]; } - (BOOL)isEqualToMessagePattern:(KWMessagePattern *)aMessagePattern { if (self.selector != aMessagePattern.selector) return NO; if (self.argumentFilters == nil && aMessagePattern.argumentFilters == nil) return YES; return [self.argumentFilters isEqualToArray:aMessagePattern.argumentFilters]; } #pragma mark - Retrieving String Representations - (NSString *)selectorString { return NSStringFromSelector(self.selector); } - (NSString *)selectorAndArgumentFiltersString { NSMutableString *description = [[NSMutableString alloc] init]; NSArray *components = [NSStringFromSelector(self.selector) componentsSeparatedByString:@":"]; NSUInteger count = [components count] - 1; for (NSUInteger i = 0; i < count; ++i) { NSString *selectorComponent = components[i]; NSString *argumentFilterString = [KWFormatter formatObject:(self.argumentFilters)[i]]; [description appendFormat:@"%@:%@ ", selectorComponent, argumentFilterString]; } return description; } - (NSString *)stringValue { if (self.argumentFilters == nil) return [self selectorString]; else return [self selectorAndArgumentFiltersString]; } #pragma mark - Debugging - (NSString *)description { return [NSString stringWithFormat:@"selector: %@\nargumentFilters: %@", NSStringFromSelector(self.selector), self.argumentFilters]; } @end