/ NSObjCRuntime.m
NSObjCRuntime.m
  1  //
  2  //  NSObjCRuntime.m
  3  //  CoreFoundation
  4  //
  5  //  Copyright (c) 2014 Apportable. All rights reserved.
  6  //
  7  
  8  #import <assert.h>
  9  #import <objc/runtime.h>
 10  
 11  #import <Foundation/NSException.h>
 12  #import <Foundation/NSObjCRuntime.h>
 13  #import <Foundation/NSString.h>
 14  
 15  #import "NSObjCRuntimeInternal.h"
 16  
 17  double NSFoundationVersionNumber = 1047.22;
 18  
 19  // NSGetSizeAndAlignment does not allow for debugging sizes, like
 20  // those emitted by method_getTypeEncoding, to occur in the type
 21  // declaration ("i8" compared to just "i"). However,
 22  // initWithObjCTypes: requires us to parse essentially the same
 23  // format, but with the size hints, and so we let NSMethodSignature
 24  // know this naughty little detail.
 25  
 26  const char *__NSGetSizeAndAlignment(const char *decl, NSUInteger *size, NSUInteger *alignment, BOOL stripSizeHints)
 27  {
 28      decl = stripQualifiersAndComments(decl);
 29  
 30      switch (*decl++) {
 31          case _C_CHR: {
 32              *size += sizeof(char);
 33              *alignment = MAX(*alignment, __alignof(char));
 34              break;
 35          }
 36  
 37          case _C_SHT: {
 38              *size += sizeof(short);
 39              *alignment = MAX(*alignment, __alignof(short));
 40              break;
 41          }
 42  
 43          case _C_INT: {
 44              *size += sizeof(int);
 45              *alignment = MAX(*alignment, __alignof(int));
 46              break;
 47          }
 48  
 49          case _C_LNG: {
 50              *size += sizeof(long);
 51              *alignment = MAX(*alignment, __alignof(long));
 52              break;
 53          }
 54  
 55          case _C_LNG_LNG: {
 56              *size += sizeof(long long);
 57              *alignment = MAX(*alignment, __alignof(long long));
 58              break;
 59          }
 60  
 61          case _C_UCHR: {
 62              *size += sizeof(unsigned char);
 63              *alignment = MAX(*alignment, __alignof(unsigned char));
 64              break;
 65          }
 66  
 67          case _C_USHT: {
 68              *size += sizeof(unsigned short);
 69              *alignment = MAX(*alignment, __alignof(unsigned short));
 70              break;
 71          }
 72  
 73          case _C_UINT: {
 74              *size += sizeof(unsigned int);
 75              *alignment = MAX(*alignment, __alignof(unsigned int));
 76              break;
 77          }
 78  
 79          case _C_ULNG: {
 80              *size += sizeof(unsigned long);
 81              *alignment = MAX(*alignment, __alignof(unsigned long));
 82              break;
 83          }
 84  
 85          case _C_ULNG_LNG: {
 86              *size += sizeof(unsigned long long);
 87              *alignment = MAX(*alignment, __alignof(unsigned long long));
 88              break;
 89          }
 90  
 91          case _C_BOOL: {
 92              *size += sizeof(BOOL);
 93              *alignment = MAX(*alignment, __alignof(BOOL));
 94              break;
 95          }
 96  
 97          case _C_FLT: {
 98              *size += sizeof(float);
 99              *alignment = MAX(*alignment, __alignof(float));
100              break;
101          }
102  
103          case _C_DBL: {
104              *size += sizeof(double);
105              *alignment = MAX(*alignment, __alignof(double));
106              break;
107          }
108  
109          case _C_LNG_DBL: {
110              *size += sizeof(long double);
111              *alignment = MAX(*alignment, __alignof(long double));
112              break;
113          }
114  
115          case _C_VOID: {
116              // Apple claims a size and alignment of 0 for void. A
117              // GCCism does allow sizeof(void), but it doesn't do the
118              // right thing anyway.
119              break;
120          }
121  
122          case _C_CHARPTR: {
123              *size += sizeof(char *);
124              *alignment += sizeof(char *);
125              break;
126          }
127  
128          case _C_ID: {
129              *size += sizeof(id);
130              *alignment = MAX(*alignment, __alignof(id));
131              if (decl[0] == _C_UNDEF)
132              {
133                  ++decl;
134              }
135              break;
136          }
137  
138          case _C_CLASS: {
139              *size += sizeof(Class);
140              *alignment = MAX(*alignment, __alignof(Class));
141              break;
142          }
143  
144          case _C_SEL: {
145              *size += sizeof(SEL);
146              *alignment = MAX(*alignment, __alignof(SEL));
147              break;
148          }
149  
150          case _C_PTR: {
151              *size += sizeof(void *);
152              *alignment = MAX(*alignment, __alignof(void *));
153              if (decl[0] == _C_UNDEF) {
154                  //special-case ^? - _C_UNDEF is an error anywhere but after a _C_PTR or _C_ID (if found in structs, it's just a ?, not a token)
155                  ++decl;
156              }
157              else {
158                  // All pointers are the same size, but we still have to
159                  // consume the rest of the type.
160                  NSUInteger dummy_size = 0;
161                  NSUInteger dummy_alignment = 0;
162                  decl = __NSGetSizeAndAlignment(decl, &dummy_size, &dummy_alignment, stripSizeHints);
163              }
164              break;
165          }
166  
167          case _C_ARY_B: {
168              // strol() Just Works, as Apple does parse "[i]" as an array
169              // of 0 ints. They also only allow decimal sizes (no hex or
170              // any such thing).
171              size_t count = strtol((char *)decl, (char **)&decl, 10);
172  
173              // The alignment is the same as the alignment of the member
174              // type. The size of an array is the count times the size of
175              // an array member, rounded up to a multiple of its alignment.
176              decl = __NSGetSizeAndAlignment(decl, size, alignment, stripSizeHints);
177              *size *= count;
178  
179              // Consume the ']'.
180              ++decl;
181              break;
182          }
183  
184          case _C_STRUCT_B: {
185              // The struct name is delineated either by '=', or by '}' for
186              // opaque types.
187              while (*decl != 0 && !strchr("=}", *decl)) {
188                  decl++;
189              }
190  
191              // Consume the '=' or '}'.
192              if (*(decl++) == _C_STRUCT_E)
193              {
194                  break;
195              }
196  
197              while (*decl != _C_STRUCT_E) {
198                  NSUInteger field_size = 0;
199                  NSUInteger field_alignment = 0;
200                  decl = __NSGetSizeAndAlignment(decl, &field_size, &field_alignment, stripSizeHints);
201  
202                  // Align the field, by rounding *size up to a multiple of
203                  // field_alignment.
204                  if (field_size > 0)
205                  {
206                      size_t unalignment = *size % field_alignment;
207  
208                      if (unalignment > 0)
209                      {
210                          *size += field_alignment - unalignment;
211                      }
212                  }
213  
214                  // Add the size of the field, and possibly increase the
215                  // alignment of the struct.
216                  *size += field_size;
217                  *alignment = MAX(*alignment, field_alignment);
218              }
219  
220              // The size must be a multiple of the alignment.
221              if (*size > 0 && *alignment > 0)
222              {
223                  size_t unalignment = *size % *alignment;
224                  if (unalignment > 0)
225                  {
226                      *size += *alignment - unalignment;
227                  }
228              }
229  
230              // Consume the '}'.
231              ++decl;
232              break;
233          }
234  
235          case _C_UNION_B: {
236              // The union name is delineated either by '=', or by ')' for
237              // opaque types.
238              while (*decl != 0 && !strchr("=)", *decl)) {
239                  decl++;
240              }
241  
242              // Consume the '=' or ')'.
243              if (*(decl++) == _C_UNION_E)
244              {
245                  break;
246              }
247  
248              while (*decl != _C_UNION_E) {
249                  NSUInteger field_size = 0;
250                  NSUInteger field_alignment = 0;
251                  decl = __NSGetSizeAndAlignment(decl, &field_size, &field_alignment, stripSizeHints);
252  
253                  // The size and alignment are the maximum among all the fields.
254                  *size = MAX(*size, field_size);
255                  *alignment = MAX(*alignment, field_alignment);
256              }
257  
258              // Consume the ')'.
259              ++decl;
260              break;
261          }
262  
263          case _C_BFLD: // TODO throw an exception on bitfields.
264          case 'j': // Unsupported
265          default: {
266              @throw [NSException exceptionWithName:NSInvalidArgumentException reason:nil userInfo:nil];
267          }
268      }
269  
270      if (stripSizeHints)
271      {
272          // Read off the advisory size.
273          strtol((char *)decl, (char **)&decl, 10);
274      }
275  
276      return decl;
277  }
278  
279  const char *NSGetSizeAndAlignment(const char *decl, NSUInteger *sizep, NSUInteger *alignp)
280  {
281      assert(decl != NULL);
282  
283      NSUInteger size = 0;
284      NSUInteger alignment = 0;
285  
286      if (*decl != '\0')
287      {
288          decl = __NSGetSizeAndAlignment(decl, &size, &alignment, NO);
289      }
290  
291      if (sizep != NULL) {
292          *sizep = size;
293      }
294      
295      if (alignp != NULL) {
296          *alignp = alignment;
297      }
298  
299      return decl;
300  }