// // DPObjCRuntime.m // HigherOrderMessaging // // Created by Ofri Wolfus on 03/11/06. // Copyright 2006 Ofri Wolfus. All rights reserved. // #import "DPObjCRuntime.h" #import #include #if defined(DP_EXTERN_INLINE) #undef DP_EXTERN_INLINE #define DP_EXTERN_INLINE /**/ #endif /*********************************************************************** * _dp_subtypeUntil. * * Delegation. * Code taken from Apple's Objective-C runtime, released under ASPL. **********************************************************************/ static int _dp_subtypeUntil(const char *type, char end) { int level = 0; const char * head = type; // while (*type) { if (!*type || (!level && (*type == end))) return (int)(type - head); switch (*type) { case ']': case '}': case ')': level--; break; case '[': case '{': case '(': level += 1; break; } type += 1; } return 0; } /*********************************************************************** * _dp_skipFirstType. * Code taken from Apple's Objective-C runtime, released under ASPL. **********************************************************************/ static const char * _dp_skipFirstType(const char * type) { while (1) { switch (*type++) { case 'O': /* bycopy */ case 'n': /* in */ case 'o': /* out */ case 'N': /* inout */ case 'r': /* const */ case 'V': /* oneway */ case '^': /* pointers */ break; /* arrays */ case '[': while ((*type >= '0') && (*type <= '9')) type += 1; return type + _dp_subtypeUntil (type, ']') + 1; /* structures */ case '{': return type + _dp_subtypeUntil (type, '}') + 1; /* unions */ case '(': return type + _dp_subtypeUntil (type, ')') + 1; /* basic types */ default: return type; } } } #pragma mark - #pragma mark Objective-C II #if __OBJC2__ unsigned dp_getArgumentInfo(const char *typedesc, int arg, const char** type, int* offset) { unsigned nargs = 0; unsigned self_offset = 0; BOOL offset_is_negative = NO; // First, skip the return type typedesc = _dp_skipFirstType(typedesc); // Next, skip stack size while ((*typedesc >= '0') && (*typedesc <= '9')) typedesc += 1; // Now, we have the arguments - position typedesc to the appropriate argument while (*typedesc && nargs != arg) { // Skip argument type typedesc = _dp_skipFirstType(typedesc); if (nargs == 0) { // Skip GNU runtime's register parameter hint if (*typedesc == '+') typedesc++; // Skip negative sign in offset if (*typedesc == '-') { offset_is_negative = YES; typedesc += 1; } else offset_is_negative = NO; while ((*typedesc >= '0') && (*typedesc <= '9')) self_offset = self_offset * 10 + (*typedesc++ - '0'); if (offset_is_negative) self_offset = -(self_offset); } else { // Skip GNU runtime's register parameter hint if (*typedesc == '+') typedesc++; // Skip (possibly negative) argument offset if (*typedesc == '-') typedesc += 1; while ((*typedesc >= '0') && (*typedesc <= '9')) typedesc += 1; } nargs += 1; } if (*typedesc) { unsigned arg_offset = 0; *type = typedesc; typedesc = _dp_skipFirstType(typedesc); if (arg == 0) { #ifdef hppa *offset = -sizeof(id); #else *offset = 0; #endif // hppa } else { // Skip GNU register parameter hint if (*typedesc == '+') typedesc++; // Pick up (possibly negative) argument offset if (*typedesc == '-') { offset_is_negative = YES; typedesc += 1; } else offset_is_negative = NO; while ((*typedesc >= '0') && (*typedesc <= '9')) arg_offset = arg_offset * 10 + (*typedesc++ - '0'); if (offset_is_negative) arg_offset = - arg_offset; #ifdef hppa // For stacks which grow up, since margs points // to the top of the stack or the END of the args, // the first offset is at -sizeof(id) rather than 0. self_offset += sizeof(id); #endif *offset = arg_offset - self_offset; } } else { *type = 0; *offset = 0; } return nargs; } /* * The following code was taken from Apple's runtime sources * and is covered by the ASPL license. */ unsigned dp_getNumberOfArguments(const char *typedesc) { unsigned nargs; // First, skip the return type typedesc = _dp_skipFirstType(typedesc); // Next, skip stack size while ((*typedesc >= '0') && (*typedesc <= '9')) typedesc += 1; // Now, we have the arguments - count how many nargs = 0; while (*typedesc) { // Traverse argument type typedesc = _dp_skipFirstType(typedesc); // Skip GNU runtime's register parameter hint if (*typedesc == '+') typedesc++; // Traverse (possibly negative) argument offset if (*typedesc == '-') typedesc += 1; while ((*typedesc >= '0') && (*typedesc <= '9')) typedesc += 1; // Made it past an argument nargs += 1; } return nargs; } #ifndef __alpha__ unsigned dp_getSizeOfArguments(const char *typedesc) { unsigned stack_size; #if defined(__ppc__) || defined(ppc) unsigned trueBaseOffset; unsigned foundBaseOffset; #endif // Get our starting points stack_size = 0; // Skip the return type #if defined (__ppc__) || defined(ppc) // Struct returns cause the parameters to be bumped // by a register, so the offset to the receiver is // 4 instead of the normal 0. trueBaseOffset = (*typedesc == '{') ? 4 : 0; #endif typedesc = _dp_skipFirstType(typedesc); // Convert ASCII number string to integer while ((*typedesc >= '0') && (*typedesc <= '9')) stack_size = (stack_size * 10) + (*typedesc++ - '0'); #if defined (__ppc__) || defined(ppc) // NOTE: This is a temporary measure pending a compiler fix. // Work around PowerPC compiler bug wherein the method argument // string contains an incorrect value for the "stack size." // Generally, the size is reported 4 bytes too small, so we apply // that fudge factor. Unfortunately, there is at least one case // where the error is something other than -4: when the last // parameter is a double, the reported stack is much too high // (about 32 bytes). We do not attempt to detect that case. // The result of returning a too-high value is that objc_msgSendv // can bus error if the destination of the marg_list copying // butts up against excluded memory. // This fix disables itself when it sees a correctly built // type string (i.e. the offset for the Id is correct). This // keeps us out of lockstep with the compiler. // skip the '@' marking the Id field typedesc = _dp_skipFirstType(typedesc); // Skip GNU runtime's register parameter hint if (*typedesc == '+') typedesc++; // pick up the offset for the Id field foundBaseOffset = 0; while ((*typedesc >= '0') && (*typedesc <= '9')) foundBaseOffset = (foundBaseOffset * 10) + (*typedesc++ - '0'); // add fudge factor iff the Id field offset was wrong if (foundBaseOffset != trueBaseOffset) stack_size += 4; #endif return stack_size; } #else // __alpha__ unsigned dp_getSizeOfArguments(const char *type) { const char * type; int size; int index; int align; int offset; unsigned stack_size; int nargs; nargs = dp_getNumberOfArguments(type); stack_size = (*method->method_types == '{') ? sizeof(void *) : 0; for (index = 0; index < nargs; index += 1) { (void) dp_getArgumentInfo(type, index, &type, &offset); size = sizeOfType(type, &align); stack_size += ((size + 7) & ~7); } return stack_size; } #endif // __alpha // ===================================================================================== // // ===================================================================================== // // ===================================================================================== // #pragma mark - #pragma mark Objective-C I #else // !__OBJC2__ unsigned dp_getNumberOfArguments(const char *typedesc) { struct objc_method m; m.method_types = (char *)typedesc; return method_getNumberOfArguments(&m); } unsigned dp_getArgumentInfo(const char *types, int argIndex, const char** type, int* offset) { struct objc_method m; m.method_types = (char *)types; return method_getArgumentInfo(&m, argIndex, type, offset); } unsigned dp_getSizeOfArguments(const char *types) { struct objc_method m; m.method_types = (char *)types; return method_getSizeOfArguments(&m); } #endif // __OBJC2__ // ===================================================================================== // // ===================================================================================== // // ===================================================================================== // #pragma mark - #pragma mark Runtime Additions BOOL class_isSubclassOfClass(Class cls, Class superCls) { while (cls) { if (cls == superCls) return YES; cls = class_getSuperclass(cls); } return NO; } BOOL object_respondsToSelector(id object, SEL sel) { return object_isInstance(object) ? class_getInstanceMethod(object, sel) != NULL : class_respondsToSelector(object, sel); } unsigned int sel_getNumberOfArguments(SEL sel) { unsigned int c = 2U; const char *name = sel_getName(sel); for (name = sel_getName(sel); *name; ++name) if (*name == ':') ++c; return c; } // ===================================================================================== // // ===================================================================================== // // ===================================================================================== // #pragma mark - #pragma mark Method Description MethodDescription dp_copyMethodDescription(Method m) { MethodDescription r = malloc(sizeof(struct dp_method_description)); memcpy(r, dp_getMethodDescription(m), sizeof(struct dp_method_description)); return r; } MethodDescription description_createDescription(SEL name, const char *types) { MethodDescription d = malloc(sizeof(struct dp_method_description)); description_setName(d, name); description_setTypes(d, types); return d; } SEL description_getName(MethodDescription desc) { return desc->name; } const char *description_getTypes(MethodDescription desc) { return (const char *)desc->types; } void description_setName(MethodDescription desc, SEL name) { desc->name = name; } void description_setTypes(MethodDescription desc, const char *types) { desc->types = (char *)types; } unsigned description_getNumberOfArguments(MethodDescription d) { return dp_getNumberOfArguments(description_getTypes(d)); } unsigned description_getArgumentInfo(MethodDescription d, int argIndex, const char** type, int* offset) { return dp_getArgumentInfo(description_getTypes(d), argIndex, type, offset); } unsigned description_getSizeOfArguments(MethodDescription d) { return dp_getSizeOfArguments(description_getTypes(d)); } // ===================================================================================== // // ===================================================================================== // // ===================================================================================== // #pragma mark - #pragma mark Other Method dp_getMethod(id obj, SEL sel) { return object_isInstance(obj) ? class_getInstanceMethod(object_getClass(obj), sel) : class_getClassMethod(obj, sel) ?: class_getInstanceMethod(obj, sel); } id dp_msgSendv(id target, SEL sel, unsigned args_size, marg_list args) { Method m = dp_getMethod(target, sel); switch (*method_getTypeEncoding(m) ?: _C_ID) { case _C_STRUCT_B: objc_msgSendv_stret(marg_getValue(args, 0, void *), target, sel, args_size, args); #ifdef __i386__ case _C_FLT: case _C_DBL: //case *@encode(long double): objc_msgSendv_fpret(target, sel, args_size, args); #endif #ifdef __x86_64__ case @encode(long double)[0]: objc_msgSendv_fpret(target, sel, args_size, args); #endif default: return objc_msgSendv(target, sel, args_size, args); } return nil; }