Traditional Message Forwarding

GCC runtime

Taken from GCC’s <objc/message.h>.

IMP objc_msg_lookup (id receiver, SEL op);

If receiver does not respond to the selector op, objc_msg_lookup() will try to call +resolveClassMethod: or resolveInstanceMethod: as appropriate, and if they return YES, it will try the lookup again (+resolveClassMethod: and +resolveInstanceMethod: can thus install dynamically methods as they are requested). If +resolveClassMethod: or +resolveInstanceMethod: are either not available, or return NO, or return YES but ‘receiver’ still doesn’t implement the ‘selector’ after calling them, the runtime returns a generic “forwarding” function that can be called with the required method signature and which can process the method invocation according to the forwarding API. There are two runtime hooks that allow Foundation libraries (such as GNUstep-Base) to return their own forwarding function in preference to the runtime ones. When that happens, the Foundation library effectively takes complete control of the forwarding process; any method invocation where the selector is not implemented by the receiver will end up calling a forwarding function chosen by the Foundation library.

objc_EXPORT IMP (*__objc_msg_forward)(SEL);
objc_EXPORT IMP (*__objc_msg_forward2)(id, SEL);

Hooks for method forwarding. They make it easy to substitute the built-in forwarding with one based on a library, such as ffi, that implement closures, thereby avoiding gcc’s __builtin_apply problems. __objc_msg_forward2’s result will be preferred over that of __objc_msg_forward if both are set and return non-NULL.

Apple/NeXT runtime

The forward handler is set by the function objc_setForwardHandler. The default forward handler in Objective-C 1.x is nil. The runtime, if it detects a forward handler of nil, will call forward:: on the object. In Objective-C 2.x, the default forward handler prints a message detailing the error and exits the program.

objc_setForwardHandler

Sets the function to be called by objc_msgForward.

Defined in <objc/runtime.h>.

Declaration

void objc_setForwardHandler(void * _Nonnull fwd, void * _Nonnull fwd_stret);

Paramaters

Code snippets

<objc/message.h>

/* Message Forwarding Primitives
 * Use these functions to forward a message as if the receiver did not 
 * respond to it. 
 *
 * The receiver must not be nil.
 * 
 * class_getMethodImplementation() may return (IMP)_objc_msgForward.
 * class_getMethodImplementation_stret() may return (IMP)_objc_msgForward_stret
 * 
 * These functions must be cast to an appropriate function pointer type 
 * before being called. 
 *
 * Before Mac OS X 10.6, _objc_msgForward must not be called directly 
 * but may be compared to other IMP values.
 */
#if !OBJC_OLD_DISPATCH_PROTOTYPES
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincompatible-library-redeclaration"
OBJC_EXPORT void
_objc_msgForward(void /* id receiver, SEL sel, ... */ ) 
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

OBJC_EXPORT void
_objc_msgForward_stret(void /* id receiver, SEL sel, ... */ ) 
    OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0, 2.0)
    OBJC_ARM64_UNAVAILABLE;
#pragma clang diagnostic pop
#else
OBJC_EXPORT id _Nullable
_objc_msgForward(id _Nonnull receiver, SEL _Nonnull sel, ...) 
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

OBJC_EXPORT void
_objc_msgForward_stret(id _Nonnull receiver, SEL _Nonnull sel, ...) 
    OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0, 2.0)
    OBJC_ARM64_UNAVAILABLE;
#endif

objc-runtime.mm

#if !__OBJC2__

// Default forward handler (nil) goes to forward:: dispatch.
void *_objc_forward_handler = nil;
void *_objc_forward_stret_handler = nil;

#else

// Default forward handler halts the process.
__attribute__((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
    _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
                "(no message forward handler is installed)", 
                class_isMetaClass(object_getClass(self)) ? '+' : '-', 
                object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;

#if SUPPORT_STRET
struct stret { int i[100]; };
__attribute__((noreturn, cold)) struct stret
objc_defaultForwardStretHandler(id self, SEL sel)
{
    objc_defaultForwardHandler(self, sel);
}
void *_objc_forward_stret_handler = (void*)objc_defaultForwardStretHandler;
#endif

#endif

void objc_setForwardHandler(void *fwd, void *fwd_stret)
{
    _objc_forward_handler = fwd;
#if SUPPORT_STRET
    _objc_forward_stret_handler = fwd_stret;
#endif
}

References

NeXT Release 3.3 documentation for Object class

NeXT Release 3.3 documentation for runtime functions

Re: objc_setForwardHandler

Apple Objective-C runtime source code (version 818.2)

<objc/runtime.h>

<objc/message.h>

objc-runtime.mm