2001-12-17 14:31:42 +00:00
|
|
|
|
/** callframe.m - Wrapper/Objective-C interface for ffcall function interface
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
Copyright (C) 2000, Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
Written by: Adam Fedor <fedor@gnu.org>
|
|
|
|
|
Created: Nov 2000
|
|
|
|
|
|
|
|
|
|
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 Library 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 Library General Public
|
|
|
|
|
License along with this library; if not, write to the Free
|
|
|
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include "callframe.h"
|
|
|
|
|
#include <Foundation/NSException.h>
|
|
|
|
|
#include <Foundation/NSData.h>
|
|
|
|
|
#include <base/GSInvocation.h>
|
|
|
|
|
|
|
|
|
|
#if defined(ALPHA) || (defined(MIPS) && (_MIPS_SIM == _ABIN32))
|
|
|
|
|
typedef long long smallret_t;
|
|
|
|
|
#else
|
|
|
|
|
typedef int smallret_t;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
extern BOOL sel_types_match(const char* t1, const char* t2);
|
|
|
|
|
|
|
|
|
|
callframe_t *
|
|
|
|
|
callframe_from_info (NSArgumentInfo *info, int numargs, void **retval)
|
|
|
|
|
{
|
2001-06-27 10:25:54 +00:00
|
|
|
|
unsigned size = sizeof(callframe_t);
|
|
|
|
|
unsigned align = __alignof(double);
|
2002-03-22 17:52:33 +00:00
|
|
|
|
unsigned offset = 0;
|
2001-06-27 10:25:54 +00:00
|
|
|
|
void *buf;
|
|
|
|
|
int i;
|
|
|
|
|
callframe_t *cframe;
|
|
|
|
|
|
|
|
|
|
if (numargs > 0)
|
|
|
|
|
{
|
|
|
|
|
if (size % align != 0)
|
|
|
|
|
{
|
|
|
|
|
size += align - (size % align);
|
|
|
|
|
}
|
|
|
|
|
offset = size;
|
|
|
|
|
size += numargs * sizeof(void*);
|
|
|
|
|
if (size % align != 0)
|
|
|
|
|
{
|
|
|
|
|
size += (align - (size % align));
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < numargs; i++)
|
|
|
|
|
{
|
|
|
|
|
size += info[i+1].size;
|
|
|
|
|
|
|
|
|
|
if (size % align != 0)
|
|
|
|
|
{
|
|
|
|
|
size += (align - size % align);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
2001-09-21 15:14:57 +00:00
|
|
|
|
/*
|
|
|
|
|
* If we need space allocated to store a return value,
|
|
|
|
|
* make room for it at the end of the callframe so we
|
|
|
|
|
* only need to do a single malloc.
|
|
|
|
|
*/
|
|
|
|
|
if (retval)
|
|
|
|
|
{
|
|
|
|
|
unsigned full = size;
|
|
|
|
|
unsigned pos;
|
|
|
|
|
|
|
|
|
|
if (full % align != 0)
|
|
|
|
|
{
|
|
|
|
|
full += (align - full % align);
|
|
|
|
|
}
|
|
|
|
|
pos = full;
|
|
|
|
|
full += MAX(info[0].size, sizeof(smallret_t));
|
2002-02-20 05:08:46 +00:00
|
|
|
|
cframe = buf = NSZoneCalloc(NSDefaultMallocZone(), full, 1);
|
2001-09-21 15:14:57 +00:00
|
|
|
|
if (cframe)
|
|
|
|
|
{
|
|
|
|
|
*retval = buf + pos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-02-20 05:08:46 +00:00
|
|
|
|
cframe = buf = NSZoneCalloc(NSDefaultMallocZone(), size, 1);
|
2001-09-21 15:14:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-08 19:06:00 +00:00
|
|
|
|
if (cframe)
|
|
|
|
|
{
|
2001-06-27 10:25:54 +00:00
|
|
|
|
cframe->nargs = numargs;
|
|
|
|
|
cframe->args = buf + offset;
|
|
|
|
|
offset += numargs * sizeof(void*);
|
|
|
|
|
if (offset % align != 0)
|
|
|
|
|
{
|
|
|
|
|
offset += align - (offset % align);
|
|
|
|
|
}
|
2000-12-08 19:06:00 +00:00
|
|
|
|
for (i = 0; i < cframe->nargs; i++)
|
2001-06-27 10:25:54 +00:00
|
|
|
|
{
|
|
|
|
|
cframe->args[i] = buf + offset;
|
|
|
|
|
|
|
|
|
|
offset += info[i+1].size;
|
|
|
|
|
|
|
|
|
|
if (offset % align != 0)
|
|
|
|
|
{
|
|
|
|
|
offset += (align - offset % align);
|
|
|
|
|
}
|
|
|
|
|
}
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cframe;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
callframe_set_arg(callframe_t *cframe, int index, void *buffer, int size)
|
|
|
|
|
{
|
|
|
|
|
if (index < 0 || index >= cframe->nargs)
|
|
|
|
|
return;
|
|
|
|
|
memcpy(cframe->args[index], buffer, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
callframe_get_arg(callframe_t *cframe, int index, void *buffer, int size)
|
|
|
|
|
{
|
|
|
|
|
if (index < 0 || index >= cframe->nargs)
|
|
|
|
|
return;
|
|
|
|
|
memcpy(buffer, cframe->args[index], size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
callframe_arg_addr(callframe_t *cframe, int index)
|
|
|
|
|
{
|
|
|
|
|
if (index < 0 || index >= cframe->nargs)
|
|
|
|
|
return NULL;
|
|
|
|
|
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()
|
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
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.
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
An entry, ctxt->type should be a string that describes the return value
|
2000-12-08 19:06:00 +00:00
|
|
|
|
and arguments. It's argument types and argument type qualifiers
|
|
|
|
|
should match exactly those that were used when the arguments were
|
2001-09-19 21:31:18 +00:00
|
|
|
|
encoded. callframe_do_call() uses this information to determine
|
|
|
|
|
which variable types it should decode.
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
The type info is used to get the types and type qualifiers, but not
|
2000-12-08 19:06:00 +00:00
|
|
|
|
to get the register and stack locations---we get that information
|
|
|
|
|
from the selector type of the SEL that is decoded as the second
|
2001-09-19 21:31:18 +00:00
|
|
|
|
argument. In this way, the type info may come from a machine
|
|
|
|
|
of a different architecture. Having the original type info is
|
2000-12-08 19:06:00 +00:00
|
|
|
|
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:
|
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
void my_decoder (DOContext *ctxt)
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
CTXT contains the context information for the item to decode.
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
callframe_do_call() calls this function once for each of the methods
|
|
|
|
|
arguments. The DECODER function should place the ARGNUM'th
|
2001-09-19 21:31:18 +00:00
|
|
|
|
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.
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
void my_encoder (DOContext *ctxt)
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
CTXT contains the context information for the item to encode.
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
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
|
2001-09-19 21:31:18 +00:00
|
|
|
|
the value at memory location ctxt->datum wherever the user wants to
|
2000-12-08 19:06:00 +00:00
|
|
|
|
record the ARGNUM'th return value.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2001-09-19 21:31:18 +00:00
|
|
|
|
callframe_do_call (DOContext *ctxt,
|
|
|
|
|
void(*decoder)(DOContext*),
|
|
|
|
|
void(*encoder)(DOContext*))
|
2000-12-08 19:06:00 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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 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;
|
2001-09-21 15:14:57 +00:00
|
|
|
|
void *retval;
|
2001-09-19 21:31:18 +00:00
|
|
|
|
const char *encoded_types = ctxt->type;
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
/* Decode the object, (which is always the first argument to a method),
|
|
|
|
|
into the local variable OBJECT. */
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->type = @encode(id);
|
|
|
|
|
ctxt->datum = &object;
|
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
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 ":" */
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->type = @encode(SEL);
|
|
|
|
|
ctxt->datum = &selector;
|
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
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. */
|
2001-04-24 03:40:04 +00:00
|
|
|
|
#if NeXT_RUNTIME
|
2000-12-08 19:06:00 +00:00
|
|
|
|
{
|
|
|
|
|
Method m;
|
|
|
|
|
m = class_getInstanceMethod(object->isa, selector);
|
|
|
|
|
if (!m)
|
|
|
|
|
abort();
|
|
|
|
|
type = m->method_types;
|
|
|
|
|
}
|
|
|
|
|
#elif 0
|
|
|
|
|
{
|
|
|
|
|
Method_t m;
|
|
|
|
|
m = class_get_instance_method (object->class_pointer,
|
|
|
|
|
selector);
|
|
|
|
|
NSCParameterAssert (m);
|
|
|
|
|
type = m->method_types;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
type = sel_get_type (selector);
|
|
|
|
|
#endif /* NeXT_runtime */
|
|
|
|
|
|
|
|
|
|
/* Make sure we successfully got the method type, and that its
|
|
|
|
|
types match the ENCODED_TYPES. */
|
|
|
|
|
NSCParameterAssert (type);
|
|
|
|
|
NSCParameterAssert (sel_types_match(encoded_types, type));
|
|
|
|
|
|
|
|
|
|
/* Build the cif frame */
|
|
|
|
|
sig = [NSMethodSignature signatureWithObjCTypes: type];
|
|
|
|
|
cframe = callframe_from_info([sig methodInfo], [sig numberOfArguments],
|
|
|
|
|
&retval);
|
2001-09-21 15:14:57 +00:00
|
|
|
|
ctxt->datToFree = cframe;
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
/*
|
|
|
|
|
* Setup information in context.
|
|
|
|
|
*/
|
|
|
|
|
ctxt->datum = callframe_arg_addr(cframe, argnum);
|
|
|
|
|
ctxt->type = tmptype;
|
|
|
|
|
ctxt->flags = flags;
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
/* 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))
|
2001-09-19 21:31:18 +00:00
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
/* 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);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
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. */
|
2001-09-19 21:31:18 +00:00
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* End of the for() loop that enumerates the method's arguments. */
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->type = 0;
|
|
|
|
|
ctxt->datum = 0;
|
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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
|
2001-09-19 21:31:18 +00:00
|
|
|
|
in retval. We need to encode any pass-by-reference info */
|
2000-12-08 19:06:00 +00:00
|
|
|
|
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];
|
2001-09-21 15:14:57 +00:00
|
|
|
|
ctxt->objToFree = (id)inv;
|
2000-12-08 19:06:00 +00:00
|
|
|
|
GSFFCallInvokeWithTargetAndImp((NSInvocation *)inv, object,
|
|
|
|
|
method_implementation);
|
2001-09-21 15:14:57 +00:00
|
|
|
|
ctxt->objToFree = nil;
|
2000-12-08 19:06:00 +00:00
|
|
|
|
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. */
|
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->flags = flags;
|
|
|
|
|
|
2000-12-08 19:06:00 +00:00
|
|
|
|
/* If there is a return value, encode it. */
|
2001-09-19 21:31:18 +00:00
|
|
|
|
if (*tmptype == _C_VOID)
|
2000-12-08 19:06:00 +00:00
|
|
|
|
{
|
|
|
|
|
if ((flags & _F_ONEWAY) == 0)
|
|
|
|
|
{
|
2001-09-19 21:31:18 +00:00
|
|
|
|
int dummy = 0;
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->type = @encode(int);
|
|
|
|
|
ctxt->datum = (void*)&dummy;
|
|
|
|
|
(*encoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
2001-09-19 21:31:18 +00:00
|
|
|
|
/* No return value to encode; do nothing. */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (*tmptype == _C_PTR)
|
2000-12-08 19:06:00 +00:00
|
|
|
|
{
|
|
|
|
|
/* The argument is a pointer to something; increment TYPE
|
|
|
|
|
so we can see what it is a pointer to. */
|
|
|
|
|
tmptype++;
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->type = tmptype;
|
|
|
|
|
ctxt->datum = *(void**)ctxt->datum;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ctxt->type = tmptype;
|
|
|
|
|
ctxt->datum = retval;
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
2001-09-19 21:31:18 +00:00
|
|
|
|
/* Encode the value that was pointed to. */
|
|
|
|
|
(*encoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
2001-09-19 21:31:18 +00:00
|
|
|
|
if (((flags & _F_OUT) || !(flags & _F_IN))
|
|
|
|
|
&& (*tmptype == _C_PTR || *tmptype == _C_CHARPTR))
|
2000-12-08 19:06:00 +00:00
|
|
|
|
{
|
2001-09-19 21:31:18 +00:00
|
|
|
|
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);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-09-21 15:14:57 +00:00
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), ctxt->datToFree);
|
|
|
|
|
ctxt->datToFree = 0;
|
2001-06-27 09:42:57 +00:00
|
|
|
|
|
2000-12-08 19:06:00 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2002-08-20 15:07:58 +00:00
|
|
|
|
The callback function is finally called with the 'type' set to a null pointer
|
2000-12-08 19:06:00 +00:00
|
|
|
|
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
|
2001-09-19 21:31:18 +00:00
|
|
|
|
callframe_build_return (NSInvocation *inv,
|
2000-12-08 19:06:00 +00:00
|
|
|
|
const char *type,
|
|
|
|
|
BOOL out_parameters,
|
2001-09-19 21:31:18 +00:00
|
|
|
|
void(*decoder)(DOContext *ctxt),
|
|
|
|
|
DOContext *ctxt)
|
2000-12-08 19:06:00 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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);
|
2001-09-21 15:14:57 +00:00
|
|
|
|
ctxt->datToFree = cframe;
|
2000-12-08 19:06:00 +00:00
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->type = tmptype;
|
|
|
|
|
ctxt->datum = retval;
|
|
|
|
|
ctxt->flags = flags;
|
|
|
|
|
|
2000-12-08 19:06:00 +00:00
|
|
|
|
switch (*tmptype)
|
|
|
|
|
{
|
|
|
|
|
case _C_PTR:
|
2001-09-19 21:31:18 +00:00
|
|
|
|
{
|
|
|
|
|
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. */
|
|
|
|
|
*(void**)retval =
|
2002-02-20 05:08:46 +00:00
|
|
|
|
NSZoneCalloc(NSDefaultMallocZone(), retLength, 1);
|
2001-09-19 21:31:18 +00:00
|
|
|
|
/* We are responsible for making sure this memory gets free'd
|
|
|
|
|
eventually. Ask NSData class to autorelease it. */
|
|
|
|
|
[NSData dataWithBytesNoCopy: *(void**)retval
|
|
|
|
|
length: retLength];
|
|
|
|
|
ctxt->type = tmptype;
|
|
|
|
|
ctxt->datum = *(void**)retval;
|
|
|
|
|
/* Decode the return value into the memory we allocated. */
|
|
|
|
|
(*decoder) (ctxt);
|
|
|
|
|
}
|
2000-12-08 19:06:00 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case _C_STRUCT_B:
|
|
|
|
|
case _C_UNION_B:
|
|
|
|
|
case _C_ARY_B:
|
|
|
|
|
/* Decode the return value into the memory we allocated. */
|
2001-09-19 21:31:18 +00:00
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case _C_FLT:
|
|
|
|
|
case _C_DBL:
|
2001-09-19 21:31:18 +00:00
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case _C_VOID:
|
|
|
|
|
{
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->type = @encode(int);
|
|
|
|
|
ctxt->flags = 0;
|
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2001-09-19 21:31:18 +00:00
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
[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);
|
|
|
|
|
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->type = tmptype;
|
|
|
|
|
ctxt->datum = datum;
|
|
|
|
|
ctxt->flags = flags;
|
|
|
|
|
|
2000-12-08 19:06:00 +00:00
|
|
|
|
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.*/
|
2001-09-19 21:31:18 +00:00
|
|
|
|
tmptype++;
|
|
|
|
|
ctxt->type = tmptype;
|
|
|
|
|
|
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
/* Copy the pointed-to data back to the original
|
|
|
|
|
pointer */
|
|
|
|
|
[inv getArgument: &ptr atIndex: argnum];
|
2001-09-19 21:31:18 +00:00
|
|
|
|
memcpy(ptr, datum, objc_sizeof_type(tmptype));
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
|
|
|
|
else if (*tmptype == _C_CHARPTR
|
2001-09-19 21:31:18 +00:00
|
|
|
|
&& ((flags & _F_OUT) || !(flags & _F_IN)))
|
2000-12-08 19:06:00 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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. */
|
2001-09-19 21:31:18 +00:00
|
|
|
|
(*decoder) (ctxt);
|
2000-12-08 19:06:00 +00:00
|
|
|
|
[inv setArgument: datum atIndex: argnum];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2001-09-19 21:31:18 +00:00
|
|
|
|
ctxt->type = 0;
|
|
|
|
|
ctxt->datum = 0;
|
|
|
|
|
(*decoder) (ctxt); /* Tell it we have finished. */
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
2001-06-27 09:42:57 +00:00
|
|
|
|
|
2001-09-21 15:14:57 +00:00
|
|
|
|
if (ctxt->datToFree != 0)
|
|
|
|
|
{
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), ctxt->datToFree);
|
|
|
|
|
ctxt->datToFree = 0;
|
|
|
|
|
}
|
2001-06-27 09:42:57 +00:00
|
|
|
|
|
|
|
|
|
return;
|
2000-12-08 19:06:00 +00:00
|
|
|
|
}
|
|
|
|
|
|