mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-14 18:10:57 +00:00
Code cleanups
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@28770 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
503d05b4d4
commit
a1e0763383
8 changed files with 312 additions and 1897 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
||||||
|
2009-10-04 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
* Source/cifframe.h: Remove unused code
|
||||||
|
* Source/cifframe.m: ditto
|
||||||
|
* Source/callframe.h: ditto
|
||||||
|
* Source/callframe.m: ditto
|
||||||
|
* Source/NSConnection.m: Use NSInvocation functionality directly
|
||||||
|
rather than duplicated versions of the code in ffi/ffcall/mframe
|
||||||
|
specific files. Should simplify things and make maintenance easier.
|
||||||
|
|
||||||
2009-10-03 Richard Frith-Macdonald <rfm@gnu.org>
|
2009-10-03 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* Source/GSInvocation.h:
|
* Source/GSInvocation.h:
|
||||||
|
|
|
@ -93,6 +93,7 @@ int con_data (id prx)
|
||||||
id obj;
|
id obj;
|
||||||
small_struct small = {12};
|
small_struct small = {12};
|
||||||
foo ffoo = {'Z', 1234.5678, 99, "cow", 9876543};
|
foo ffoo = {'Z', 1234.5678, 99, "cow", 9876543};
|
||||||
|
foo bck;
|
||||||
|
|
||||||
printf("Testing data sending\n");
|
printf("Testing data sending\n");
|
||||||
|
|
||||||
|
@ -214,11 +215,13 @@ int con_data (id prx)
|
||||||
#if 1 || !defined(__MINGW32__)
|
#if 1 || !defined(__MINGW32__)
|
||||||
pool = [NSAutoreleasePool new];
|
pool = [NSAutoreleasePool new];
|
||||||
printf("Struct:\n");
|
printf("Struct:\n");
|
||||||
|
memcpy(&bck, &ffoo, sizeof(bck));
|
||||||
printf(" sending c='%c',d=%g,i=%d,s=%s,l=%ld",
|
printf(" sending c='%c',d=%g,i=%d,s=%s,l=%ld",
|
||||||
ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
|
ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
|
||||||
ffoo = [prx sendStruct: ffoo];
|
ffoo = [prx sendStruct: ffoo];
|
||||||
printf(" got c='%c',d=%g,i=%d,s=%s,l=%ld\n",
|
printf(" got c='%c',d=%g,i=%d,s=%s,l=%ld\n",
|
||||||
ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
|
ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
|
||||||
|
memcpy(&ffoo, &bck, sizeof(bck));
|
||||||
printf(" sending ptr to c='%c',d=%g,i=%d,s=%s,l=%ld",
|
printf(" sending ptr to c='%c',d=%g,i=%d,s=%s,l=%ld",
|
||||||
ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
|
ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
|
||||||
[prx getStruct: &ffoo];
|
[prx getStruct: &ffoo];
|
||||||
|
|
|
@ -291,8 +291,11 @@
|
||||||
|
|
||||||
- (void) getObject: (id *)str
|
- (void) getObject: (id *)str
|
||||||
{
|
{
|
||||||
|
static NSString *ret = @"hello";
|
||||||
printf ("(%s) got object (%s)\n", GSNameFromSelector(_cmd),
|
printf ("(%s) got object (%s)\n", GSNameFromSelector(_cmd),
|
||||||
GSClassNameFromObject(*str));
|
GSClassNameFromObject(*str));
|
||||||
|
*str = ret;
|
||||||
|
printf(" returning (%s)\n", [*str cString]);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,8 +310,9 @@
|
||||||
|
|
||||||
- (void) getString: (char **)str
|
- (void) getString: (char **)str
|
||||||
{
|
{
|
||||||
|
static char *ret = "hello";
|
||||||
printf ("(%s) got string (%s)", GSNameFromSelector(_cmd), *str);
|
printf ("(%s) got string (%s)", GSNameFromSelector(_cmd), *str);
|
||||||
(*str)[0] = 'N';
|
*str = ret;
|
||||||
printf(" returning (%s)\n", *str);
|
printf(" returning (%s)\n", *str);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1948,89 +1948,6 @@ static NSLock *cached_proxies_gate = nil;
|
||||||
RELEASE(arp);
|
RELEASE(arp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void retDecoder(DOContext *ctxt)
|
|
||||||
{
|
|
||||||
NSPortCoder *coder = ctxt->decoder;
|
|
||||||
const char *type = ctxt->type;
|
|
||||||
|
|
||||||
if (type == 0)
|
|
||||||
{
|
|
||||||
if (coder != nil)
|
|
||||||
{
|
|
||||||
ctxt->decoder = nil;
|
|
||||||
[ctxt->connection _doneInReply: coder];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* If we didn't get the reply packet yet, get it now. */
|
|
||||||
if (coder == nil)
|
|
||||||
{
|
|
||||||
BOOL is_exception;
|
|
||||||
|
|
||||||
if ([ctxt->connection isValid] == NO)
|
|
||||||
{
|
|
||||||
[NSException raise: NSGenericException
|
|
||||||
format: @"connection waiting for request was shut down"];
|
|
||||||
}
|
|
||||||
ctxt->decoder = [ctxt->connection _getReplyRmc: ctxt->seq];
|
|
||||||
coder = ctxt->decoder;
|
|
||||||
/*
|
|
||||||
* Find out if the server is returning an exception instead
|
|
||||||
* of the return values.
|
|
||||||
*/
|
|
||||||
[coder decodeValueOfObjCType: @encode(BOOL) at: &is_exception];
|
|
||||||
if (is_exception == YES)
|
|
||||||
{
|
|
||||||
/* Decode the exception object, and raise it. */
|
|
||||||
id exc = [coder decodeObject];
|
|
||||||
|
|
||||||
ctxt->decoder = nil;
|
|
||||||
[ctxt->connection _doneInReply: coder];
|
|
||||||
if (ctxt->datToFree != 0)
|
|
||||||
{
|
|
||||||
NSZoneFree(NSDefaultMallocZone(), ctxt->datToFree);
|
|
||||||
ctxt->datToFree = 0;
|
|
||||||
}
|
|
||||||
[exc raise];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*type == _C_ID)
|
|
||||||
{
|
|
||||||
*(id*)ctxt->datum = [coder decodeObject];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[coder decodeValueOfObjCType: type at: ctxt->datum];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(USE_FFCALL) && !defined(USE_LIBFFI)
|
|
||||||
static void retEncoder (DOContext *ctxt)
|
|
||||||
{
|
|
||||||
switch (*ctxt->type)
|
|
||||||
{
|
|
||||||
case _C_ID:
|
|
||||||
if (ctxt->flags & _F_BYCOPY)
|
|
||||||
{
|
|
||||||
[ctxt->encoder encodeBycopyObject: *(id*)ctxt->datum];
|
|
||||||
}
|
|
||||||
#ifdef _F_BYREF
|
|
||||||
else if (ctxt->flags & _F_BYREF)
|
|
||||||
{
|
|
||||||
[ctxt->encoder encodeByrefObject: *(id*)ctxt->datum];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[ctxt->encoder encodeObject: *(id*)ctxt->datum];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
[ctxt->encoder encodeValueOfObjCType: ctxt->type at: ctxt->datum];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NSDistantObject's -forward:: method calls this to send the message
|
* NSDistantObject's -forward:: method calls this to send the message
|
||||||
* over the wire.
|
* over the wire.
|
||||||
|
@ -2039,148 +1956,9 @@ static void retEncoder (DOContext *ctxt)
|
||||||
selector: (SEL)sel
|
selector: (SEL)sel
|
||||||
argFrame: (arglist_t)argframe
|
argFrame: (arglist_t)argframe
|
||||||
{
|
{
|
||||||
#if !defined(USE_FFCALL) && !defined(USE_LIBFFI)
|
[NSException raise: NSInternalInconsistencyException
|
||||||
BOOL outParams;
|
format: @"Obsolete method called"];
|
||||||
BOOL needsResponse;
|
return 0;
|
||||||
const char *type;
|
|
||||||
retval_t retframe;
|
|
||||||
DOContext ctxt;
|
|
||||||
NSRunLoop *runLoop = GSRunLoopForThread(nil);
|
|
||||||
|
|
||||||
memset(&ctxt, 0, sizeof(ctxt));
|
|
||||||
ctxt.connection = self;
|
|
||||||
|
|
||||||
/* Encode the method on an RMC, and send it. */
|
|
||||||
|
|
||||||
NSParameterAssert (IisValid);
|
|
||||||
|
|
||||||
if ([IrunLoops indexOfObjectIdenticalTo: runLoop] == NSNotFound)
|
|
||||||
{
|
|
||||||
if (ImultipleThreads == NO)
|
|
||||||
{
|
|
||||||
[NSException raise: NSObjectInaccessibleException
|
|
||||||
format: @"Forwarding message in wrong thread"];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[self addRunLoop: runLoop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get the method types from the selector */
|
|
||||||
#if NeXT_RUNTIME
|
|
||||||
[NSException
|
|
||||||
raise: NSGenericException
|
|
||||||
format: @"Sorry, distributed objects does not work with NeXT runtime"];
|
|
||||||
/* type = [object selectorTypeForProxy: sel]; */
|
|
||||||
#else
|
|
||||||
type = sel_get_type(sel);
|
|
||||||
if (type == 0 || *type == '\0')
|
|
||||||
{
|
|
||||||
type = [[object methodSignatureForSelector: sel] methodType];
|
|
||||||
if (type)
|
|
||||||
{
|
|
||||||
sel_register_typed_name(GSNameFromSelector(sel), type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
NSParameterAssert(type);
|
|
||||||
NSParameterAssert(*type);
|
|
||||||
|
|
||||||
ctxt.encoder = [self _makeOutRmc: 0 generate: (int*)&ctxt.seq reply: YES];
|
|
||||||
|
|
||||||
if (debug_connection > 4)
|
|
||||||
NSLog(@"building packet seq %d", ctxt.seq);
|
|
||||||
|
|
||||||
/* Send the types that we're using, so that the performer knows
|
|
||||||
exactly what qualifiers we're using.
|
|
||||||
If all selectors included qualifiers, and if I could make
|
|
||||||
sel_types_match() work the way I wanted, we wouldn't need to do
|
|
||||||
this. */
|
|
||||||
[ctxt.encoder encodeValueOfObjCType: @encode(char*) at: &type];
|
|
||||||
|
|
||||||
/* xxx This doesn't work with proxies and the NeXT runtime because
|
|
||||||
type may be a method_type from a remote machine with a
|
|
||||||
different architecture, and its argframe layout specifiers
|
|
||||||
won't be right for this machine! */
|
|
||||||
outParams = mframe_dissect_call (argframe, type, retEncoder, &ctxt);
|
|
||||||
|
|
||||||
if (outParams == YES)
|
|
||||||
{
|
|
||||||
needsResponse = YES;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
needsResponse = NO;
|
|
||||||
flags = objc_get_type_qualifiers(type);
|
|
||||||
if ((flags & _F_ONEWAY) == 0)
|
|
||||||
{
|
|
||||||
needsResponse = YES;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const char *tmptype = objc_skip_type_qualifiers(type);
|
|
||||||
|
|
||||||
if (*tmptype != _C_VOID)
|
|
||||||
{
|
|
||||||
needsResponse = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[self _sendOutRmc: ctxt.encoder type: METHOD_REQUEST];
|
|
||||||
ctxt.encoder = nil;
|
|
||||||
NSDebugMLLog(@"NSConnection", @"Sent message (%s) RMX %d to 0x%x",
|
|
||||||
GSNameFromSelector(sel), ctxt.seq, (uintptr_t)self);
|
|
||||||
|
|
||||||
if (needsResponse == NO)
|
|
||||||
{
|
|
||||||
GSIMapNode node;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Since we don't need a response, we can remove the placeholder from
|
|
||||||
* the IreplyMap. However, in case the other end has already sent us
|
|
||||||
* a response, we must check for it and scrap it if necessary.
|
|
||||||
*/
|
|
||||||
M_LOCK(IrefGate);
|
|
||||||
node = GSIMapNodeForKey(IreplyMap, (GSIMapKey)ctxt.seq);
|
|
||||||
if (node != 0 && node->value.obj != dummyObject)
|
|
||||||
{
|
|
||||||
BOOL is_exception = NO;
|
|
||||||
|
|
||||||
[node->value.obj decodeValueOfObjCType: @encode(BOOL)
|
|
||||||
at: &is_exception];
|
|
||||||
if (is_exception == YES)
|
|
||||||
NSLog(@"Got exception with %@", NSStringFromSelector(sel));
|
|
||||||
else
|
|
||||||
NSLog(@"Got response with %@", NSStringFromSelector(sel));
|
|
||||||
[self _doneInRmc: node->value.obj];
|
|
||||||
}
|
|
||||||
GSIMapRemoveKey(IreplyMap, (GSIMapKey)ctxt.seq);
|
|
||||||
M_UNLOCK(IrefGate);
|
|
||||||
retframe = alloca(sizeof(void*)); /* Dummy value for void return. */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
retframe = mframe_build_return (argframe, type, outParams,
|
|
||||||
retDecoder, &ctxt);
|
|
||||||
/* Make sure we processed all arguments, and dismissed the IP.
|
|
||||||
IP is always set to -1 after being dismissed; the only places
|
|
||||||
this is done is in this function DECODER(). IP will be nil
|
|
||||||
if mframe_build_return() never called DECODER(), i.e. when
|
|
||||||
we are just returning (void).*/
|
|
||||||
NSAssert(ctxt.decoder == nil, NSInternalInconsistencyException);
|
|
||||||
}
|
|
||||||
return retframe;
|
|
||||||
#else
|
|
||||||
/* If we've got to here then something has gone badly wrong. Most likely a
|
|
||||||
* mismatch between NSDistantObject and NSConnection implementations.
|
|
||||||
*/
|
|
||||||
NSAssert(NO, @"Legacy forwardForProxy:selector:argFrame: method called when compiled with fcall/ffi.");
|
|
||||||
return 0; // Not reached.
|
|
||||||
#endif //!defined(USE_FFCALL) && !defined(USE_LIBFFI)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2296,19 +2074,11 @@ static void retEncoder (DOContext *ctxt)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#if 1
|
|
||||||
/* Which argument number are we processing now? */
|
|
||||||
int argnum;
|
int argnum;
|
||||||
/* Type qualifier flags; see <objc/objc-api.h>. */
|
|
||||||
int flags;
|
int flags;
|
||||||
/* A pointer into the TYPE string. */
|
|
||||||
const char *tmptype;
|
const char *tmptype;
|
||||||
/* Points at individual arguments. */
|
|
||||||
void *datum;
|
void *datum;
|
||||||
const char *rettype;
|
|
||||||
/* Signature information */
|
|
||||||
NSPortCoder *aRmc;
|
NSPortCoder *aRmc;
|
||||||
|
|
||||||
BOOL is_exception;
|
BOOL is_exception;
|
||||||
|
|
||||||
if ([self isValid] == NO)
|
if ([self isValid] == NO)
|
||||||
|
@ -2335,7 +2105,6 @@ static void retEncoder (DOContext *ctxt)
|
||||||
/* Get the return type qualifier flags, and the return type. */
|
/* Get the return type qualifier flags, and the return type. */
|
||||||
flags = objc_get_type_qualifiers(type);
|
flags = objc_get_type_qualifiers(type);
|
||||||
tmptype = objc_skip_type_qualifiers(type);
|
tmptype = objc_skip_type_qualifiers(type);
|
||||||
rettype = tmptype;
|
|
||||||
|
|
||||||
/* Decode the return value and pass-by-reference values, if there
|
/* Decode the return value and pass-by-reference values, if there
|
||||||
are any. OUT_PARAMETERS should be the value returned by
|
are any. OUT_PARAMETERS should be the value returned by
|
||||||
|
@ -2345,9 +2114,6 @@ static void retEncoder (DOContext *ctxt)
|
||||||
/* xxx What happens with method declared "- (in char *) bar;" */
|
/* xxx What happens with method declared "- (in char *) bar;" */
|
||||||
/* xxx Is this right? Do we also have to check _F_ONEWAY? */
|
/* xxx Is this right? Do we also have to check _F_ONEWAY? */
|
||||||
{
|
{
|
||||||
/* ARGNUM == -1 signifies to DECODER() that this is the return
|
|
||||||
value, not an argument. */
|
|
||||||
|
|
||||||
/* If there is a return value, decode it, and put it in datum. */
|
/* If there is a return value, decode it, and put it in datum. */
|
||||||
if (*tmptype != _C_VOID || (flags & _F_ONEWAY) == 0)
|
if (*tmptype != _C_VOID || (flags & _F_ONEWAY) == 0)
|
||||||
{
|
{
|
||||||
|
@ -2394,33 +2160,23 @@ static void retEncoder (DOContext *ctxt)
|
||||||
if (*tmptype == _C_PTR
|
if (*tmptype == _C_PTR
|
||||||
&& ((flags & _F_OUT) || !(flags & _F_IN)))
|
&& ((flags & _F_OUT) || !(flags & _F_IN)))
|
||||||
{
|
{
|
||||||
|
/* If the arg was myref, we obtain its address
|
||||||
|
* and decode the data directly to it.
|
||||||
|
*/
|
||||||
tmptype++;
|
tmptype++;
|
||||||
datum = alloca (objc_sizeof_type (tmptype));
|
[inv getArgument: &datum atIndex: argnum];
|
||||||
|
|
||||||
[aRmc decodeValueOfObjCType: tmptype at: datum];
|
[aRmc decodeValueOfObjCType: tmptype at: datum];
|
||||||
[inv setArgument: datum atIndex: argnum];
|
|
||||||
}
|
}
|
||||||
else if (*tmptype == _C_CHARPTR
|
else if (*tmptype == _C_CHARPTR
|
||||||
&& ((flags & _F_OUT) || !(flags & _F_IN)))
|
&& ((flags & _F_OUT) || !(flags & _F_IN)))
|
||||||
{
|
{
|
||||||
[aRmc decodeValueOfObjCType: tmptype at: &datum];
|
[aRmc decodeValueOfObjCType: tmptype at: &datum];
|
||||||
[inv setArgument: &datum atIndex: argnum];
|
[inv setArgument: datum atIndex: argnum];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[self _doneInReply: aRmc];
|
[self _doneInReply: aRmc];
|
||||||
#elif USE_LIBFFI
|
|
||||||
cifframe_build_return (inv, type, outParams, retDecoder, &ctxt);
|
|
||||||
#elif defined(USE_FFCALL)
|
|
||||||
callframe_build_return (inv, type, outParams, retDecoder, &ctxt);
|
|
||||||
#endif
|
|
||||||
/* Make sure we processed all arguments, and dismissed the IP.
|
|
||||||
IP is always set to -1 after being dismissed; the only places
|
|
||||||
this is done is in this function DECODER(). IP will be nil
|
|
||||||
if mframe_build_return() never called DECODER(), i.e. when
|
|
||||||
we are just returning (void).*/
|
|
||||||
NSAssert(ctxt.decoder == nil, NSInternalInconsistencyException);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2722,96 +2478,6 @@ static void retEncoder (DOContext *ctxt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void callDecoder (DOContext *ctxt)
|
|
||||||
{
|
|
||||||
const char *type = ctxt->type;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need this "dismiss" to happen here and not later so that Coder
|
|
||||||
* "-awake..." methods will get sent before the method using the
|
|
||||||
* objects is invoked. We clear the 'decoder' field in the context to
|
|
||||||
* show that it is no longer valid.
|
|
||||||
*/
|
|
||||||
if (type == 0)
|
|
||||||
{
|
|
||||||
NSPortCoder *coder = ctxt->decoder;
|
|
||||||
|
|
||||||
ctxt->decoder = nil;
|
|
||||||
[ctxt->connection _doneInRmc: coder];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The coder may have an optimised method for decoding objects
|
|
||||||
* so we use that one if we are expecting an object, otherwise
|
|
||||||
* we use thegeneric method.
|
|
||||||
*/
|
|
||||||
if (*type == _C_ID)
|
|
||||||
{
|
|
||||||
*(id*)ctxt->datum = [ctxt->decoder decodeObject];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[ctxt->decoder decodeValueOfObjCType: type at: ctxt->datum];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void callEncoder (DOContext *ctxt)
|
|
||||||
{
|
|
||||||
const char *type = ctxt->type;
|
|
||||||
NSPortCoder *coder = ctxt->encoder;
|
|
||||||
|
|
||||||
if (coder == nil)
|
|
||||||
{
|
|
||||||
BOOL is_exception = NO;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* It is possible that our connection died while the method was
|
|
||||||
* being called - in this case we mustn't try to send the result
|
|
||||||
* back to the remote application!
|
|
||||||
*/
|
|
||||||
if ([ctxt->connection isValid] == NO)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We create a new coder object and set it in the context for
|
|
||||||
* later use if/when we are called again. We encode a flag to
|
|
||||||
* say that this is not an exception.
|
|
||||||
*/
|
|
||||||
ctxt->encoder = [ctxt->connection _makeOutRmc: ctxt->seq
|
|
||||||
generate: 0
|
|
||||||
reply: NO];
|
|
||||||
coder = ctxt->encoder;
|
|
||||||
[coder encodeValueOfObjCType: @encode(BOOL) at: &is_exception];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*type == _C_ID)
|
|
||||||
{
|
|
||||||
int flags = ctxt->flags;
|
|
||||||
|
|
||||||
if (flags & _F_BYCOPY)
|
|
||||||
{
|
|
||||||
[coder encodeBycopyObject: *(id*)ctxt->datum];
|
|
||||||
}
|
|
||||||
#ifdef _F_BYREF
|
|
||||||
else if (flags & _F_BYREF)
|
|
||||||
{
|
|
||||||
[coder encodeByrefObject: *(id*)ctxt->datum];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[coder encodeObject: *(id*)ctxt->datum];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[coder encodeValueOfObjCType: type at: ctxt->datum];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* NSConnection calls this to service the incoming method request. */
|
/* NSConnection calls this to service the incoming method request. */
|
||||||
- (void) _service_forwardForProxy: (NSPortCoder*)aRmc
|
- (void) _service_forwardForProxy: (NSPortCoder*)aRmc
|
||||||
|
@ -2864,7 +2530,6 @@ static void callEncoder (DOContext *ctxt)
|
||||||
ctxt.type, ctxt.seq, (uintptr_t)self);
|
ctxt.type, ctxt.seq, (uintptr_t)self);
|
||||||
|
|
||||||
IreqInCount++; /* Handling an incoming request. */
|
IreqInCount++; /* Handling an incoming request. */
|
||||||
#if 1
|
|
||||||
{
|
{
|
||||||
/* The method type string obtained from the target's OBJC_METHOD
|
/* The method type string obtained from the target's OBJC_METHOD
|
||||||
structure for the selector we're sending. */
|
structure for the selector we're sending. */
|
||||||
|
@ -3178,17 +2843,6 @@ static void callEncoder (DOContext *ctxt)
|
||||||
[self _sendOutRmc: aRmc type: METHOD_REPLY];
|
[self _sendOutRmc: aRmc type: METHOD_REPLY];
|
||||||
ctxt.encoder = nil;
|
ctxt.encoder = nil;
|
||||||
}
|
}
|
||||||
#elif defined(USE_LIBFFI)
|
|
||||||
cifframe_do_call (&ctxt, callDecoder, callEncoder);
|
|
||||||
#elif defined(USE_FFCALL)
|
|
||||||
callframe_do_call (&ctxt, callDecoder, callEncoder);
|
|
||||||
#else
|
|
||||||
mframe_do_call (&ctxt, callDecoder, callEncoder);
|
|
||||||
#endif
|
|
||||||
if (ctxt.encoder != nil)
|
|
||||||
{
|
|
||||||
[self _sendOutRmc: ctxt.encoder type: METHOD_REPLY];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NS_HANDLER
|
NS_HANDLER
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,13 +44,5 @@ extern void callframe_get_arg(callframe_t *cframe, int index, void *buffer,
|
||||||
int size);
|
int size);
|
||||||
extern void *callframe_arg_addr(callframe_t *cframe, int index);
|
extern void *callframe_arg_addr(callframe_t *cframe, int index);
|
||||||
|
|
||||||
extern void callframe_do_call (DOContext *ctxt,
|
|
||||||
void(*decoder)(DOContext*),
|
|
||||||
void(*encoder)(DOContext*));
|
|
||||||
extern void callframe_build_return (NSInvocation *inv,
|
|
||||||
const char *type,
|
|
||||||
BOOL out_parameters,
|
|
||||||
void(*decoder)(DOContext*),
|
|
||||||
DOContext* ctxt);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -158,626 +158,3 @@ callframe_arg_addr(callframe_t *cframe, int index)
|
||||||
return cframe->args[index];
|
return cframe->args[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Ugly hack to make it easier to invoke a method from outside
|
|
||||||
an NSInvocation class. Hopefully simplication of NSConnection
|
|
||||||
could remove this hack */
|
|
||||||
typedef struct _NSInvocation_t {
|
|
||||||
@defs(NSInvocation)
|
|
||||||
} NSInvocation_t;
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
|
||||||
/* Functions for handling sending and receiving messages accross a
|
|
||||||
connection
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* callframe_do_call()
|
|
||||||
|
|
||||||
This function decodes the arguments of method call, builds a
|
|
||||||
callframe, and invokes the method using GSFFCallInvokeWithTargetAndImp
|
|
||||||
then it encodes the return value and any pass-by-reference arguments.
|
|
||||||
|
|
||||||
An entry, ctxt->type should be a string that describes the return value
|
|
||||||
and arguments. It's argument types and argument type qualifiers
|
|
||||||
should match exactly those that were used when the arguments were
|
|
||||||
encoded. callframe_do_call() uses this information to determine
|
|
||||||
which variable types it should decode.
|
|
||||||
|
|
||||||
The type info is used to get the types and type qualifiers, but not
|
|
||||||
to get the register and stack locations---we get that information
|
|
||||||
from the selector type of the SEL that is decoded as the second
|
|
||||||
argument. In this way, the type info may come from a machine
|
|
||||||
of a different architecture. Having the original type info is
|
|
||||||
good, just in case the machine running callframe_do_call() has some
|
|
||||||
slightly different qualifiers. Using different qualifiers for
|
|
||||||
encoding and decoding could lead to massive confusion.
|
|
||||||
|
|
||||||
|
|
||||||
DECODER should be a pointer to a function that obtains the method's
|
|
||||||
argument values. For example:
|
|
||||||
|
|
||||||
void my_decoder (DOContext *ctxt)
|
|
||||||
|
|
||||||
CTXT contains the context information for the item to decode.
|
|
||||||
|
|
||||||
callframe_do_call() calls this function once for each of the methods
|
|
||||||
arguments. The DECODER function should place the ARGNUM'th
|
|
||||||
argument's value at the memory location ctxt->datum.
|
|
||||||
callframe_do_call() calls this function once with ctxt->datum 0,
|
|
||||||
and ctxt->type 0 to denote completion of decoding.
|
|
||||||
|
|
||||||
|
|
||||||
If DECODER malloc's new memory in the course of doing its
|
|
||||||
business, then DECODER is responsible for making sure that the
|
|
||||||
memory will get free eventually. For example, if DECODER uses
|
|
||||||
-decodeValueOfCType:at:withName: to decode a char* string, you
|
|
||||||
should remember that -decodeValueOfCType:at:withName: malloc's
|
|
||||||
new memory to hold the string, and DECODER should autorelease the
|
|
||||||
malloc'ed pointer, using the NSData class.
|
|
||||||
|
|
||||||
|
|
||||||
ENCODER should be a pointer to a function that records the method's
|
|
||||||
return value and pass-by-reference values. For example:
|
|
||||||
|
|
||||||
void my_encoder (DOContext *ctxt)
|
|
||||||
|
|
||||||
CTXT contains the context information for the item to encode.
|
|
||||||
|
|
||||||
callframe_do_call() calls this function after the method has been
|
|
||||||
run---once for the return value, and once for each of the
|
|
||||||
pass-by-reference parameters. The ENCODER function should place
|
|
||||||
the value at memory location ctxt->datum wherever the user wants to
|
|
||||||
record the ARGNUM'th return value.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
void
|
|
||||||
callframe_do_call (DOContext *ctxt,
|
|
||||||
void(*decoder)(DOContext*),
|
|
||||||
void(*encoder)(DOContext*))
|
|
||||||
{
|
|
||||||
/* The method type string obtained from the target's OBJC_METHOD
|
|
||||||
structure for the selector we're sending. */
|
|
||||||
const char *type;
|
|
||||||
/* A pointer into the local variable TYPE string. */
|
|
||||||
const char *tmptype;
|
|
||||||
/* A pointer into the argument ENCODED_TYPES string. */
|
|
||||||
const char *etmptype;
|
|
||||||
/* The target object that will receive the message. */
|
|
||||||
id object;
|
|
||||||
/* The selector for the message we're sending to the TARGET. */
|
|
||||||
SEL selector;
|
|
||||||
/* The OBJECT's Method(_t) pointer for the SELECTOR. */
|
|
||||||
GSMethod meth;
|
|
||||||
/* The OBJECT's implementation of the SELECTOR. */
|
|
||||||
IMP method_implementation;
|
|
||||||
/* Type qualifier flags; see <objc/objc-api.h>. */
|
|
||||||
unsigned flags;
|
|
||||||
/* Which argument number are we processing now? */
|
|
||||||
int argnum;
|
|
||||||
/* The cif information for calling the method */
|
|
||||||
callframe_t *cframe;
|
|
||||||
/* Does the method have any arguments that are passed by reference?
|
|
||||||
If so, we need to encode them, since the method may have changed them. */
|
|
||||||
BOOL out_parameters = NO;
|
|
||||||
/* A dummy invocation to pass to the function that invokes our method */
|
|
||||||
NSInvocation_t *inv;
|
|
||||||
/* Signature information */
|
|
||||||
NSMethodSignature *sig;
|
|
||||||
void *retval;
|
|
||||||
const char *encoded_types = ctxt->type;
|
|
||||||
|
|
||||||
/* Decode the object, (which is always the first argument to a method),
|
|
||||||
into the local variable OBJECT. */
|
|
||||||
ctxt->type = @encode(id);
|
|
||||||
ctxt->datum = &object;
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
NSCParameterAssert (object);
|
|
||||||
|
|
||||||
/* Decode the selector, (which is always the second argument to a
|
|
||||||
method), into the local variable SELECTOR. */
|
|
||||||
/* xxx @encode(SEL) produces "^v" in gcc 2.5.8. It should be ":" */
|
|
||||||
ctxt->type = @encode(SEL);
|
|
||||||
ctxt->datum = &selector;
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
NSCParameterAssert (selector);
|
|
||||||
|
|
||||||
/* Get the "selector type" for this method. The "selector type" is
|
|
||||||
a string that lists the return and argument types, and also
|
|
||||||
indicates in which registers and where on the stack the arguments
|
|
||||||
should be placed before the method call. The selector type
|
|
||||||
string we get here should have the same argument and return types
|
|
||||||
as the ENCODED_TYPES string, but it will have different register
|
|
||||||
and stack locations if the ENCODED_TYPES came from a machine of a
|
|
||||||
different architecture. */
|
|
||||||
if (GSObjCIsClass(object))
|
|
||||||
{
|
|
||||||
meth = GSGetMethod(object, selector, NO, YES);
|
|
||||||
}
|
|
||||||
else if (GSObjCIsInstance(object))
|
|
||||||
{
|
|
||||||
meth = GSGetMethod(GSObjCClass(object), selector, YES, YES);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[NSException raise: NSInvalidArgumentException
|
|
||||||
format: @"decoded object %p is invalid", object];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (meth != 0)
|
|
||||||
{
|
|
||||||
type = meth->method_types;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NSDebugLog(@"Local object <%p %s> doesn't implement: %s directly. "
|
|
||||||
@"Will search for arbitrary signature.",
|
|
||||||
object,
|
|
||||||
GSNameFromClass(GSObjCIsClass(object)
|
|
||||||
? object : GSObjCClass(object)),
|
|
||||||
GSNameFromSelector(selector));
|
|
||||||
type = GSTypesFromSelector(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure we successfully got the method type, and that its
|
|
||||||
types match the ENCODED_TYPES. */
|
|
||||||
NSCParameterAssert (type);
|
|
||||||
if (GSSelectorTypesMatch(encoded_types, type) == NO)
|
|
||||||
{
|
|
||||||
[NSException raise: NSInvalidArgumentException
|
|
||||||
format: @"callframe_do_call types (%s / %s) missmatch for %s",
|
|
||||||
encoded_types, type, GSNameFromSelector(selector)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Build the cif frame */
|
|
||||||
sig = [NSMethodSignature signatureWithObjCTypes: type];
|
|
||||||
cframe = callframe_from_info([sig methodInfo], [sig numberOfArguments],
|
|
||||||
&retval);
|
|
||||||
ctxt->datToFree = cframe;
|
|
||||||
|
|
||||||
/* Put OBJECT and SELECTOR into the ARGFRAME. */
|
|
||||||
|
|
||||||
/* Initialize our temporary pointers into the method type strings. */
|
|
||||||
tmptype = objc_skip_argspec (type);
|
|
||||||
etmptype = objc_skip_argspec (encoded_types);
|
|
||||||
NSCParameterAssert (*tmptype == _C_ID);
|
|
||||||
/* Put the target object there. */
|
|
||||||
callframe_set_arg(cframe, 0, &object, sizeof(id));
|
|
||||||
/* Get a pointer into ARGFRAME, pointing to the location where the
|
|
||||||
second argument is to be stored. */
|
|
||||||
tmptype = objc_skip_argspec (tmptype);
|
|
||||||
etmptype = objc_skip_argspec(etmptype);
|
|
||||||
NSCParameterAssert (*tmptype == _C_SEL);
|
|
||||||
/* Put the selector there. */
|
|
||||||
callframe_set_arg(cframe, 1, &selector, sizeof(SEL));
|
|
||||||
|
|
||||||
|
|
||||||
/* Decode arguments after OBJECT and SELECTOR, and put them into the
|
|
||||||
ARGFRAME. Step TMPTYPE and ETMPTYPE in lock-step through their
|
|
||||||
method type strings. */
|
|
||||||
|
|
||||||
for (tmptype = objc_skip_argspec (tmptype),
|
|
||||||
etmptype = objc_skip_argspec (etmptype), argnum = 2;
|
|
||||||
*tmptype != '\0';
|
|
||||||
tmptype = objc_skip_argspec (tmptype),
|
|
||||||
etmptype = objc_skip_argspec (etmptype), argnum++)
|
|
||||||
{
|
|
||||||
/* Get the type qualifiers, like IN, OUT, INOUT, ONEWAY. */
|
|
||||||
flags = objc_get_type_qualifiers (etmptype);
|
|
||||||
/* Skip over the type qualifiers, so now TYPE is pointing directly
|
|
||||||
at the char corresponding to the argument's type, as defined
|
|
||||||
in <objc/objc-api.h> */
|
|
||||||
tmptype = objc_skip_type_qualifiers(tmptype);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Setup information in context.
|
|
||||||
*/
|
|
||||||
ctxt->datum = callframe_arg_addr(cframe, argnum);
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->flags = flags;
|
|
||||||
|
|
||||||
/* Decide how, (or whether or not), to decode the argument
|
|
||||||
depending on its FLAGS and TMPTYPE. Only the first two cases
|
|
||||||
involve parameters that may potentially be passed by
|
|
||||||
reference, and thus only the first two may change the value
|
|
||||||
of OUT_PARAMETERS. *** Note: This logic must match exactly
|
|
||||||
the code in callframe_dissect_call(); that function should
|
|
||||||
encode exactly what we decode here. *** */
|
|
||||||
|
|
||||||
switch (*tmptype)
|
|
||||||
{
|
|
||||||
|
|
||||||
case _C_CHARPTR:
|
|
||||||
/* Handle a (char*) argument. */
|
|
||||||
/* If the char* is qualified as an OUT parameter, or if it
|
|
||||||
not explicitly qualified as an IN parameter, then we will
|
|
||||||
have to get this char* again after the method is run,
|
|
||||||
because the method may have changed it. Set
|
|
||||||
OUT_PARAMETERS accordingly. */
|
|
||||||
if ((flags & _F_OUT) || !(flags & _F_IN))
|
|
||||||
out_parameters = YES;
|
|
||||||
/* If the char* is qualified as an IN parameter, or not
|
|
||||||
explicity qualified as an OUT parameter, then decode it.
|
|
||||||
Note: the decoder allocates memory for holding the
|
|
||||||
string, and it is also responsible for making sure that
|
|
||||||
the memory gets freed eventually, (usually through the
|
|
||||||
autorelease of NSData object). */
|
|
||||||
if ((flags & _F_IN) || !(flags & _F_OUT))
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _C_PTR:
|
|
||||||
/* If the pointer's value is qualified as an OUT parameter,
|
|
||||||
or if it not explicitly qualified as an IN parameter,
|
|
||||||
then we will have to get the value pointed to again after
|
|
||||||
the method is run, because the method may have changed
|
|
||||||
it. Set OUT_PARAMETERS accordingly. */
|
|
||||||
if ((flags & _F_OUT) || !(flags & _F_IN))
|
|
||||||
out_parameters = YES;
|
|
||||||
|
|
||||||
/* Handle an argument that is a pointer to a non-char. But
|
|
||||||
(void*) and (anything**) is not allowed. */
|
|
||||||
/* The argument is a pointer to something; increment TYPE
|
|
||||||
so we can see what it is a pointer to. */
|
|
||||||
tmptype++;
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
/* Allocate some memory to be pointed to, and to hold the
|
|
||||||
value. Note that it is allocated on the stack, and
|
|
||||||
methods that want to keep the data pointed to, will have
|
|
||||||
to make their own copies. */
|
|
||||||
*(void**)ctxt->datum = alloca (objc_sizeof_type (tmptype));
|
|
||||||
ctxt->datum = *(void**)ctxt->datum;
|
|
||||||
/* If the pointer's value is qualified as an IN parameter,
|
|
||||||
or not explicity qualified as an OUT parameter, then
|
|
||||||
decode it. */
|
|
||||||
if ((flags & _F_IN) || !(flags & _F_OUT))
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Handle arguments of all other types. */
|
|
||||||
/* NOTE FOR OBJECTS: Unlike [Decoder decodeObjectAt:..],
|
|
||||||
this function does not generate a reference to the
|
|
||||||
object; the object may be autoreleased; if the method
|
|
||||||
wants to keep a reference to the object, it will have to
|
|
||||||
-retain it. */
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* End of the for () loop that enumerates the method's arguments. */
|
|
||||||
ctxt->type = 0;
|
|
||||||
ctxt->datum = 0;
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
|
|
||||||
|
|
||||||
/* Invoke the method! */
|
|
||||||
|
|
||||||
/* Find the target object's implementation of this selector. */
|
|
||||||
method_implementation = objc_msg_lookup (object, selector);
|
|
||||||
NSCParameterAssert (method_implementation);
|
|
||||||
/* Do it! Send the message to the target, and get the return value
|
|
||||||
in retval. We need to encode any pass-by-reference info */
|
|
||||||
inv = (NSInvocation_t *)NSAllocateObject([NSInvocation class], 0,
|
|
||||||
NSDefaultMallocZone());
|
|
||||||
inv->_retval = retval;
|
|
||||||
inv->_selector = selector;
|
|
||||||
inv->_cframe = cframe;
|
|
||||||
inv->_info = [sig methodInfo];
|
|
||||||
inv->_numArgs = [sig numberOfArguments];
|
|
||||||
ctxt->objToFree = (id)inv;
|
|
||||||
GSFFCallInvokeWithTargetAndImp((NSInvocation *)inv, object,
|
|
||||||
method_implementation);
|
|
||||||
ctxt->objToFree = nil;
|
|
||||||
NSDeallocateObject((NSInvocation *)inv);
|
|
||||||
|
|
||||||
/* Encode the return value and pass-by-reference values, if there
|
|
||||||
are any. This logic must match exactly that in
|
|
||||||
callframe_build_return(). */
|
|
||||||
/* OUT_PARAMETERS should be true here in exactly the same
|
|
||||||
situations as it was true in callframe_dissect_call(). */
|
|
||||||
|
|
||||||
/* Get the qualifier type of the return value. */
|
|
||||||
flags = objc_get_type_qualifiers (encoded_types);
|
|
||||||
/* Get the return type; store it our two temporary char*'s. */
|
|
||||||
etmptype = objc_skip_type_qualifiers (encoded_types);
|
|
||||||
tmptype = objc_skip_type_qualifiers (type);
|
|
||||||
|
|
||||||
/* Only encode return values if there is a non-void return value,
|
|
||||||
a non-oneway void return value, or if there are values that were
|
|
||||||
passed by reference. */
|
|
||||||
|
|
||||||
ctxt->flags = flags;
|
|
||||||
|
|
||||||
/* If there is a return value, encode it. */
|
|
||||||
if (*tmptype == _C_VOID)
|
|
||||||
{
|
|
||||||
if ((flags & _F_ONEWAY) == 0)
|
|
||||||
{
|
|
||||||
int dummy = 0;
|
|
||||||
|
|
||||||
ctxt->type = @encode(int);
|
|
||||||
ctxt->datum = (void*)&dummy;
|
|
||||||
(*encoder) (ctxt);
|
|
||||||
}
|
|
||||||
/* No return value to encode; do nothing. */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (*tmptype == _C_PTR)
|
|
||||||
{
|
|
||||||
/* The argument is a pointer to something; increment TYPE
|
|
||||||
so we can see what it is a pointer to. */
|
|
||||||
tmptype++;
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = *(void**)retval;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = retval;
|
|
||||||
}
|
|
||||||
/* Encode the value that was pointed to. */
|
|
||||||
(*encoder) (ctxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Encode the values returned by reference. Note: this logic
|
|
||||||
must match exactly the code in callframe_build_return(); that
|
|
||||||
function should decode exactly what we encode here. */
|
|
||||||
|
|
||||||
if (out_parameters)
|
|
||||||
{
|
|
||||||
/* Step through all the arguments, finding the ones that were
|
|
||||||
passed by reference. */
|
|
||||||
for (tmptype = objc_skip_argspec (tmptype),
|
|
||||||
argnum = 0,
|
|
||||||
etmptype = objc_skip_argspec (etmptype);
|
|
||||||
*tmptype != '\0';
|
|
||||||
tmptype = objc_skip_argspec (tmptype),
|
|
||||||
argnum++,
|
|
||||||
etmptype = objc_skip_argspec (etmptype))
|
|
||||||
{
|
|
||||||
/* Get the type qualifiers, like IN, OUT, INOUT, ONEWAY. */
|
|
||||||
flags = objc_get_type_qualifiers(etmptype);
|
|
||||||
/* Skip over the type qualifiers, so now TYPE is pointing directly
|
|
||||||
at the char corresponding to the argument's type, as defined
|
|
||||||
in <objc/objc-api.h> */
|
|
||||||
tmptype = objc_skip_type_qualifiers (tmptype);
|
|
||||||
|
|
||||||
/* Decide how, (or whether or not), to encode the argument
|
|
||||||
depending on its FLAGS and TMPTYPE. */
|
|
||||||
if (((flags & _F_OUT) || !(flags & _F_IN))
|
|
||||||
&& (*tmptype == _C_PTR || *tmptype == _C_CHARPTR))
|
|
||||||
{
|
|
||||||
ctxt->flags = flags;
|
|
||||||
ctxt->datum = callframe_arg_addr(cframe, argnum);
|
|
||||||
|
|
||||||
if (*tmptype == _C_PTR)
|
|
||||||
{
|
|
||||||
/* The argument is a pointer (to a non-char), and the
|
|
||||||
pointer's value is qualified as an OUT parameter, or
|
|
||||||
it not explicitly qualified as an IN parameter, then
|
|
||||||
it is a pass-by-reference argument.*/
|
|
||||||
ctxt->type = ++tmptype;
|
|
||||||
ctxt->datum = *(void**)ctxt->datum;
|
|
||||||
}
|
|
||||||
else if (*tmptype == _C_CHARPTR)
|
|
||||||
{
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
/* The argument is a pointer char string, and the
|
|
||||||
pointer's value is qualified as an OUT parameter, or
|
|
||||||
it not explicitly qualified as an IN parameter, then
|
|
||||||
it is a pass-by-reference argument. */
|
|
||||||
}
|
|
||||||
(*encoder) (ctxt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if !GS_WITH_GC
|
|
||||||
NSZoneFree(NSDefaultMallocZone(), ctxt->datToFree);
|
|
||||||
#endif
|
|
||||||
ctxt->datToFree = 0;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* callframe_build_return()
|
|
||||||
|
|
||||||
This function decodes the values returned from a method call,
|
|
||||||
sets up the invocation with the return value, and updates the
|
|
||||||
pass-by-reference arguments.
|
|
||||||
|
|
||||||
The callback function is finally called with the 'type' set to a null pointer
|
|
||||||
to tell it that the return value and all return parameters have been
|
|
||||||
dealt with. This permits the function to do any tidying up necessary. */
|
|
||||||
|
|
||||||
void
|
|
||||||
callframe_build_return (NSInvocation *inv,
|
|
||||||
const char *type,
|
|
||||||
BOOL out_parameters,
|
|
||||||
void(*decoder)(DOContext *ctxt),
|
|
||||||
DOContext *ctxt)
|
|
||||||
{
|
|
||||||
/* Which argument number are we processing now? */
|
|
||||||
int argnum;
|
|
||||||
/* Type qualifier flags; see <objc/objc-api.h>. */
|
|
||||||
int flags;
|
|
||||||
/* A pointer into the TYPE string. */
|
|
||||||
const char *tmptype;
|
|
||||||
/* Points at individual arguments. */
|
|
||||||
void *datum;
|
|
||||||
const char *rettype;
|
|
||||||
/* A pointer to the memory holding the return value of the method. */
|
|
||||||
void *retval;
|
|
||||||
/* Storage for the argument information */
|
|
||||||
callframe_t *cframe;
|
|
||||||
/* Signature information */
|
|
||||||
NSMethodSignature *sig;
|
|
||||||
|
|
||||||
/* Build the call frame */
|
|
||||||
sig = [NSMethodSignature signatureWithObjCTypes: type];
|
|
||||||
cframe = callframe_from_info([sig methodInfo], [sig numberOfArguments],
|
|
||||||
&retval);
|
|
||||||
ctxt->datToFree = cframe;
|
|
||||||
|
|
||||||
/* Get the return type qualifier flags, and the return type. */
|
|
||||||
flags = objc_get_type_qualifiers(type);
|
|
||||||
tmptype = objc_skip_type_qualifiers(type);
|
|
||||||
rettype = tmptype;
|
|
||||||
|
|
||||||
/* Decode the return value and pass-by-reference values, if there
|
|
||||||
are any. OUT_PARAMETERS should be the value returned by
|
|
||||||
callframe_dissect_call(). */
|
|
||||||
if (out_parameters || *tmptype != _C_VOID || (flags & _F_ONEWAY) == 0)
|
|
||||||
/* xxx What happens with method declared "- (oneway) foo: (out int*)ip;" */
|
|
||||||
/* xxx What happens with method declared "- (in char *) bar;" */
|
|
||||||
/* xxx Is this right? Do we also have to check _F_ONEWAY? */
|
|
||||||
{
|
|
||||||
/* ARGNUM == -1 signifies to DECODER() that this is the return
|
|
||||||
value, not an argument. */
|
|
||||||
|
|
||||||
/* If there is a return value, decode it, and put it in retval. */
|
|
||||||
if (*tmptype != _C_VOID || (flags & _F_ONEWAY) == 0)
|
|
||||||
{
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = retval;
|
|
||||||
ctxt->flags = flags;
|
|
||||||
|
|
||||||
switch (*tmptype)
|
|
||||||
{
|
|
||||||
case _C_PTR:
|
|
||||||
{
|
|
||||||
unsigned retLength;
|
|
||||||
|
|
||||||
/* We are returning a pointer to something. */
|
|
||||||
/* Increment TYPE so we can see what it is a pointer to. */
|
|
||||||
tmptype++;
|
|
||||||
retLength = objc_sizeof_type(tmptype);
|
|
||||||
/* Allocate memory to hold the value we're pointing to. */
|
|
||||||
#if GS_WITH_GC
|
|
||||||
*(void**)retval =
|
|
||||||
NSAllocateCollectable(retLength, NSScannedOption);
|
|
||||||
#else
|
|
||||||
*(void**)retval =
|
|
||||||
NSZoneCalloc(NSDefaultMallocZone(), retLength, 1);
|
|
||||||
/* We are responsible for making sure this memory gets free'd
|
|
||||||
eventually. Ask NSData class to autorelease it. */
|
|
||||||
[NSData dataWithBytesNoCopy: *(void**)retval
|
|
||||||
length: retLength];
|
|
||||||
#endif
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = *(void**)retval;
|
|
||||||
/* Decode the return value into the memory we allocated. */
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _C_STRUCT_B:
|
|
||||||
case _C_UNION_B:
|
|
||||||
case _C_ARY_B:
|
|
||||||
/* Decode the return value into the memory we allocated. */
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _C_FLT:
|
|
||||||
case _C_DBL:
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _C_VOID:
|
|
||||||
{
|
|
||||||
ctxt->type = @encode(int);
|
|
||||||
ctxt->flags = 0;
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[inv setReturnValue: retval];
|
|
||||||
|
|
||||||
/* Decode the values returned by reference. Note: this logic
|
|
||||||
must match exactly the code in callframe_do_call(); that
|
|
||||||
function should decode exactly what we encode here. */
|
|
||||||
|
|
||||||
if (out_parameters)
|
|
||||||
{
|
|
||||||
/* Step through all the arguments, finding the ones that were
|
|
||||||
passed by reference. */
|
|
||||||
for (tmptype = objc_skip_argspec (tmptype), argnum = 0;
|
|
||||||
*tmptype != '\0';
|
|
||||||
tmptype = objc_skip_argspec (tmptype), argnum++)
|
|
||||||
{
|
|
||||||
/* Get the type qualifiers, like IN, OUT, INOUT, ONEWAY. */
|
|
||||||
flags = objc_get_type_qualifiers(tmptype);
|
|
||||||
/* Skip over the type qualifiers, so now TYPE is
|
|
||||||
pointing directly at the char corresponding to the
|
|
||||||
argument's type, as defined in <objc/objc-api.h> */
|
|
||||||
tmptype = objc_skip_type_qualifiers(tmptype);
|
|
||||||
|
|
||||||
/* Decide how, (or whether or not), to encode the
|
|
||||||
argument depending on its FLAGS and TMPTYPE. */
|
|
||||||
datum = callframe_arg_addr(cframe, argnum);
|
|
||||||
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = datum;
|
|
||||||
ctxt->flags = flags;
|
|
||||||
|
|
||||||
if (*tmptype == _C_PTR
|
|
||||||
&& ((flags & _F_OUT) || !(flags & _F_IN)))
|
|
||||||
{
|
|
||||||
void *ptr;
|
|
||||||
|
|
||||||
/* The argument is a pointer (to a non-char), and
|
|
||||||
the pointer's value is qualified as an OUT
|
|
||||||
parameter, or it not explicitly qualified as an
|
|
||||||
IN parameter, then it is a pass-by-reference
|
|
||||||
argument.*/
|
|
||||||
tmptype++;
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
|
|
||||||
/* Use the original pointer to find the buffer
|
|
||||||
* to store the returned data */
|
|
||||||
[inv getArgument: &ptr atIndex: argnum];
|
|
||||||
ctxt->datum = ptr;
|
|
||||||
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
else if (*tmptype == _C_CHARPTR
|
|
||||||
&& ((flags & _F_OUT) || !(flags & _F_IN)))
|
|
||||||
{
|
|
||||||
/* The argument is a pointer char string, and the
|
|
||||||
pointer's value is qualified as an OUT parameter,
|
|
||||||
or it not explicitly qualified as an IN
|
|
||||||
parameter, then it is a pass-by-reference
|
|
||||||
argument. Encode it.*/
|
|
||||||
/* xxx Perhaps we could save time and space by
|
|
||||||
saving a copy of the string before the method
|
|
||||||
call, and then comparing it to this string; if it
|
|
||||||
didn't change, don't bother to send it back
|
|
||||||
again. */
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
[inv setArgument: datum atIndex: argnum];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctxt->type = 0;
|
|
||||||
ctxt->datum = 0;
|
|
||||||
(*decoder) (ctxt); /* Tell it we have finished. */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctxt->datToFree != 0)
|
|
||||||
{
|
|
||||||
#if !GS_WITH_GC
|
|
||||||
NSZoneFree(NSDefaultMallocZone(), ctxt->datToFree);
|
|
||||||
#endif
|
|
||||||
ctxt->datToFree = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -61,12 +61,4 @@ extern void *cifframe_arg_addr(cifframe_t *cframe, int index);
|
||||||
extern BOOL cifframe_decode_arg (const char *type, void* buffer);
|
extern BOOL cifframe_decode_arg (const char *type, void* buffer);
|
||||||
extern BOOL cifframe_encode_arg (const char *type, void* buffer);
|
extern BOOL cifframe_encode_arg (const char *type, void* buffer);
|
||||||
|
|
||||||
extern void cifframe_do_call (DOContext *ctxt,
|
|
||||||
void(*decoder)(DOContext*),
|
|
||||||
void(*encoder)(DOContext*));
|
|
||||||
extern void cifframe_build_return (NSInvocation *inv,
|
|
||||||
const char *type,
|
|
||||||
BOOL out_parameters,
|
|
||||||
void(*decoder)(DOContext*),
|
|
||||||
DOContext* ctxt);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -504,14 +504,6 @@ cifframe_type(const char *typePtr, const char **advance)
|
||||||
return ftype;
|
return ftype;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Ugly hack to make it easier to invoke a method from outside
|
|
||||||
an NSInvocation class. Hopefully simplication of NSConnection
|
|
||||||
could remove this hack */
|
|
||||||
typedef struct _NSInvocation_t {
|
|
||||||
@defs(NSInvocation)
|
|
||||||
} NSInvocation_t;
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
/* Functions for handling sending and receiving messages accross a
|
/* Functions for handling sending and receiving messages accross a
|
||||||
connection
|
connection
|
||||||
|
@ -577,612 +569,3 @@ cifframe_encode_arg (const char *type, void* buffer)
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cifframe_do_call()
|
|
||||||
|
|
||||||
This function decodes the arguments of method call, builds a
|
|
||||||
cifframe, and invokes the method using GSFFIInvokeWithTargetAndImp
|
|
||||||
then it encodes the return value and any pass-by-reference arguments.
|
|
||||||
|
|
||||||
An entry, ctxt->type should be a string that describes the return value
|
|
||||||
and arguments. It's argument types and argument type qualifiers
|
|
||||||
should match exactly those that were used when the arguments were
|
|
||||||
encoded. cifframe_do_call() uses this information to determine
|
|
||||||
which variable types it should decode.
|
|
||||||
|
|
||||||
The type info is used to get the types and type qualifiers, but not
|
|
||||||
to get the register and stack locations---we get that information
|
|
||||||
from the selector type of the SEL that is decoded as the second
|
|
||||||
argument. In this way, the type info may come from a machine
|
|
||||||
of a different architecture. Having the original type info is
|
|
||||||
good, just in case the machine running cifframe_do_call() has some
|
|
||||||
slightly different qualifiers. Using different qualifiers for
|
|
||||||
encoding and decoding could lead to massive confusion.
|
|
||||||
|
|
||||||
|
|
||||||
DECODER should be a pointer to a function that obtains the method's
|
|
||||||
argument values. For example:
|
|
||||||
|
|
||||||
void my_decoder (DOContext *ctxt)
|
|
||||||
|
|
||||||
CTXT contains the context information for the item to decode.
|
|
||||||
|
|
||||||
cifframe_do_call() calls this function once for each of the methods
|
|
||||||
arguments. The DECODER function should place the ARGNUM'th
|
|
||||||
argument's value at the memory location ctxt->datum.
|
|
||||||
cifframe_do_call() calls this function once with ctxt->datum 0,
|
|
||||||
and ctxt->type 0 to denote completion of decoding.
|
|
||||||
|
|
||||||
|
|
||||||
If DECODER malloc's new memory in the course of doing its
|
|
||||||
business, then DECODER is responsible for making sure that the
|
|
||||||
memory will get free eventually. For example, if DECODER uses
|
|
||||||
-decodeValueOfCType:at:withName: to decode a char* string, you
|
|
||||||
should remember that -decodeValueOfCType:at:withName: malloc's
|
|
||||||
new memory to hold the string, and DECODER should autorelease the
|
|
||||||
malloc'ed pointer, using the NSData class.
|
|
||||||
|
|
||||||
|
|
||||||
ENCODER should be a pointer to a function that records the method's
|
|
||||||
return value and pass-by-reference values. For example:
|
|
||||||
|
|
||||||
void my_encoder (DOContext *ctxt)
|
|
||||||
|
|
||||||
CTXT contains the context information for the item to encode.
|
|
||||||
|
|
||||||
cifframe_do_call() calls this function after the method has been
|
|
||||||
run---once for the return value, and once for each of the
|
|
||||||
pass-by-reference parameters. The ENCODER function should place
|
|
||||||
the value at memory location ctxt->datum wherever the user wants to
|
|
||||||
record the ARGNUM'th return value.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
void
|
|
||||||
cifframe_do_call (DOContext *ctxt,
|
|
||||||
void(*decoder)(DOContext*),
|
|
||||||
void(*encoder)(DOContext*))
|
|
||||||
{
|
|
||||||
/* The method type string obtained from the target's OBJC_METHOD
|
|
||||||
structure for the selector we're sending. */
|
|
||||||
const char *type;
|
|
||||||
/* A pointer into the local variable TYPE string. */
|
|
||||||
const char *tmptype;
|
|
||||||
/* A pointer into the argument ENCODED_TYPES string. */
|
|
||||||
const char *etmptype;
|
|
||||||
/* The target object that will receive the message. */
|
|
||||||
id object;
|
|
||||||
/* The selector for the message we're sending to the TARGET. */
|
|
||||||
SEL selector;
|
|
||||||
/* The OBJECT's Method(_t) pointer for the SELECTOR. */
|
|
||||||
GSMethod meth = 0;
|
|
||||||
/* The OBJECT's implementation of the SELECTOR. */
|
|
||||||
IMP method_implementation;
|
|
||||||
/* Type qualifier flags; see <objc/objc-api.h>. */
|
|
||||||
unsigned flags;
|
|
||||||
/* Which argument number are we processing now? */
|
|
||||||
int argnum;
|
|
||||||
/* The cif information for calling the method */
|
|
||||||
cifframe_t *cframe;
|
|
||||||
/* Does the method have any arguments that are passed by reference?
|
|
||||||
If so, we need to encode them, since the method may have changed them. */
|
|
||||||
BOOL out_parameters = NO;
|
|
||||||
/* A dummy invocation to pass to the function that invokes our method */
|
|
||||||
NSInvocation_t *inv;
|
|
||||||
/* Signature information */
|
|
||||||
NSMethodSignature *sig;
|
|
||||||
void *retval;
|
|
||||||
const char *encoded_types = ctxt->type;
|
|
||||||
|
|
||||||
/* Decode the object, (which is always the first argument to a method),
|
|
||||||
into the local variable OBJECT. */
|
|
||||||
ctxt->type = @encode(id);
|
|
||||||
ctxt->datum = &object;
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
NSCParameterAssert (object);
|
|
||||||
|
|
||||||
/* Decode the selector, (which is always the second argument to a
|
|
||||||
method), into the local variable SELECTOR. */
|
|
||||||
/* xxx @encode(SEL) produces "^v" in gcc 2.5.8. It should be ":" */
|
|
||||||
ctxt->type = @encode(SEL);
|
|
||||||
ctxt->datum = &selector;
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
NSCParameterAssert (selector);
|
|
||||||
|
|
||||||
/* Get the "selector type" for this method. The "selector type" is
|
|
||||||
a string that lists the return and argument types, and also
|
|
||||||
indicates in which registers and where on the stack the arguments
|
|
||||||
should be placed before the method call. The selector type
|
|
||||||
string we get here should have the same argument and return types
|
|
||||||
as the ENCODED_TYPES string, but it will have different register
|
|
||||||
and stack locations if the ENCODED_TYPES came from a machine of a
|
|
||||||
different architecture. */
|
|
||||||
if (GSObjCIsClass(object))
|
|
||||||
{
|
|
||||||
meth = GSGetMethod(object, selector, NO, YES);
|
|
||||||
}
|
|
||||||
else if (GSObjCIsInstance(object))
|
|
||||||
{
|
|
||||||
meth = GSGetMethod(GSObjCClass(object), selector, YES, YES);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[NSException raise: NSInvalidArgumentException
|
|
||||||
format: @"decoded object %p is invalid", object];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (meth != 0)
|
|
||||||
{
|
|
||||||
type = meth->method_types;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NSDebugLog(@"Local object <%p %s> doesn't implement: %s directly. "
|
|
||||||
@"Will search for arbitrary signature.",
|
|
||||||
object,
|
|
||||||
GSNameFromClass(GSObjCIsClass(object)
|
|
||||||
? object : (id)GSObjCClass(object)),
|
|
||||||
GSNameFromSelector(selector));
|
|
||||||
type = GSTypesFromSelector(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure we successfully got the method type, and that its
|
|
||||||
types match the ENCODED_TYPES. */
|
|
||||||
NSCParameterAssert (type);
|
|
||||||
if (GSSelectorTypesMatch(encoded_types, type) == NO)
|
|
||||||
{
|
|
||||||
[NSException raise: NSInvalidArgumentException
|
|
||||||
format: @"cifframe_do_call types (%s / %s) missmatch for %s",
|
|
||||||
encoded_types, type, GSNameFromSelector(selector)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Build the cif frame */
|
|
||||||
sig = [NSMethodSignature signatureWithObjCTypes: type];
|
|
||||||
cframe = cifframe_from_info([sig methodInfo], [sig numberOfArguments],
|
|
||||||
&retval);
|
|
||||||
ctxt->datToFree = cframe;
|
|
||||||
|
|
||||||
/* Put OBJECT and SELECTOR into the ARGFRAME. */
|
|
||||||
|
|
||||||
/* Initialize our temporary pointers into the method type strings. */
|
|
||||||
tmptype = objc_skip_argspec (type);
|
|
||||||
etmptype = objc_skip_argspec (encoded_types);
|
|
||||||
NSCParameterAssert (*tmptype == _C_ID);
|
|
||||||
/* Put the target object there. */
|
|
||||||
cifframe_set_arg(cframe, 0, &object, sizeof(id));
|
|
||||||
/* Get a pointer into ARGFRAME, pointing to the location where the
|
|
||||||
second argument is to be stored. */
|
|
||||||
tmptype = objc_skip_argspec (tmptype);
|
|
||||||
etmptype = objc_skip_argspec(etmptype);
|
|
||||||
NSCParameterAssert (*tmptype == _C_SEL);
|
|
||||||
/* Put the selector there. */
|
|
||||||
cifframe_set_arg(cframe, 1, &selector, sizeof(SEL));
|
|
||||||
|
|
||||||
|
|
||||||
/* Decode arguments after OBJECT and SELECTOR, and put them into the
|
|
||||||
ARGFRAME. Step TMPTYPE and ETMPTYPE in lock-step through their
|
|
||||||
method type strings. */
|
|
||||||
|
|
||||||
for (tmptype = objc_skip_argspec (tmptype),
|
|
||||||
etmptype = objc_skip_argspec (etmptype), argnum = 2;
|
|
||||||
*tmptype != '\0';
|
|
||||||
tmptype = objc_skip_argspec (tmptype),
|
|
||||||
etmptype = objc_skip_argspec (etmptype), argnum++)
|
|
||||||
{
|
|
||||||
/* Get the type qualifiers, like IN, OUT, INOUT, ONEWAY. */
|
|
||||||
flags = objc_get_type_qualifiers (etmptype);
|
|
||||||
/* Skip over the type qualifiers, so now TYPE is pointing directly
|
|
||||||
at the char corresponding to the argument's type, as defined
|
|
||||||
in <objc/objc-api.h> */
|
|
||||||
tmptype = objc_skip_type_qualifiers(tmptype);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Setup information in context.
|
|
||||||
*/
|
|
||||||
ctxt->datum = cifframe_arg_addr(cframe, argnum);
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->flags = flags;
|
|
||||||
|
|
||||||
/* Decide how, (or whether or not), to decode the argument
|
|
||||||
depending on its FLAGS and TMPTYPE. Only the first two cases
|
|
||||||
involve parameters that may potentially be passed by
|
|
||||||
reference, and thus only the first two may change the value
|
|
||||||
of OUT_PARAMETERS. *** Note: This logic must match exactly
|
|
||||||
the code in cifframe_dissect_call(); that function should
|
|
||||||
encode exactly what we decode here. *** */
|
|
||||||
|
|
||||||
switch (*tmptype)
|
|
||||||
{
|
|
||||||
|
|
||||||
case _C_CHARPTR:
|
|
||||||
/* Handle a (char*) argument. */
|
|
||||||
/* If the char* is qualified as an OUT parameter, or if it
|
|
||||||
not explicitly qualified as an IN parameter, then we will
|
|
||||||
have to get this char* again after the method is run,
|
|
||||||
because the method may have changed it. Set
|
|
||||||
OUT_PARAMETERS accordingly. */
|
|
||||||
if ((flags & _F_OUT) || !(flags & _F_IN))
|
|
||||||
out_parameters = YES;
|
|
||||||
/* If the char* is qualified as an IN parameter, or not
|
|
||||||
explicity qualified as an OUT parameter, then decode it.
|
|
||||||
Note: the decoder allocates memory for holding the
|
|
||||||
string, and it is also responsible for making sure that
|
|
||||||
the memory gets freed eventually, (usually through the
|
|
||||||
autorelease of NSData object). */
|
|
||||||
if ((flags & _F_IN) || !(flags & _F_OUT))
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _C_PTR:
|
|
||||||
/* If the pointer's value is qualified as an OUT parameter,
|
|
||||||
or if it not explicitly qualified as an IN parameter,
|
|
||||||
then we will have to get the value pointed to again after
|
|
||||||
the method is run, because the method may have changed
|
|
||||||
it. Set OUT_PARAMETERS accordingly. */
|
|
||||||
if ((flags & _F_OUT) || !(flags & _F_IN))
|
|
||||||
out_parameters = YES;
|
|
||||||
|
|
||||||
/* Handle an argument that is a pointer to a non-char. But
|
|
||||||
(void*) and (anything**) is not allowed. */
|
|
||||||
/* The argument is a pointer to something; increment TYPE
|
|
||||||
so we can see what it is a pointer to. */
|
|
||||||
tmptype++;
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
/* Allocate some memory to be pointed to, and to hold the
|
|
||||||
value. Note that it is allocated on the stack, and
|
|
||||||
methods that want to keep the data pointed to, will have
|
|
||||||
to make their own copies. */
|
|
||||||
*(void**)ctxt->datum = alloca (objc_sizeof_type (tmptype));
|
|
||||||
ctxt->datum = *(void**)ctxt->datum;
|
|
||||||
/* If the pointer's value is qualified as an IN parameter,
|
|
||||||
or not explicity qualified as an OUT parameter, then
|
|
||||||
decode it. */
|
|
||||||
if ((flags & _F_IN) || !(flags & _F_OUT))
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Handle arguments of all other types. */
|
|
||||||
/* NOTE FOR OBJECTS: Unlike [Decoder decodeObjectAt:..],
|
|
||||||
this function does not generate a reference to the
|
|
||||||
object; the object may be autoreleased; if the method
|
|
||||||
wants to keep a reference to the object, it will have to
|
|
||||||
-retain it. */
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* End of the for () loop that enumerates the method's arguments. */
|
|
||||||
ctxt->type = 0;
|
|
||||||
ctxt->datum = 0;
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
|
|
||||||
|
|
||||||
/* Invoke the method! */
|
|
||||||
|
|
||||||
/* Find the target object's implementation of this selector. */
|
|
||||||
method_implementation = objc_msg_lookup (object, selector);
|
|
||||||
NSCParameterAssert (method_implementation);
|
|
||||||
/* Do it! Send the message to the target, and get the return value
|
|
||||||
in retval. We need to encode any pass-by-reference info */
|
|
||||||
inv = (NSInvocation_t *)NSAllocateObject([NSInvocation class], 0,
|
|
||||||
NSDefaultMallocZone());
|
|
||||||
inv->_retval = retval;
|
|
||||||
inv->_selector = selector;
|
|
||||||
inv->_cframe = cframe;
|
|
||||||
inv->_info = [sig methodInfo];
|
|
||||||
inv->_numArgs = [sig numberOfArguments];
|
|
||||||
ctxt->objToFree = (id)inv;
|
|
||||||
GSFFIInvokeWithTargetAndImp((NSInvocation *)inv, object,
|
|
||||||
method_implementation);
|
|
||||||
ctxt->objToFree = nil;
|
|
||||||
NSDeallocateObject((NSInvocation *)inv);
|
|
||||||
|
|
||||||
/* Encode the return value and pass-by-reference values, if there
|
|
||||||
are any. This logic must match exactly that in
|
|
||||||
cifframe_build_return(). */
|
|
||||||
/* OUT_PARAMETERS should be true here in exactly the same
|
|
||||||
situations as it was true in cifframe_dissect_call(). */
|
|
||||||
|
|
||||||
/* Get the qualifier type of the return value. */
|
|
||||||
flags = objc_get_type_qualifiers (encoded_types);
|
|
||||||
/* Get the return type; store it our two temporary char*'s. */
|
|
||||||
etmptype = objc_skip_type_qualifiers (encoded_types);
|
|
||||||
tmptype = objc_skip_type_qualifiers (type);
|
|
||||||
|
|
||||||
/* Only encode return values if there is a non-void return value,
|
|
||||||
a non-oneway void return value, or if there are values that were
|
|
||||||
passed by reference. */
|
|
||||||
|
|
||||||
ctxt->flags = flags;
|
|
||||||
|
|
||||||
/* If there is a return value, encode it. */
|
|
||||||
if (*tmptype == _C_VOID)
|
|
||||||
{
|
|
||||||
if ((flags & _F_ONEWAY) == 0)
|
|
||||||
{
|
|
||||||
int dummy = 0;
|
|
||||||
|
|
||||||
ctxt->type = @encode(int);
|
|
||||||
ctxt->datum = (void*)&dummy;
|
|
||||||
(*encoder) (ctxt);
|
|
||||||
}
|
|
||||||
/* No return value to encode; do nothing. */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (*tmptype == _C_PTR)
|
|
||||||
{
|
|
||||||
/* The argument is a pointer to something; increment TYPE
|
|
||||||
so we can see what it is a pointer to. */
|
|
||||||
tmptype++;
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = *(void**)retval;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cifframe_decode_arg(tmptype, retval);
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = retval;
|
|
||||||
}
|
|
||||||
/* Encode the value that was pointed to. */
|
|
||||||
(*encoder) (ctxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Encode the values returned by reference. Note: this logic
|
|
||||||
must match exactly the code in cifframe_build_return(); that
|
|
||||||
function should decode exactly what we encode here. */
|
|
||||||
|
|
||||||
if (out_parameters)
|
|
||||||
{
|
|
||||||
/* Step through all the arguments, finding the ones that were
|
|
||||||
passed by reference. */
|
|
||||||
for (tmptype = objc_skip_argspec (tmptype),
|
|
||||||
argnum = 0,
|
|
||||||
etmptype = objc_skip_argspec (etmptype);
|
|
||||||
*tmptype != '\0';
|
|
||||||
tmptype = objc_skip_argspec (tmptype),
|
|
||||||
argnum++,
|
|
||||||
etmptype = objc_skip_argspec (etmptype))
|
|
||||||
{
|
|
||||||
/* Get the type qualifiers, like IN, OUT, INOUT, ONEWAY. */
|
|
||||||
flags = objc_get_type_qualifiers(etmptype);
|
|
||||||
/* Skip over the type qualifiers, so now TYPE is pointing directly
|
|
||||||
at the char corresponding to the argument's type, as defined
|
|
||||||
in <objc/objc-api.h> */
|
|
||||||
tmptype = objc_skip_type_qualifiers (tmptype);
|
|
||||||
|
|
||||||
/* Decide how, (or whether or not), to encode the argument
|
|
||||||
depending on its FLAGS and TMPTYPE. */
|
|
||||||
if (((flags & _F_OUT) || !(flags & _F_IN))
|
|
||||||
&& (*tmptype == _C_PTR || *tmptype == _C_CHARPTR))
|
|
||||||
{
|
|
||||||
ctxt->flags = flags;
|
|
||||||
ctxt->datum = cifframe_arg_addr(cframe, argnum);
|
|
||||||
|
|
||||||
if (*tmptype == _C_PTR)
|
|
||||||
{
|
|
||||||
/* The argument is a pointer (to a non-char), and the
|
|
||||||
pointer's value is qualified as an OUT parameter, or
|
|
||||||
it not explicitly qualified as an IN parameter, then
|
|
||||||
it is a pass-by-reference argument.*/
|
|
||||||
ctxt->type = ++tmptype;
|
|
||||||
ctxt->datum = *(void**)ctxt->datum;
|
|
||||||
}
|
|
||||||
else if (*tmptype == _C_CHARPTR)
|
|
||||||
{
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
/* The argument is a pointer char string, and the
|
|
||||||
pointer's value is qualified as an OUT parameter, or
|
|
||||||
it not explicitly qualified as an IN parameter, then
|
|
||||||
it is a pass-by-reference argument. */
|
|
||||||
}
|
|
||||||
(*encoder) (ctxt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if !GS_WITH_GC
|
|
||||||
NSZoneFree(NSDefaultMallocZone(), ctxt->datToFree);
|
|
||||||
#endif
|
|
||||||
ctxt->datToFree = 0;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cifframe_build_return()
|
|
||||||
|
|
||||||
This function decodes the values returned from a method call,
|
|
||||||
sets up the invocation with the return value, and updates the
|
|
||||||
pass-by-reference arguments.
|
|
||||||
|
|
||||||
The callback function is finally called with the 'type' set to a null pointer
|
|
||||||
to tell it that the return value and all return parameters have been
|
|
||||||
dealt with. This permits the function to do any tidying up necessary. */
|
|
||||||
|
|
||||||
void
|
|
||||||
cifframe_build_return (NSInvocation *inv,
|
|
||||||
const char *type,
|
|
||||||
BOOL out_parameters,
|
|
||||||
void(*decoder)(DOContext *ctxt),
|
|
||||||
DOContext *ctxt)
|
|
||||||
{
|
|
||||||
/* Which argument number are we processing now? */
|
|
||||||
int argnum;
|
|
||||||
/* Type qualifier flags; see <objc/objc-api.h>. */
|
|
||||||
int flags;
|
|
||||||
/* A pointer into the TYPE string. */
|
|
||||||
const char *tmptype;
|
|
||||||
/* Points at individual arguments. */
|
|
||||||
void *datum;
|
|
||||||
const char *rettype;
|
|
||||||
/* A pointer to the memory holding the return value of the method. */
|
|
||||||
void *retval;
|
|
||||||
/* Storage for the argument information */
|
|
||||||
cifframe_t *cframe;
|
|
||||||
/* Signature information */
|
|
||||||
NSMethodSignature *sig;
|
|
||||||
|
|
||||||
/* Build the cif frame */
|
|
||||||
sig = [NSMethodSignature signatureWithObjCTypes: type];
|
|
||||||
cframe = cifframe_from_info([sig methodInfo], [sig numberOfArguments],
|
|
||||||
&retval);
|
|
||||||
ctxt->datToFree = cframe;
|
|
||||||
|
|
||||||
/* Get the return type qualifier flags, and the return type. */
|
|
||||||
flags = objc_get_type_qualifiers(type);
|
|
||||||
tmptype = objc_skip_type_qualifiers(type);
|
|
||||||
rettype = tmptype;
|
|
||||||
|
|
||||||
/* Decode the return value and pass-by-reference values, if there
|
|
||||||
are any. OUT_PARAMETERS should be the value returned by
|
|
||||||
cifframe_dissect_call(). */
|
|
||||||
if (out_parameters || *tmptype != _C_VOID || (flags & _F_ONEWAY) == 0)
|
|
||||||
/* xxx What happens with method declared "- (oneway) foo: (out int*)ip;" */
|
|
||||||
/* xxx What happens with method declared "- (in char *) bar;" */
|
|
||||||
/* xxx Is this right? Do we also have to check _F_ONEWAY? */
|
|
||||||
{
|
|
||||||
/* ARGNUM == -1 signifies to DECODER() that this is the return
|
|
||||||
value, not an argument. */
|
|
||||||
|
|
||||||
/* If there is a return value, decode it, and put it in retval. */
|
|
||||||
if (*tmptype != _C_VOID || (flags & _F_ONEWAY) == 0)
|
|
||||||
{
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = retval;
|
|
||||||
ctxt->flags = flags;
|
|
||||||
|
|
||||||
switch (*tmptype)
|
|
||||||
{
|
|
||||||
case _C_PTR:
|
|
||||||
{
|
|
||||||
unsigned retLength;
|
|
||||||
|
|
||||||
/* We are returning a pointer to something. */
|
|
||||||
/* Increment TYPE so we can see what it is a pointer to. */
|
|
||||||
tmptype++;
|
|
||||||
retLength = objc_sizeof_type(tmptype);
|
|
||||||
/* Allocate memory to hold the value we're pointing to. */
|
|
||||||
#if GS_WITH_GC
|
|
||||||
*(void**)retval =
|
|
||||||
NSAllocateCollectable(retLength, NSScannedOption);
|
|
||||||
#else
|
|
||||||
*(void**)retval =
|
|
||||||
NSZoneCalloc(NSDefaultMallocZone(), retLength, 1);
|
|
||||||
/* We are responsible for making sure this memory gets free'd
|
|
||||||
eventually. Ask NSData class to autorelease it. */
|
|
||||||
[NSData dataWithBytesNoCopy: *(void**)retval
|
|
||||||
length: retLength];
|
|
||||||
#endif
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = *(void**)retval;
|
|
||||||
/* Decode the return value into the memory we allocated. */
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _C_STRUCT_B:
|
|
||||||
case _C_UNION_B:
|
|
||||||
case _C_ARY_B:
|
|
||||||
/* Decode the return value into the memory we allocated. */
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _C_FLT:
|
|
||||||
case _C_DBL:
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _C_VOID:
|
|
||||||
{
|
|
||||||
ctxt->type = @encode(int);
|
|
||||||
ctxt->flags = 0;
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[inv setReturnValue: retval];
|
|
||||||
|
|
||||||
/* Decode the values returned by reference. Note: this logic
|
|
||||||
must match exactly the code in cifframe_do_call(); that
|
|
||||||
function should decode exactly what we encode here. */
|
|
||||||
|
|
||||||
if (out_parameters)
|
|
||||||
{
|
|
||||||
/* Step through all the arguments, finding the ones that were
|
|
||||||
passed by reference. */
|
|
||||||
for (tmptype = objc_skip_argspec (tmptype), argnum = 0;
|
|
||||||
*tmptype != '\0';
|
|
||||||
tmptype = objc_skip_argspec (tmptype), argnum++)
|
|
||||||
{
|
|
||||||
/* Get the type qualifiers, like IN, OUT, INOUT, ONEWAY. */
|
|
||||||
flags = objc_get_type_qualifiers(tmptype);
|
|
||||||
/* Skip over the type qualifiers, so now TYPE is
|
|
||||||
pointing directly at the char corresponding to the
|
|
||||||
argument's type, as defined in <objc/objc-api.h> */
|
|
||||||
tmptype = objc_skip_type_qualifiers(tmptype);
|
|
||||||
|
|
||||||
/* Decide how, (or whether or not), to encode the
|
|
||||||
argument depending on its FLAGS and TMPTYPE. */
|
|
||||||
datum = cifframe_arg_addr(cframe, argnum);
|
|
||||||
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
ctxt->datum = datum;
|
|
||||||
ctxt->flags = flags;
|
|
||||||
|
|
||||||
if (*tmptype == _C_PTR
|
|
||||||
&& ((flags & _F_OUT) || !(flags & _F_IN)))
|
|
||||||
{
|
|
||||||
void *ptr;
|
|
||||||
/* The argument is a pointer (to a non-char), and
|
|
||||||
the pointer's value is qualified as an OUT
|
|
||||||
parameter, or it not explicitly qualified as an
|
|
||||||
IN parameter, then it is a pass-by-reference
|
|
||||||
argument.*/
|
|
||||||
tmptype++;
|
|
||||||
ctxt->type = tmptype;
|
|
||||||
|
|
||||||
/* Use the original pointer to find the buffer
|
|
||||||
* to store the returned data */
|
|
||||||
[inv getArgument: &ptr atIndex: argnum];
|
|
||||||
ctxt->datum = ptr;
|
|
||||||
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
}
|
|
||||||
else if (*tmptype == _C_CHARPTR
|
|
||||||
&& ((flags & _F_OUT) || !(flags & _F_IN)))
|
|
||||||
{
|
|
||||||
/* The argument is a pointer char string, and the
|
|
||||||
pointer's value is qualified as an OUT parameter,
|
|
||||||
or it not explicitly qualified as an IN
|
|
||||||
parameter, then it is a pass-by-reference
|
|
||||||
argument. Encode it.*/
|
|
||||||
/* xxx Perhaps we could save time and space by
|
|
||||||
saving a copy of the string before the method
|
|
||||||
call, and then comparing it to this string; if it
|
|
||||||
didn't change, don't bother to send it back
|
|
||||||
again. */
|
|
||||||
(*decoder) (ctxt);
|
|
||||||
[inv setArgument: datum atIndex: argnum];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctxt->type = 0;
|
|
||||||
ctxt->datum = 0;
|
|
||||||
(*decoder) (ctxt); /* Tell it we have finished. */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctxt->datToFree != 0)
|
|
||||||
{
|
|
||||||
#if !GS_WITH_GC
|
|
||||||
NSZoneFree(NSDefaultMallocZone(), ctxt->datToFree);
|
|
||||||
#endif
|
|
||||||
ctxt->datToFree = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue