Index: src/OFObject.h ================================================================== --- src/OFObject.h +++ src/OFObject.h @@ -477,10 +477,30 @@ * * \param class The class from which the instance methods should be inherited */ + (void)inheritMethodsFromClass: (Class)class_; +/** + * \brief Try to resolve the specified class method. + * + * This method is called if a class method was not found, so that an + * implementation can be provided at runtime. + * + * \return Whether the method has been added to the class + */ ++ (BOOL)resolveClassMethod: (SEL)selector; + +/** + * \brief Try to resolve the specified instance method. + * + * This method is called if an instance method was not found, so that an + * implementation can be provided at runtime. + * + * \return Whether the method has been added to the class + */ ++ (BOOL)resolveInstanceMethod: (SEL)selector; + /** * \brief Initializes an already allocated object. * * Derived classes may override this, but need to do self = [super init] before * they do any initialization themselves. init may never return nil, instead Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -101,11 +101,11 @@ #if !defined(OF_APPLE_RUNTIME) || defined(__OBJC2__) static void uncaught_exception_handler(id exception) { - fprintf(stderr, "\nUnhandled exception:\n%s\n", + fprintf(stderr, "\nRuntime error: Unhandled exception:\n%s\n", [[exception description] UTF8String]); } #endif static void @@ -113,10 +113,60 @@ { @throw [OFEnumerationMutationException exceptionWithClass: [object class] object: object]; } + +#ifdef OF_OBJFW_RUNTIME +static id +method_not_found_handler(id obj, SEL sel, ...) +{ + fprintf(stderr, "Runtime error: Selector %s is not implemented in " + "class %s!\n", sel_getName(sel), + class_getName(object_getClass(obj))); + abort(); +} + +static IMP +forward_handler(id obj, SEL sel) +{ + if (class_isMetaClass(object_getClass(obj))) { + if (![obj respondsToSelector: @selector(resolveClassMethod:)]) + return method_not_found_handler; + + if (![obj resolveClassMethod: sel]) + return method_not_found_handler; + + if (![obj respondsToSelector: sel]) { + fprintf(stderr, "Runtime error: [%s " + "resolveClassMethod: %s] returned YES without " + "adding the method!\n", class_getName(obj), + sel_getName(sel)); + abort(); + } + } else { + Class c = object_getClass(obj); + + if (![c respondsToSelector: @selector(resolveInstanceMethod:)]) + return method_not_found_handler; + + if (![c resolveInstanceMethod: sel]) + return method_not_found_handler; + + if (![obj respondsToSelector: sel]) { + fprintf(stderr, "Runtime error: [%s " + "resolveInstanceMethod: %s] returned YES without " + "adding the method!\n", + class_getName(object_getClass(obj)), + sel_getName(sel)); + abort(); + } + } + + return objc_msg_lookup(obj, sel); +} +#endif #ifndef HAVE_OBJC_ENUMERATIONMUTATION void objc_enumerationMutation(id object) { @@ -200,10 +250,14 @@ #endif #if !defined(OF_APPLE_RUNTIME) || defined(__OBJC2__) objc_setUncaughtExceptionHandler(uncaught_exception_handler); #endif + +#ifdef OF_OBJFW_RUNTIME + objc_forward_handler = forward_handler; +#endif #ifdef HAVE_OBJC_ENUMERATIONMUTATION objc_setEnumerationMutationHandler(enumeration_mutation_handler); #endif @@ -461,10 +515,20 @@ } #endif [self inheritMethodsFromClass: [class superclass]]; } + ++ (BOOL)resolveClassMethod: (SEL)selector +{ + return NO; +} + ++ (BOOL)resolveInstanceMethod: (SEL)selector +{ + return NO; +} - init { Class class; void (*last)(id, SEL) = NULL;