diff --git a/Source/NSProtocolChecker.m b/Source/NSProtocolChecker.m
index de215e9d5..8639acece 100644
--- a/Source/NSProtocolChecker.m
+++ b/Source/NSProtocolChecker.m
@@ -1,9 +1,9 @@
/** Implementation of NSProtocolChecker for GNUStep
Copyright (C) 1995 Free Software Foundation, Inc.
- Written by: Mike Kienenberger
+ Original by: Mike Kienenberger
Date: Jun 1998
- Rewrite: Richard Frith-Macdonald
+ Written: Richard Frith-Macdonald
Date: April 2004
This file is part of the GNUstep Base Library.
@@ -36,11 +36,9 @@
@implementation NSProtocolChecker
/**
- * Allocates and initializes an NSProtocolChecker instance that will
- * forward any messages in the aProtocol protocol to anObject, its
- * target. Thus, the checker can be vended in lieu of anObject to
- * restrict the messages that can be sent to anObject. Returns the
- * new instance.
+ * Allocates and initializes an NSProtocolChecker instance by calling
+ * -initWithTarget:protocol:
+ * Autoreleases and returns the new instance.
*/
+ (id) protocolCheckerWithTarget: (NSObject*)anObject
protocol: (Protocol*)aProtocol
@@ -55,24 +53,65 @@
[super dealloc];
}
+- (struct objc_method_description*) _methodDescription: (SEL)aSelector
+{
+ extern struct objc_method_description *GSDescriptionForInstanceMethod();
+ extern struct objc_method_description *GSDescriptionForClassMethod();
+
+ if (_myProtocol != nil && _myTarget != nil)
+ {
+ struct objc_method_description* mth;
+
+ /* Older gcc versions may not initialise Protocol objects properly
+ * so we have an evil hack which checks for a known bad value of
+ * the class pointer, and uses an internal function
+ * (implemented in NSObject.m) to examine the protocol contents
+ * without sending any ObjectiveC message to it.
+ */
+ if (GSObjCIsInstance(_myTarget))
+ {
+ if ((int)GSObjCClass(_myProtocol) == 0x2)
+ {
+ mth = GSDescriptionForInstanceMethod(_myProtocol, aSelector);
+ }
+ else
+ {
+ mth = [_myProtocol descriptionForInstanceMethod: aSelector];
+ }
+ }
+ else
+ {
+ if ((int)GSObjCClass(_myProtocol) == 0x2)
+ {
+ mth = GSDescriptionForClassMethod(_myProtocol, aSelector);
+ }
+ else
+ {
+ mth = [_myProtocol descriptionForClassMethod: aSelector];
+ }
+ }
+ return mth;
+ }
+ return 0;
+}
+
/*
* Forwards any message to the delegate if the method is declared in
* the checker's protocol; otherwise raises an NSInvalidArgumentException.
*/
- (void) forwardInvocation: (NSInvocation*)anInvocation
{
- if (GSObjCIsInstance(_myTarget))
+ const char *type;
+
+ if ([self _methodDescription: [anInvocation selector]] == 0)
{
- if (![_myProtocol descriptionForInstanceMethod: [anInvocation selector]])
+ if (GSObjCIsInstance(_myTarget))
{
[NSException raise: NSInvalidArgumentException
format: @"<%s -%@> not declared",
[_myProtocol name], NSStringFromSelector([anInvocation selector])];
}
- }
- else
- {
- if (![_myProtocol descriptionForClassMethod: [anInvocation selector]])
+ else
{
[NSException raise: NSInvalidArgumentException
format: @"<%s +%@> not declared",
@@ -80,6 +119,23 @@
}
}
[anInvocation invokeWithTarget: _myTarget];
+
+ /*
+ * If the method returns 'self' (ie the target object) replace the
+ * returned value with the protocol checker.
+ */
+ type = [[anInvocation methodSignature] methodReturnType];
+ if (strcmp(type, @encode(id)) == 0)
+ {
+ id buf;
+
+ [anInvocation getReturnValue: &buf];
+ if (buf == _myTarget)
+ {
+ buf = self;
+ [anInvocation setReturnValue: &buf];
+ }
+ }
}
- (id) init
@@ -92,9 +148,10 @@
* Initializes a newly allocated NSProtocolChecker instance that will
* forward any messages in the aProtocol protocol to anObject, its
* delegate. Thus, the checker can be vended in lieu of anObject to
- * restrict the messages that can be sent to anObject. If anObject is
- * allowed to be freed or dereferenced by clients, the free method
- * should be included in aProtocol. Returns the new instance.
+ * restrict the messages that can be sent to anObject. If any method
+ * in the protocol returns anObject, the checker will replace the returned
+ * value with itsself rather than the target object.
+ * Returns the new instance.
*/
- (id) initWithTarget: (NSObject*)anObject protocol: (Protocol*)aProtocol
{
@@ -110,11 +167,105 @@
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
{
- if (aSelector == _cmd || [self respondsToSelector: aSelector] == YES)
+ const char *types;
+ struct objc_method *mth;
+ Class c;
+
+ if (aSelector == 0)
+ [NSException raise: NSInvalidArgumentException
+ format: @"%@ null selector given", NSStringFromSelector(_cmd)];
+
+ /*
+ * Evil hack to prevent recursion - if we are asking a remote
+ * object for a method signature, we can't ask it for the
+ * signature of methodSignatureForSelector:, so we hack in
+ * the signature required manually :-(
+ */
+ if (sel_eq(aSelector, _cmd))
{
- return [_myTarget methodSignatureForSelector: aSelector];
+ static NSMethodSignature *sig = nil;
+
+ if (sig == nil)
+ {
+ sig = [NSMethodSignature signatureWithObjCTypes: "@@::"];
+ RETAIN(sig);
+ }
+ return sig;
}
- return nil;
+
+ if (_myProtocol != nil)
+ {
+ const char *types = 0;
+ struct objc_method_description *desc;
+
+ desc = [self _methodDescription: aSelector];
+ if (desc != 0)
+ {
+ types = desc->types;
+ }
+ if (types == 0)
+ {
+ return nil;
+ }
+ return [NSMethodSignature signatureWithObjCTypes: types];
+ }
+
+ c = GSObjCClass(self);
+ mth = GSGetInstanceMethod(c, aSelector);
+ if (mth == 0)
+ {
+ return nil; // Method not implemented
+ }
+ types = mth->method_types;
+
+ /*
+ * If there are protocols that this class conforms to,
+ * the method may be listed in a protocol with more
+ * detailed type information than in the class itsself
+ * and we must therefore use the information from the
+ * protocol.
+ * This is because protocols also carry information
+ * used by the Distributed Objects system, which the
+ * runtime does not maintain in classes.
+ */
+ if (c->protocols != 0)
+ {
+ struct objc_protocol_list *protocols = c->protocols;
+ BOOL found = NO;
+
+ while (found == NO && protocols != 0)
+ {
+ unsigned i = 0;
+
+ while (found == NO && i < protocols->count)
+ {
+ Protocol *p;
+ struct objc_method_description *pmth;
+
+ p = protocols->list[i++];
+ if (c == (Class)self)
+ {
+ pmth = [p descriptionForClassMethod: aSelector];
+ }
+ else
+ {
+ pmth = [p descriptionForInstanceMethod: aSelector];
+ }
+ if (pmth != 0)
+ {
+ types = pmth->types;
+ found = YES;
+ }
+ }
+ protocols = protocols->next;
+ }
+ }
+
+ if (types == 0)
+ {
+ return nil;
+ }
+ return [NSMethodSignature signatureWithObjCTypes: types];
}
/**
@@ -126,25 +277,6 @@
return _myProtocol;
}
-- (BOOL) respondsToSelector: (SEL)aSelector
-{
- if (GSObjCIsInstance(_myTarget))
- {
- if ([_myProtocol descriptionForInstanceMethod: aSelector])
- {
- return YES;
- }
- }
- else
- {
- if ([_myProtocol descriptionForClassMethod: aSelector])
- {
- return YES;
- }
- }
- return NO;
-}
-
/**
* Returns the target of the NSProtocolChecker.
*/