5 // Created by Wincent Colaiuta on 14 June 2005.
7 // Copyright 2005-2007 Wincent Colaiuta.
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation, either version 3 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #import <objc/objc-class.h>
29 #import "NSInvocation+WOTest.h"
30 #import "NSMethodSignature+WOTest.h"
31 #import "NSObject+WOTest.h"
32 #import "NSProxy+WOTest.h"
33 #import "NSValue+WOTest.h"
36 @implementation WOStub
40 return self; // super (NSProxy) has no init method
45 [self setInvocation:nil];
46 [self setReturnValue:nil];
47 [self setException:nil];
53 [self setAcceptsAnyArguments:YES];
58 #pragma mark Recording
60 - (id)returning:(NSValue *)aValue
62 NSAssert(([self returnValue] == nil), @"WOStub returning: invoked but return value already recorded");
63 [self setReturnValue:aValue];
67 - (id)raising:(id)anException
69 NSAssert(([self exception] == nil), @"WOStub raising: invoked but exception already recorded");
70 [self setException:anException];
75 #pragma mark Testing equality
77 - (BOOL)matchesInvocation:(NSInvocation *)anInvocation
79 NSParameterAssert(anInvocation != nil);
80 NSInvocation *recordedInvocation = [self invocation];
81 NSAssert((recordedInvocation != nil), @"WOStub sent matchesInvocation but no invocation yet recorded");
82 return ([anInvocation WOTest_isEqualToInvocation:recordedInvocation] ||
83 ([self acceptsAnyArguments] && [anInvocation WOTest_isEqualToInvocationIgnoringArguments:recordedInvocation]));
87 #pragma mark Proxy methods
89 - (void)forwardInvocation:(NSInvocation *)anInvocation
91 NSAssert(([self invocation] == nil), @"WOStub sent message but message previously recorded");
92 [self setInvocation:anInvocation];
93 [anInvocation retainArguments];
98 http://lists.apple.com/archives/cocoa-dev/2004/Jun/msg00990.html
100 "On PPC, the prearg area is used to store the 13 floating-point parameter registers, which may contain method parameters that need to be restored when the marg_list is used. The i386 function call ABI has no additional registers to be saved, so its prearg area is empty. The implementations of _objc_msgForward() and objc_msgSendv() in objc4's objc-msg-ppc.s contain more details that may be useful to you.
102 In general, you probably want to avoid marg_list and objc_msgSendv(). Together they are primarily an implementation detail of forward:: ."
108 - forward:(SEL)sel :(marg_list)args
110 NSAssert(([self invocation] == nil), @"WOStub sent message but message previously recorded");
112 // let standard event flow take place (but note that NSProxy implementation raises so subclasses must do the real work)
113 if ([self methodSignatureForSelector:sel])
114 return [super forward:sel :args];
116 // fallback to internal lookup
117 NSMethodSignature *signature = [[delegate methodSignatures] objectForKey:NSStringFromSelector(sel)];
119 // at this point it would be great to be able to improvise but it's not possible to figure out an accurate method signature
120 NSAssert((signature != nil), ([NSString stringWithFormat:@"no method signature for selector %@", NSStringFromSelector(sel)]));
122 NSInvocation *forwardInvocation = [NSInvocation invocationWithMethodSignature:signature];
123 [forwardInvocation setSelector:sel];
126 // store arguments in invocation
128 for (unsigned i = 0, max = [signature numberOfArguments]; i < max; i++)
130 const char *type = [signature getArgumentTypeAtIndex:i]; // always id, SEL, ...
134 // TODO: finish this implementation and copy it (or otherwise make it available) to WOMock.m
135 // leave the compiler warnings about "unused variable 'type'" and "unused variable 'offset'" to remind me to do it
137 // the PPC ABI has special conventions for floats, doubles, structs
139 // contents of floating point registers f1 through f13 stored at args + 0 through args + 96
140 // register based params go in r3 (receiver id), r4 (SEL), and r5 through r10
141 // these params are respectively at:
142 // 1st param: r3 (receiver id) args + (13 * 8) + 24 (24 bytes in the linkage area not sure what it's for)
143 // 2nd param: r4 (SEL) args + (13 * 8) + 28
144 // 3rd param: r5 args + (13 * 8) + 32
145 // 4th param: r6 args + (13 * 8) + 36
146 // 5th param: r7 args + (13 * 8) + 40
147 // 6th param: r8 args + (13 * 8) + 44
148 // 7th param: r9 args + (13 * 8) + 48
149 // 8th param: r10 args + (13 * 8) + 52
150 // the remaining parameters are on the stack (starting at args + (13 * 8) + 56)
151 // note that marg_prearg_size is defined in the headers for ppc and equals 128 (13 * 8 + 24 bytes for linkage area)
153 // from http://darwinsource.opendarwin.org/10.4.3/objc4-267/runtime/Messengers.subproj/objc-msg-ppc.s
154 // typedef struct objc_sendv_margs {
155 // double floatingPointArgs[13];
156 // int linkageArea[6];
157 // int registerArgs[8];
158 // int stackArgs[variable];
161 // if (strcmp(type, @encode(float)) == 0)
163 // else if (strcmp(type, @encode(double)) == 0)
167 #elif defined(__i386__)
169 // on i386 the marg_getRef macro and its helper, marg_adjustedOffset, should work fine
170 if (strcmp(type, @encode(id)) == 0)
172 id *ref = marg_getRef(args, offset, id);
173 [forwardInvocation WOTest_setArgumentValue:[NSValue valueWithBytes:ref objCType:type] atIndex:i];
175 else if (strcmp(type, @encode(SEL)) == 0)
177 SEL *ref = marg_getRef(args, offset, SEL);
178 [forwardInvocation WOTest_setArgumentValue:[NSValue valueWithBytes:ref objCType:type] atIndex:i];
181 [NSException raise:NSGenericException format:@"type %s not supported", type];
183 offset += [NSValue WOTest_sizeForType:[NSString stringWithUTF8String:type]];
185 #elif defined(__ppc64__)
186 // there is no objc-msg-ppc.s so for now just omit support rather than make assumptions
187 #error ppc64 not supported yet
191 #error Unsupported architecture
196 [self forwardInvocation:forwardInvocation];
197 return nil; // nobody cares what a stub returns
201 #pragma mark Accessors
203 - (NSInvocation *)recordedInvocation
205 NSInvocation *recordedInvocation = [self invocation];
206 NSAssert((recordedInvocation != nil), @"WOStub sent recordedInvocation but no invocation yet recorded");
207 return recordedInvocation;
210 - (NSInvocation *)invocation
212 return [[invocation retain] autorelease];
215 - (void)setInvocation:(NSInvocation *)anInvocation
217 if (invocation != anInvocation)
219 [anInvocation retain];
220 [invocation release];
221 invocation = anInvocation;
225 - (NSValue *)returnValue
227 return [[returnValue retain] autorelease];
230 - (void)setReturnValue:(NSValue *)aReturnValue
232 if (returnValue != aReturnValue)
234 [aReturnValue retain];
235 [returnValue release];
236 returnValue = aReturnValue;
240 - (BOOL)acceptsAnyArguments
242 return acceptsAnyArguments;
245 - (void)setAcceptsAnyArguments:(BOOL)flag
247 acceptsAnyArguments = flag;
252 return [[exception retain] autorelease];
255 - (void)setException:(id)anException
257 if (exception != anException)
259 [anException retain];
261 exception = anException;