]>
Commit | Line | Data |
---|---|---|
93a37866 A |
1 | /* |
2 | * Copyright (C) 2013 Apple Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
ed1e77d3 | 26 | #import <objc/Protocol.h> |
93a37866 A |
27 | #import <objc/runtime.h> |
28 | #import <wtf/HashSet.h> | |
29 | #import <wtf/Vector.h> | |
30 | ||
31 | inline bool protocolImplementsProtocol(Protocol *candidate, Protocol *target) | |
32 | { | |
33 | unsigned protocolProtocolsCount; | |
34 | Protocol ** protocolProtocols = protocol_copyProtocolList(candidate, &protocolProtocolsCount); | |
35 | for (unsigned i = 0; i < protocolProtocolsCount; ++i) { | |
36 | if (protocol_isEqual(protocolProtocols[i], target)) { | |
37 | free(protocolProtocols); | |
38 | return true; | |
39 | } | |
40 | } | |
41 | free(protocolProtocols); | |
42 | return false; | |
43 | } | |
44 | ||
45 | inline void forEachProtocolImplementingProtocol(Class cls, Protocol *target, void (^callback)(Protocol *)) | |
46 | { | |
47 | ASSERT(cls); | |
48 | ASSERT(target); | |
49 | ||
50 | Vector<Protocol *> worklist; | |
51 | HashSet<void*> visited; | |
52 | ||
53 | // Initially fill the worklist with the Class's protocols. | |
54 | unsigned protocolsCount; | |
55 | Protocol ** protocols = class_copyProtocolList(cls, &protocolsCount); | |
56 | worklist.append(protocols, protocolsCount); | |
57 | free(protocols); | |
58 | ||
59 | while (!worklist.isEmpty()) { | |
60 | Protocol *protocol = worklist.last(); | |
61 | worklist.removeLast(); | |
62 | ||
63 | // Are we encountering this Protocol for the first time? | |
64 | if (!visited.add(protocol).isNewEntry) | |
65 | continue; | |
66 | ||
67 | // If it implements the protocol, make the callback. | |
68 | if (protocolImplementsProtocol(protocol, target)) | |
69 | callback(protocol); | |
70 | ||
71 | // Add incorporated protocols to the worklist. | |
72 | protocols = protocol_copyProtocolList(protocol, &protocolsCount); | |
73 | worklist.append(protocols, protocolsCount); | |
74 | free(protocols); | |
75 | } | |
76 | } | |
77 | ||
78 | inline void forEachMethodInClass(Class cls, void (^callback)(Method)) | |
79 | { | |
80 | unsigned count; | |
81 | Method* methods = class_copyMethodList(cls, &count); | |
82 | for (unsigned i = 0; i < count; ++i) | |
83 | callback(methods[i]); | |
84 | free(methods); | |
85 | } | |
86 | ||
87 | inline void forEachMethodInProtocol(Protocol *protocol, BOOL isRequiredMethod, BOOL isInstanceMethod, void (^callback)(SEL, const char*)) | |
88 | { | |
89 | unsigned count; | |
90 | struct objc_method_description* methods = protocol_copyMethodDescriptionList(protocol, isRequiredMethod, isInstanceMethod, &count); | |
91 | for (unsigned i = 0; i < count; ++i) | |
92 | callback(methods[i].name, methods[i].types); | |
93 | free(methods); | |
94 | } | |
95 | ||
96 | inline void forEachPropertyInProtocol(Protocol *protocol, void (^callback)(objc_property_t)) | |
97 | { | |
98 | unsigned count; | |
99 | objc_property_t* properties = protocol_copyPropertyList(protocol, &count); | |
100 | for (unsigned i = 0; i < count; ++i) | |
101 | callback(properties[i]); | |
102 | free(properties); | |
103 | } | |
104 | ||
105 | template<char open, char close> | |
106 | void skipPair(const char*& position) | |
107 | { | |
108 | size_t count = 1; | |
109 | do { | |
110 | char c = *position++; | |
111 | if (!c) | |
112 | @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Malformed type encoding" userInfo:nil]; | |
113 | if (c == open) | |
114 | ++count; | |
115 | else if (c == close) | |
116 | --count; | |
117 | } while (count); | |
118 | } | |
119 | ||
120 | class StringRange { | |
121 | WTF_MAKE_NONCOPYABLE(StringRange); | |
122 | public: | |
123 | StringRange(const char* begin, const char* end) : m_ptr(strndup(begin, end - begin)) { } | |
124 | ~StringRange() { free(m_ptr); } | |
125 | operator const char*() const { return m_ptr; } | |
126 | const char* get() const { return m_ptr; } | |
127 | ||
128 | private: | |
129 | char* m_ptr; | |
130 | }; | |
131 | ||
132 | class StructBuffer { | |
133 | WTF_MAKE_NONCOPYABLE(StructBuffer); | |
134 | public: | |
135 | StructBuffer(const char* encodedType) | |
136 | { | |
137 | NSUInteger size, alignment; | |
138 | NSGetSizeAndAlignment(encodedType, &size, &alignment); | |
139 | --alignment; | |
140 | m_allocation = static_cast<char*>(malloc(size + alignment)); | |
141 | m_buffer = reinterpret_cast<char*>((reinterpret_cast<intptr_t>(m_allocation) + alignment) & ~alignment); | |
142 | } | |
143 | ||
144 | ~StructBuffer() { free(m_allocation); } | |
145 | operator void*() const { return m_buffer; } | |
146 | ||
147 | private: | |
148 | void* m_allocation; | |
149 | void* m_buffer; | |
150 | }; | |
151 | ||
152 | template<typename DelegateType> | |
153 | typename DelegateType::ResultType parseObjCType(const char*& position) | |
154 | { | |
155 | ASSERT(*position); | |
156 | ||
157 | switch (*position++) { | |
158 | case 'c': | |
159 | return DelegateType::template typeInteger<char>(); | |
160 | case 'i': | |
161 | return DelegateType::template typeInteger<int>(); | |
162 | case 's': | |
163 | return DelegateType::template typeInteger<short>(); | |
164 | case 'l': | |
165 | return DelegateType::template typeInteger<long>(); | |
166 | case 'q': | |
ed1e77d3 | 167 | return DelegateType::template typeDouble<long long>(); |
93a37866 A |
168 | case 'C': |
169 | return DelegateType::template typeInteger<unsigned char>(); | |
170 | case 'I': | |
171 | return DelegateType::template typeInteger<unsigned>(); | |
172 | case 'S': | |
173 | return DelegateType::template typeInteger<unsigned short>(); | |
174 | case 'L': | |
175 | return DelegateType::template typeInteger<unsigned long>(); | |
176 | case 'Q': | |
177 | return DelegateType::template typeDouble<unsigned long long>(); | |
178 | case 'f': | |
179 | return DelegateType::template typeDouble<float>(); | |
180 | case 'd': | |
181 | return DelegateType::template typeDouble<double>(); | |
182 | case 'B': | |
183 | return DelegateType::typeBool(); | |
184 | case 'v': | |
185 | return DelegateType::typeVoid(); | |
186 | ||
187 | case '@': { // An object (whether statically typed or typed id) | |
188 | if (position[0] == '?' && position[1] == '<') { | |
189 | position += 2; | |
190 | const char* begin = position; | |
191 | skipPair<'<','>'>(position); | |
192 | return DelegateType::typeBlock(begin, position - 1); | |
193 | } | |
194 | ||
195 | if (*position == '"') { | |
81345200 A |
196 | const char* begin = position + 1; |
197 | const char* protocolPosition = strchr(begin, '<'); | |
198 | const char* endOfType = strchr(begin, '"'); | |
199 | position = endOfType + 1; | |
200 | ||
201 | // There's no protocol involved in this type, so just handle the class name. | |
202 | if (!protocolPosition || protocolPosition > endOfType) | |
203 | return DelegateType::typeOfClass(begin, endOfType); | |
204 | // We skipped the class name and went straight to the protocol, so this is an id type. | |
205 | if (begin == protocolPosition) | |
206 | return DelegateType::typeId(); | |
207 | // We have a class name with a protocol. For now, ignore the protocol. | |
208 | return DelegateType::typeOfClass(begin, protocolPosition); | |
93a37866 A |
209 | } |
210 | ||
211 | return DelegateType::typeId(); | |
212 | } | |
213 | ||
214 | case '{': { // {name=type...} A structure | |
215 | const char* begin = position - 1; | |
216 | skipPair<'{','}'>(position); | |
217 | return DelegateType::typeStruct(begin, position); | |
218 | } | |
219 | ||
220 | // NOT supporting C strings, arrays, pointers, unions, bitfields, function pointers. | |
221 | case '*': // A character string (char *) | |
222 | case '[': // [array type] An array | |
223 | case '(': // (name=type...) A union | |
224 | case 'b': // bnum A bit field of num bits | |
225 | case '^': // ^type A pointer to type | |
226 | case '?': // An unknown type (among other things, this code is used for function pointers) | |
227 | // NOT supporting Objective-C Class, SEL | |
228 | case '#': // A class object (Class) | |
229 | case ':': // A method selector (SEL) | |
230 | default: | |
231 | return nil; | |
232 | } | |
233 | } | |
234 | ||
235 | extern "C" { | |
236 | // Forward declare some Objective-C runtime internal methods that are not API. | |
237 | const char *_protocol_getMethodTypeEncoding(Protocol *, SEL, BOOL isRequiredMethod, BOOL isInstanceMethod); | |
238 | id objc_initWeak(id *, id); | |
239 | void objc_destroyWeak(id *); | |
240 | bool _Block_has_signature(void *); | |
241 | const char * _Block_signature(void *); | |
242 | } |