DPQueue is now a two-lock concurrent queue.
[hom.git] / Source / DPMessage.h
blob731cf145aa6d56072ebd7482469743d1a2f16639
1 //
2 // DPMessage.h
3 // HigherOrderMessaging
4 //
5 // Created by Ofri Wolfus on 09/05/07.
6 // Copyright 2007 Ofri Wolfus. All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without modification,
9 // are permitted provided that the following conditions are met:
10 //
11 // 1. Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // 2. Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 // 3. Neither the name of Ofri Wolfus nor the names of his contributors
17 // may be used to endorse or promote products derived from this software
18 // without specific prior written permission.
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #if defined(__HOM_FRAMEWORK__)
32 #import <HigherOrderMessaging/DPObjCRuntime.h>
33 #elif defined(__UITESTINGKIT__)
34 #import <UITestingKit/DPObjCRuntime.h>
35 #else
36 #import "DPObjCRuntime.h"
37 #endif
39 #if __OBJC2__
40 #import <objc/runtime.h>
41 #import <objc/message.h>
42 #else
43 #import <objc/objc-class.h>
44 #import <objc/objc-runtime.h>
45 #endif
47 #include <alloca.h>
50 @class NSString;
51 @protocol NSCopying;
53 #define DP_MAX_FRAME_SIZE 1024
55 // This struct is private
56 struct _dp_message_content {
57 SEL _sel;
58 unsigned int _frameSize;
59 char _frame[DP_MAX_FRAME_SIZE];
63 // Warning: In the current implementation it's undefined what happens
64 // if the arguments frame is larger than sizeof(char[1024]).
65 @interface DPMessage {
66 Class isa;
68 @private
69 struct _dp_message_content content;
72 + (id)messageWithSelector:(SEL)sel frame:(void *)frame;
74 - (SEL)selector;
75 - (marg_list)arguments;
76 - (unsigned int)sizeOfArguments;
78 @end
81 @interface DPMessage (Extensions)
84 * Returns whether the receiver assumes to return a struct or not.
86 * If you know you're goign to send the receiver to an object that'll
87 * return a struct and the result of this method is NO, you should
88 * allocate a new arguments list with a pointer to a memory for the returned structure
89 * at the beginning of it. Then copy the list of the receiver after the pointer of
90 * your new list. On the other hand, if you know your object is not going to return a
91 * struct, and the result of this method is YES, you must allocate a new
92 * arguments list that starts with a pointer to your receiver.
94 * NOTE: The result of this method is not always accurate. Depending on the
95 * architecture and its function calling ABI, methods returning small structs
96 * may not be considered to return structs.
98 - (BOOL)returnsStruct;
100 // This is the same as sel_getNumberOfArguments([msg selector])
101 - (unsigned)numberOfArguments;
103 // For non object return values you must use one of the objc_msgSendv() functions directly.
104 - (id)sendTo:(id)receiver;
106 + (Class)class;
107 - (Class)class;
109 - (NSString *)description;
110 - (BOOL)respondsToSelector:(SEL)aSelector;
113 * Returns an arguments frame that can be passed to __builtin_apply().
114 * The size of the frame (passed as the third argument to __builtin_apply())
115 * is the same as the size returned by -sizeOfArguments method.
116 * A new frame is dynamically allocated using malloc() with each invocation
117 * of this method and you are responsible for releasing it.
119 * It is not documented anywhere, but currently simply passing marg_list to
120 * __builtin_return() works (tested on G4 with Max OS X 10.4.9).
122 * NOTE: This method only works for methods returning integer values.
123 * For other return type the result is undefined and invoking this method
124 * will probably cause a crash.
126 - (void *)gccArgumentsFrame;
128 // sizeOfArguments is *NOT* the size of the frame.
129 // Use this method to get the size of the frame.
130 - (unsigned)realFrameSize;
132 @end
135 @interface DPMessage (MemoryManagement) <NSCopying>
137 // Returns a copy of the receiver allocated on the heap
138 // (a normal instance that's ref counted).
139 - (id)copyWithZone:(NSZone *)zone;
140 - (id)copy;
142 // retain/release/autorelease have no effect on messages
143 // returned by the MSG() macro.
144 // Affected messages are those returned by -copyWithZone: or -copy
145 - (id)retain;
146 - (void)release;
147 - (id)autorelease;
149 @end
152 // Allocates and returns an instance on stack
153 #define DPAllocateStackObject(cls, extraBytes) \
154 ({ id __obj = (id)alloca(((size_t)((Class)cls)->instance_size) + extraBytes); \
155 __obj->isa = cls; __obj; })
157 // Private
158 DP_EXTERN Class _dp_uninitialized_msg_cls;
159 DP_EXTERN Class _dp_msg_cls;
160 DP_EXTERN_INLINE id _dp_getCachedMessage(void);
163 * Use this macro to create DPMessage instance.
164 * Currently, this (and MSGV) is the only way to be passed as an argument
165 * to a method or function. If you wish to stoe the returned instance in
166 * a variable, send it a -copy message and use the result instead.
167 * The instance returned from the -copy method is ref-counted like normal
168 * objects and follows the rules of the copy method.
170 * Example: NSArray *results = [myArray select:MSG(hasPrefix:@"a")];
172 #define MSG(X...) \
173 ({ id __msg = _dp_getCachedMessage(); Class __cls = __msg != nil ? __msg->isa : _dp_msg_cls;\
174 if (__builtin_expect(__msg == nil, 0)) __msg = DPAllocateStackObject(_dp_uninitialized_msg_cls, 0); \
175 __msg->isa = _dp_uninitialized_msg_cls; [__msg X]; __msg->isa = __cls; __msg; })
178 * An alternative to MSG(). This macro takes a selector followed by the arguments
179 * of the message, and returns a matching DPMessage instance.
180 * The returned instance is a subject to the same rules and restrictions of
181 * instances returned by MSG().
183 * Example: NSArray *results = [myArray select:MSGV(@selector(hasPrefix:), @"a")];
185 #define MSGV(sel...) \
186 ({ id __msg = _dp_getCachedMessage(); Class __cls = __msg != nil ? __msg->isa : _dp_msg_cls;\
187 if (__builtin_expect(__msg == nil, 0)) __msg = DPAllocateStackObject(_dp_uninitialized_msg_cls, 0); \
188 __msg->isa = _dp_uninitialized_msg_cls; objc_msgSend(__msg, sel); __msg->isa = __cls; __msg; })
191 * Returns the maximum (as returned by method_getSizeOfArguments) of a
192 * given selector. In order to be efficient, an internal cache is used
193 * to cache all selectors.
195 * The implementation will catch classes being added at runtime, but will not
196 * notice methods being dynamically added and classes unregistering (actually,
197 * the implementation uses the total amount of registered classes as in indication
198 * the cache needs to be rebuilt, so it'll not catch only the case of X classes
199 * being registered while X classes unregister [which will result in the same
200 * total of registered classes]).
201 * In these edge cases, use dp_flushArgSizeCache() to force the cache to be
202 * rebuilt the next time the function is invoked.
204 * This function is used internally by DPMessage so if it returns wrong results
205 * so will DPMessage.
207 DP_EXTERN unsigned int dp_maxArgSizeForSelector(SEL sel);
208 DP_EXTERN void dp_flushArgSizeCache(void);
210 DP_EXTERN unsigned dp_getCacheSize(void);
213 * When defined to 1, no attempts to auto-rebuild the cache based on the class
214 * count are made.
215 * Instead, the implementation listens to NSBundleDidLoadNotification and
216 * flushes the cache when the notification arrives.
217 * If you're going to add classes in other ways than NSBundle and still want
218 * the count-based implementation, define DP_NSBUNEL_DETECTION_ONLY to 0.
220 #ifndef DP_NSBUNEL_DETECTION_ONLY
221 #define DP_NSBUNEL_DETECTION_ONLY 1
222 #endif