libs-base/Source/NSInvocation.m
theraven 85549433db A huge blob of changes to make -base build with libobjc2, without using the old libobjc2 headers.
Most of these changes involve simply removing direct manipulation of any runtime structures and replacing them with runtime function calls.  For example class->name becomes class_getName(class) and so on.

libobjc2, like the Apple runtime, the NeXT runtime, and every version of the Objective-C spec, calls the class pointer in id isa.  A few files now have #define class_pointer isa at the top.  This line replaces class_pointer in the old GNU libobjc headers with isa so either class_pointer or isa can be used for accessing the class of an object.  Note: object_getClass() should be used in most cases because, in some future version of the runtime, this will skip things like lock classes and other hidden classes (e.g. KVO classes).

All of the old forwarding stuff has been removed.  Most of this stuff followed convoluted code paths that ended with an exception.  A few simply broke in exciting ways.  Hopefully no one has used them for the last ten years or so, but we can bring them back with some #ifndef stuff if they're really needed by anyone.

There is currently a bug in configure, which prevents dladdr() from being detected, so you need to manually tweak config.h to build - I have not fixed the fall-back code in objc-load.m to work with libobjc2, I just added a new version that uses the loader's functionality directly.  

Although -base now builds, it builds with a lot of warnings.  <string.h> is missing from a lot of files, so memcpy() and strlen() generate implicit function declaration warnings.  

Additionally, libobjc2 does still provide the sel_{get,register}_*() functions, but they're wrappers around the newer API ones.  These are deprecated and are not exposed in the headers.  Although they work, we should be replacing them with the libobjc2 versions as soon as possible.

This incorporates a patch by Eric.



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@31265 72102866-910b-0410-8b05-ffd578937521
2010-09-09 15:06:09 +00:00

881 lines
20 KiB
Objective-C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** Implementation of NSInvocation for GNUStep
Copyright (C) 1998,2003 Free Software Foundation, Inc.
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: August 1998
Based on code by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
<title>NSInvocation class reference</title>
$Date$ $Revision$
*/
#import "common.h"
#define EXPOSE_NSInvocation_IVARS 1
#import "Foundation/NSException.h"
#import "Foundation/NSCoder.h"
#import "Foundation/NSData.h"
#import "Foundation/NSInvocation.h"
#import "Foundation/NSZone.h"
#import "GSInvocation.h"
#import "GSPrivate.h"
#import "GNUstepBase/NSObject+GNUstepBase.h"
#if defined(USE_LIBFFI)
#include "cifframe.h"
#elif defined(USE_FFCALL)
#include "callframe.h"
#endif
#if defined(HAVE_SYS_MMAN_H)
#include <sys/mman.h>
#endif
#if defined(HAVE_UNISTD_H)
#import <unistd.h>
#endif
#if defined(HAVE_MMAP)
# if !defined(MAP_ANONYMOUS)
# if defined(MAP_ANON)
# define MAP_ANONYMOUS MAP_ANON
# else
# undef HAVE_MMAP
# endif
# endif
#endif
@implementation GSCodeBuffer
+ (GSCodeBuffer*) memoryWithSize: (NSUInteger)_size
{
return [[[self alloc] initWithSize: _size] autorelease];
}
- (void*) buffer
{
return buffer;
}
- (void) dealloc
{
if (size > 0)
{
#if defined(HAVE_MMAP)
munmap(buffer, size);
#else
#if !defined(__MINGW__) && defined(HAVE_MPROTECT)
if (mprotect(buffer, NSPageSize(), PROT_READ|PROT_WRITE) == -1)
{
NSLog(@"Failed to protect memory as writable: %@", [NSError _last]);
}
#endif
NSDeallocateMemoryPages(buffer, NSPageSize());
#endif
}
[super dealloc];
}
- (id) initWithSize: (NSUInteger)_size
{
NSAssert(_size > 0, @"Tried to allocate zero length buffer.");
NSAssert(_size <= NSPageSize(), @"Tried to allocate more than one page.");
#if defined(HAVE_MMAP)
#if defined(HAVE_MPROTECT)
/* We have mprotect, so we create memory as writable and change it to
* executable later (writable and executable may not be possible at
* the same time).
*/
buffer = mmap (NULL, _size, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
#else
/* We do not have mprotect, so we have to try to create writable and
* executable memory.
*/
buffer = mmap (NULL, _size, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
#endif /* HAVE_MPROTECT */
if (buffer == (void*)-1) buffer = (void*)0;
#else
buffer = NSAllocateMemoryPages(NSPageSize());
#endif /* HAVE_MMAP */
if (buffer == (void*)0)
{
NSLog(@"Failed to map %u bytes for execute: %@", _size, [NSError _last]);
buffer = 0;
[self dealloc];
self = nil;
}
else
{
size = _size;
}
return self;
}
/* Ensure that the protection on the buffer is such that it will execute
* on any architecture.
*/
- (void) protect
{
#if defined(__MINGW__)
DWORD old;
if (VirtualProtect(buffer, size, PAGE_EXECUTE, &old) == 0)
{
NSLog(@"Failed to protect memory as executable: %@", [NSError _last]);
}
#elif defined(HAVE_MPROTECT)
if (mprotect(buffer, NSPageSize(), PROT_READ|PROT_EXEC) == -1)
{
NSLog(@"Failed to protect memory as executable: %@", [NSError _last]);
}
#endif
}
@end
static Class NSInvocation_abstract_class;
static Class NSInvocation_concrete_class;
@interface GSInvocationProxy
{
@public
Class isa;
id target;
NSInvocation *invocation;
}
+ (id) _newWithTarget: (id)t;
- (NSInvocation*) _invocation;
- (void) forwardInvocation: (NSInvocation*)anInvocation;
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector;
@end
@interface GSMessageProxy : GSInvocationProxy
@end
#define _inf ((NSArgumentInfo*)_info)
/**
* <p>The <code>NSInvocation</code> class implements a mechanism of constructing
* messages (as <code>NSInvocation</code> instances), sending these to other
* objects, and handling the returned values.
* </p>
* <p>An <code>NSInvocation</code> object may contain a target object to which a
* message can be sent, or may send the message to an arbitrary object.<br />
* Each message consists of a selector for that method and an argument
* list. Once the message has been sent, the invocation will contain
* a return value whose contents may be copied out of it.
* </p>
* <p>The target, selector, and arguments of an instance be constructed
* dynamically, providing a great deal of power/flexibility.
* </p>
* <p>The sending of the message to the target object (using the -invoke
* or -invokeWithTarget: method) can be done at any time, but a standard
* use of this is by the [NSObject-forwardInvocation:] method which is
* called whenever a method is not implemented by the class of the
* object to which it was sent.
* </p>
* <p>Related to the class are two convenience macros ... NS_MESSAGE()
* and NS_INVOCATION() ... to allow easy construction of invocations
* with all the arguments set up.
* </p>
*/
@implementation NSInvocation
#ifdef USE_LIBFFI
static inline void
_get_arg(NSInvocation *inv, int index, void *buffer)
{
cifframe_get_arg((cifframe_t *)inv->_cframe, index, buffer,
((NSArgumentInfo*)inv->_info)[index+1].size);
}
static inline void
_set_arg(NSInvocation *inv, int index, void *buffer)
{
cifframe_set_arg((cifframe_t *)inv->_cframe, index, buffer,
((NSArgumentInfo*)inv->_info)[index+1].size);
}
static inline void *
_arg_addr(NSInvocation *inv, int index)
{
return cifframe_arg_addr((cifframe_t *)inv->_cframe, index);
}
#elif defined(USE_FFCALL)
static inline void
_get_arg(NSInvocation *inv, int index, void *buffer)
{
callframe_get_arg((callframe_t *)inv->_cframe, index, buffer,
((NSArgumentInfo*)inv->_info)[index+1].size);
}
static inline void
_set_arg(NSInvocation *inv, int index, void *buffer)
{
callframe_set_arg((callframe_t *)inv->_cframe, index, buffer,
((NSArgumentInfo*)inv->_info)[index+1].size);
}
static inline void *
_arg_addr(NSInvocation *inv, int index)
{
return callframe_arg_addr((callframe_t *)inv->_cframe, index);
}
#else
static inline void
_get_arg(NSInvocation *inv, int index, void *buffer)
{
}
static inline void
_set_arg(NSInvocation *inv, int index, void *buffer)
{
}
static inline void *
_arg_addr(NSInvocation *inv, int index)
{
return 0;
}
#endif
+ (id) allocWithZone: (NSZone*)aZone
{
if (self == NSInvocation_abstract_class)
{
return NSAllocateObject(NSInvocation_concrete_class, 0, aZone);
}
else
{
return NSAllocateObject(self, 0, aZone);
}
}
+ (void) initialize
{
if (self == [NSInvocation class])
{
NSInvocation_abstract_class = self;
#if defined(USE_LIBFFI)
NSInvocation_concrete_class = [GSFFIInvocation class];
#elif defined(USE_FFCALL)
NSInvocation_concrete_class = [GSFFCallInvocation class];
#else
NSInvocation_concrete_class = [GSDummyInvocation class];
#endif
}
}
/**
* Returns an invocation instance which can be used to send messages to
* a target object using the described signature.<br />
* You must set the target and selector (using -setTarget: and -setSelector:)
* before you attempt to use the invocation.<br />
* Raises an NSInvalidArgumentException if the signature is nil.
*/
+ (NSInvocation*) invocationWithMethodSignature: (NSMethodSignature*)_signature
{
return AUTORELEASE([[NSInvocation_concrete_class alloc]
initWithMethodSignature: _signature]);
}
- (void) dealloc
{
if (_targetRetained)
{
_targetRetained = NO;
RELEASE(_target);
}
if (_argsRetained)
{
_argsRetained = NO;
if (_cframe && _sig)
{
unsigned int i;
for (i = 3; i <= _numArgs; i++)
{
if (*_inf[i].type == _C_CHARPTR)
{
char *str = 0;
_get_arg(self, i-1, &str);
if (str != 0)
{
NSZoneFree(NSDefaultMallocZone(), str);
}
}
else if (*_inf[i].type == _C_ID)
{
id obj = nil;
_get_arg(self, i-1, &obj);
RELEASE(obj);
}
}
}
}
CLEAR_RETURN_VALUE_IF_OBJECT;
#if defined(USE_LIBFFI)
if (_cframe)
{
/* If we get here then we are not using GC, so the _frame instance
* variable points to a mutable data object containing _cframe and
* we can release it.
*/
[((GSFFIInvocation*)self)->_frame release];
}
#elif defined(USE_FFCALL)
if (_cframe)
{
NSZoneFree(NSDefaultMallocZone(), _cframe);
}
#endif
if (_retptr)
{
NSZoneFree(NSDefaultMallocZone(), _retptr);
}
RELEASE(_sig);
[super dealloc];
}
/**
* Copies the argument identified by index into the memory location specified
* by the buffer argument.<br />
* An index of zero is the target object, an index of one is the selector,
* so the actual method arguments start at index 2.
*/
- (void) getArgument: (void*)buffer
atIndex: (NSInteger)index
{
if ((NSUInteger)index >= _numArgs)
{
[NSException raise: NSInvalidArgumentException
format: @"bad invocation argument index"];
}
if (index == 0)
{
*(id*)buffer = _target;
}
else if (index == 1)
{
*(SEL*)buffer = _selector;
}
else
{
_get_arg(self, index, buffer);
}
}
/**
* Copies the invocations return value to the location pointed to by buffer
* if a return value has been set (see the -setReturnValue: method).<br />
* If there isn't a return value then this method raises an exception.
*/
- (void) getReturnValue: (void*)buffer
{
const char *type;
if (_validReturn == NO)
{
[NSException raise: NSGenericException
format: @"getReturnValue with no value set"];
}
type = [_sig methodReturnType];
if (*_inf[0].type != _C_VOID)
{
memcpy(buffer, _retval, _inf[0].size);
}
}
/**
* Returns the selector of the invocation (the argument at index 1)
*/
- (SEL) selector
{
return _selector;
}
/**
* Sets the argument identified by index from the memory location specified
* by the buffer argument.<br />
* Using an index of 0 is equivalent to calling -setTarget: and using an
* argument of 1 is equivalent to -setSelector:<br />
* Proper arguments start at index 2.<br />
* NB. Unlike -setTarget: and -setSelector: the value of buffer must be
* <em>a pointer to</em> the argument to be set in the invocation.<br />
* If -retainArguments was called, then any object argument set in the
* receiver is retained by it.
*/
- (void) setArgument: (void*)buffer
atIndex: (NSInteger)index
{
if ((NSUInteger)index >= _numArgs)
{
[NSException raise: NSInvalidArgumentException
format: @"bad invocation argument index"];
}
if (index == 0)
{
[self setTarget: *(id*)buffer];
}
else if (index == 1)
{
[self setSelector: *(SEL*)buffer];
}
else
{
int i = index+1; /* Allow for return type in '_inf' */
const char *type = _inf[i].type;
if (_argsRetained && (*type == _C_ID || *type == _C_CHARPTR))
{
if (*type == _C_ID)
{
id old;
_get_arg(self, index, &old);
_set_arg(self, index, buffer);
IF_NO_GC(RETAIN(*(id*)buffer));
if (old != nil)
{
RELEASE(old);
}
}
else
{
char *oldstr;
char *newstr = *(char**)buffer;
_get_arg(self, index, &oldstr);
if (newstr == 0)
{
_set_arg(self, index, buffer);
}
else
{
char *tmp;
tmp = NSZoneMalloc(NSDefaultMallocZone(), strlen(newstr)+1);
strcpy(tmp, newstr);
_set_arg(self, index, tmp);
}
if (oldstr != 0)
{
NSZoneFree(NSDefaultMallocZone(), oldstr);
}
}
}
else
{
_set_arg(self, index, buffer);
}
}
}
/**
* Sets the return value of the invocation to the item that buffer points to.
*/
- (void) setReturnValue: (void*)buffer
{
const char *type;
type = _inf[0].type;
CLEAR_RETURN_VALUE_IF_OBJECT;
if (*type != _C_VOID)
{
memcpy(_retval, buffer, _inf[0].size);
}
RETAIN_RETURN_VALUE;
_validReturn = YES;
}
/**
* Sets the selector for the invocation.
*/
- (void) setSelector: (SEL)aSelector
{
_selector = aSelector;
}
/**
* Sets the target object for the invocation.<br />
* If -retainArguments was called, then the target is retained.
*/
- (void) setTarget: (id)anObject
{
if (_targetRetained)
{
ASSIGN(_target, anObject);
}
else
{
_target = anObject;
}
}
/**
* Returns the target object of the invocation.
*/
- (id) target
{
return _target;
}
/**
* Returns a flag to indicate whether object arguments of the invocation
* (including its target) are retained by the invocation.
*/
- (BOOL) argumentsRetained
{
return _argsRetained;
}
/**
* Instructs the invocation to retain its object arguments (including the
* target). The default is not to retain them.
*/
- (void) retainArguments
{
[self retainArgumentsIncludingTarget: YES];
}
/**
* Returns YES if target has been retained yet, NO otherwise.
*/
- (BOOL) targetRetained
{
return _targetRetained;
}
/**
* Similar to -[NSInvocation retainArguments], but allows the sender to
* explicitly control whether the target is retained as well. Retaining
* the target is sometimes not desirable (such as in NSUndoManager), as
* retain loops could result.
*/
- (void) retainArgumentsIncludingTarget: (BOOL)retainTargetFlag
{
if (_argsRetained == NO)
{
unsigned int i;
_argsRetained = YES;
if (_cframe == 0)
{
return;
}
for (i = 3; i <= _numArgs; i++)
{
if (*_inf[i].type == _C_ID)
{
id old;
_get_arg(self, i-1, &old);
if (old != nil)
{
IF_NO_GC(RETAIN(old));
}
}
else if (*_inf[i].type == _C_CHARPTR)
{
char *str;
_get_arg(self, i-1, &str);
if (str != 0)
{
char *tmp;
tmp = NSZoneMalloc(NSDefaultMallocZone(), strlen(str)+1);
strcpy(tmp, str);
_set_arg(self, i-1, &tmp);
}
}
}
}
if (retainTargetFlag && _targetRetained == NO)
{
_targetRetained = YES;
IF_NO_GC(RETAIN(_target));
}
}
/**
* Sends the message encapsulated in the invocation to its target.
*/
- (void) invoke
{
[self invokeWithTarget: _target];
}
/**
* Sends the message encapsulated in the invocation to anObject.
*/
- (void) invokeWithTarget: (id)anObject
{
[self subclassResponsibility: _cmd];
}
/**
* Returns the method signature of the invocation.
*/
- (NSMethodSignature*) methodSignature
{
return _sig;
}
- (NSString*) description
{
/*
* Don't use -[NSString stringWithFormat:] method because it can cause
* infinite recursion.
*/
char buffer[1024];
snprintf (buffer, 1024, "<%s %p selector: %s target: %s>", \
GSClassNameFromObject(self), \
self, \
_selector ? sel_getName(_selector) : "nil", \
_target ? class_getName([_target class]) : "nil" \
);
return [NSString stringWithUTF8String: buffer];
}
- (void) encodeWithCoder: (NSCoder*)aCoder
{
const char *types = [_sig methodType];
unsigned int i;
[aCoder encodeValueOfObjCType: @encode(char*)
at: &types];
[aCoder encodeObject: _target];
[aCoder encodeValueOfObjCType: _inf[2].type
at: &_selector];
for (i = 3; i <= _numArgs; i++)
{
const char *type = _inf[i].type;
void *datum;
datum = _arg_addr(self, i-1);
if (*type == _C_ID)
{
[aCoder encodeObject: *(id*)datum];
}
else
{
[aCoder encodeValueOfObjCType: type at: datum];
}
}
if (*_inf[0].type != _C_VOID)
{
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &_validReturn];
if (_validReturn)
{
[aCoder encodeValueOfObjCType: _inf[0].type at: _retval];
}
}
}
- (id) initWithCoder: (NSCoder*)aCoder
{
NSMethodSignature *newSig;
const char *types;
void *datum;
unsigned int i;
[aCoder decodeValueOfObjCType: @encode(char*) at: &types];
newSig = [NSMethodSignature signatureWithObjCTypes: types];
NSZoneFree(NSDefaultMallocZone(), (void*)types);
DESTROY(self);
self = RETAIN([NSInvocation invocationWithMethodSignature: newSig]);
[aCoder decodeValueOfObjCType: @encode(id) at: &_target];
[aCoder decodeValueOfObjCType: @encode(SEL) at: &_selector];
for (i = 3; i <= _numArgs; i++)
{
datum = _arg_addr(self, i-1);
[aCoder decodeValueOfObjCType: _inf[i].type at: datum];
}
_argsRetained = YES;
if (*_inf[0].type != _C_VOID)
{
[aCoder decodeValueOfObjCType: @encode(BOOL) at: &_validReturn];
if (_validReturn)
{
[aCoder decodeValueOfObjCType: _inf[0].type at: _retval];
}
}
return self;
}
@end
/**
* Provides some minor extensions and some utility methods to aid
* integration of <code>NSInvocation</code> with the Objective-C runtime.
*/
@implementation NSInvocation (GNUstep)
- (BOOL) sendsToSuper
{
return _sendToSuper;
}
- (void) setSendsToSuper: (BOOL)flag
{
_sendToSuper = flag;
}
@end
/**
* These methods are for internal use only ... not public API<br />
* They are used by the NS_INVOCATION() and NS_MESSAGE() macros to help
* create invocations.
*/
@implementation NSInvocation (MacroSetup)
/** <init /><override-subclass />
* Initialised an invocation instance which can be used to send messages to
* a target object using aSignature.<br />
* You must set the target and selector (using -setTarget: and -setSelector:)
* before you attempt to use the invocation.<br />
* Raises an NSInvalidArgumentException if aSignature is nil.
*/
- (id) initWithMethodSignature: (NSMethodSignature*)aSignature
{
[self subclassResponsibility: _cmd];
return nil;
}
/**
* Internal use.<br />
* Provides a return frame that the ObjectiveC runtime can use to
* return the result of an invocation to a calling function.
*/
+ (id) _newProxyForInvocation: (id)target
{
return [GSInvocationProxy _newWithTarget: target];
}
+ (id) _newProxyForMessage: (id)target
{
return [GSMessageProxy _newWithTarget: target];
}
+ (NSInvocation*) _returnInvocationAndDestroyProxy: (id)proxy
{
NSInvocation *inv = [proxy _invocation];
NSDeallocateObject(proxy);
return inv;
}
@end
@implementation NSInvocation (BackwardCompatibility)
- (void) invokeWithObject: (id)obj
{
[self invokeWithTarget: (id)obj];
}
@end
#if !defined(USE_FFCALL) && !defined(USE_LIBFFI)
#warning Using dummy NSInvocation implementation. It is strongly recommended that you use libffi.
@implementation GSDummyInvocation
/*
* This is the de_signated initialiser.
*/
- (id) initWithMethodSignature: (NSMethodSignature*)aSignature
{
return self;
}
- (void) invokeWithTarget: (id)anObject
{
CLEAR_RETURN_VALUE_IF_OBJECT;
_validReturn = NO;
/*
* A message to a nil object returns nil.
*/
if (anObject == nil)
{
_validReturn = YES;
return;
}
}
@end
#endif
@implementation GSInvocationProxy
+ (id) _newWithTarget: (id)t
{
GSInvocationProxy *o;
o = (GSInvocationProxy*) NSAllocateObject(self, 0, NSDefaultMallocZone());
o->target = RETAIN(t);
return o;
}
- (NSInvocation*) _invocation
{
return invocation;
}
- (void) forwardInvocation: (NSInvocation*)anInvocation
{
invocation = anInvocation;
}
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
{
return [target methodSignatureForSelector: aSelector];
}
@end
@implementation GSMessageProxy
- (NSInvocation*) _invocation
{
[invocation setTarget: target];
return invocation;
}
@end