EXTRuntimeExtensions.m 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. //
  2. // EXTRuntimeExtensions.m
  3. // extobjc
  4. //
  5. // Created by Justin Spahr-Summers on 2011-03-05.
  6. // Copyright (C) 2012 Justin Spahr-Summers.
  7. // Released under the MIT license.
  8. //
  9. #import "EXTRuntimeExtensions.h"
  10. #import <Foundation/Foundation.h>
  11. mtl_propertyAttributes *mtl_copyPropertyAttributes (objc_property_t property) {
  12. const char * const attrString = property_getAttributes(property);
  13. if (!attrString) {
  14. fprintf(stderr, "ERROR: Could not get attribute string from property %s\n", property_getName(property));
  15. return NULL;
  16. }
  17. if (attrString[0] != 'T') {
  18. fprintf(stderr, "ERROR: Expected attribute string \"%s\" for property %s to start with 'T'\n", attrString, property_getName(property));
  19. return NULL;
  20. }
  21. const char *typeString = attrString + 1;
  22. const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL);
  23. if (!next) {
  24. fprintf(stderr, "ERROR: Could not read past type in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  25. return NULL;
  26. }
  27. size_t typeLength = next - typeString;
  28. if (!typeLength) {
  29. fprintf(stderr, "ERROR: Invalid type in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  30. return NULL;
  31. }
  32. // allocate enough space for the structure and the type string (plus a NUL)
  33. mtl_propertyAttributes *attributes = calloc(1, sizeof(mtl_propertyAttributes) + typeLength + 1);
  34. if (!attributes) {
  35. fprintf(stderr, "ERROR: Could not allocate mtl_propertyAttributes structure for attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  36. return NULL;
  37. }
  38. // copy the type string
  39. strncpy(attributes->type, typeString, typeLength);
  40. attributes->type[typeLength] = '\0';
  41. // if this is an object type, and immediately followed by a quoted string...
  42. if (typeString[0] == *(@encode(id)) && typeString[1] == '"') {
  43. // we should be able to extract a class name
  44. const char *className = typeString + 2;
  45. next = strchr(className, '"');
  46. if (!next) {
  47. fprintf(stderr, "ERROR: Could not read class name in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  48. return NULL;
  49. }
  50. if (className != next) {
  51. size_t classNameLength = next - className;
  52. char trimmedName[classNameLength + 1];
  53. strncpy(trimmedName, className, classNameLength);
  54. trimmedName[classNameLength] = '\0';
  55. // attempt to look up the class in the runtime
  56. attributes->objectClass = objc_getClass(trimmedName);
  57. }
  58. }
  59. if (*next != '\0') {
  60. // skip past any junk before the first flag
  61. next = strchr(next, ',');
  62. }
  63. while (next && *next == ',') {
  64. char flag = next[1];
  65. next += 2;
  66. switch (flag) {
  67. case '\0':
  68. break;
  69. case 'R':
  70. attributes->readonly = YES;
  71. break;
  72. case 'C':
  73. attributes->memoryManagementPolicy = mtl_propertyMemoryManagementPolicyCopy;
  74. break;
  75. case '&':
  76. attributes->memoryManagementPolicy = mtl_propertyMemoryManagementPolicyRetain;
  77. break;
  78. case 'N':
  79. attributes->nonatomic = YES;
  80. break;
  81. case 'G':
  82. case 'S':
  83. {
  84. const char *nextFlag = strchr(next, ',');
  85. SEL name = NULL;
  86. if (!nextFlag) {
  87. // assume that the rest of the string is the selector
  88. const char *selectorString = next;
  89. next = "";
  90. name = sel_registerName(selectorString);
  91. } else {
  92. size_t selectorLength = nextFlag - next;
  93. if (!selectorLength) {
  94. fprintf(stderr, "ERROR: Found zero length selector name in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  95. goto errorOut;
  96. }
  97. char selectorString[selectorLength + 1];
  98. strncpy(selectorString, next, selectorLength);
  99. selectorString[selectorLength] = '\0';
  100. name = sel_registerName(selectorString);
  101. next = nextFlag;
  102. }
  103. if (flag == 'G')
  104. attributes->getter = name;
  105. else
  106. attributes->setter = name;
  107. }
  108. break;
  109. case 'D':
  110. attributes->dynamic = YES;
  111. attributes->ivar = NULL;
  112. break;
  113. case 'V':
  114. // assume that the rest of the string (if present) is the ivar name
  115. if (*next == '\0') {
  116. // if there's nothing there, let's assume this is dynamic
  117. attributes->ivar = NULL;
  118. } else {
  119. attributes->ivar = next;
  120. next = "";
  121. }
  122. break;
  123. case 'W':
  124. attributes->weak = YES;
  125. break;
  126. case 'P':
  127. attributes->canBeCollected = YES;
  128. break;
  129. case 't':
  130. fprintf(stderr, "ERROR: Old-style type encoding is unsupported in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  131. // skip over this type encoding
  132. while (*next != ',' && *next != '\0')
  133. ++next;
  134. break;
  135. default:
  136. fprintf(stderr, "ERROR: Unrecognized attribute string flag '%c' in attribute string \"%s\" for property %s\n", flag, attrString, property_getName(property));
  137. }
  138. }
  139. if (next && *next != '\0') {
  140. fprintf(stderr, "Warning: Unparsed data \"%s\" in attribute string \"%s\" for property %s\n", next, attrString, property_getName(property));
  141. }
  142. if (!attributes->getter) {
  143. // use the property name as the getter by default
  144. attributes->getter = sel_registerName(property_getName(property));
  145. }
  146. if (!attributes->setter) {
  147. const char *propertyName = property_getName(property);
  148. size_t propertyNameLength = strlen(propertyName);
  149. // we want to transform the name to setProperty: style
  150. size_t setterLength = propertyNameLength + 4;
  151. char setterName[setterLength + 1];
  152. strncpy(setterName, "set", 3);
  153. strncpy(setterName + 3, propertyName, propertyNameLength);
  154. // capitalize property name for the setter
  155. setterName[3] = (char)toupper(setterName[3]);
  156. setterName[setterLength - 1] = ':';
  157. setterName[setterLength] = '\0';
  158. attributes->setter = sel_registerName(setterName);
  159. }
  160. return attributes;
  161. errorOut:
  162. free(attributes);
  163. return NULL;
  164. }