Add FFI interface to NSInvocation and NSConnection

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@8286 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Adam Fedor 2000-12-08 19:06:00 +00:00
parent 24451e5cbc
commit 9433dfee02
28 changed files with 3985 additions and 730 deletions

View file

@ -1,3 +1,40 @@
2000-12-08 Adam Fedor <fedor@gnu.org>
* Set up DO and NSInvocation to use foreign-function interface
library.
* configure.in: Add check for --enable-ffcall
* Documentation/gnustep-base.texi: Update
* Documentation/install.texi: Likewise.
* Documentation/readme.texi: Likewise.
* Documentation/gsdoc/NSTimeZone.gsdoc: Update.
* Headers/gnustep/base/NSConnection.h: New forward method.
* Headers/gnustep/base/NSInvocation.h: New frame ivar.
* Source/GNUmakefile: Conditionally compile in FFI classes if
enabled.
* Source/NSConnection.m (-forwardInvocation:forProxy:): New method.
(_service_forwardForProxy:): For FFCALL don't autorelease memory,
call proper forwarding function.
* Source/NSDistantObject.m (forwardInvocation:): Implement.
(methodSignatureForSelector:): For FFCALL, forward through
invocation.
(conformsToProtocol:): Likewise.
(respondsToSelector:): Likewise.
* Source/NSInvocation.m: Convert to abstract class with subclasses
based on FFI interface.
(_get_arg, _set_arg, _arg_addr): New functions based on FFI interface.
(+allocWithZone:): Alloc proper concrete class.
(+initialize): Set proper concrete class.
(-initWithCoder:): Replace self with proper concrete class.
(initWithArgframe:selector:): Make subclassResponsibility.
(initWithMethodSignature:): Likewise.
(returnFrame:): Likewise.
Implement GSFrameInvocation
* Source/GSFFCallInvocation: New file.
* Source/GSFFIInvocation: Likewise.
* Source/cifframe.[hm]: Likewise.
* Source/callframe.[hm]: Likewise.
2000-12-07 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSGSet.m: renamed to GSSet.m for consistency, tidied.

View file

@ -31,7 +31,7 @@ include ../Version
include ../config.mak
# The documents to be generated
DOCUMENT_NAME = gnustep-base coding-standards
DOCUMENT_NAME = coding-standards
# The text documents to be generated
DOCUMENT_TEXT_NAME = \

View file

@ -114,47 +114,6 @@ site @url{http://www.gnustep.org/information}.
Documentation for individual classes is included in gsdoc (and html) format
in the gsdoc directory.
@node Configuration, Classes, Overview, Top
@chapter Configuration
Configuration is performed by running the @file{configure} program at a
shell prompt. You may want to use some of the optional arguments to the
@file{configure} program. Type @code{configure --help}
for a list of these. It is not likely that you will need to use the
@code{--prefix} option, since gstep-base will automatically install in
the directory specified by the @code{GNUSTEP_SYSTEM_ROOT} environment
variable (specified when you installed gstep-make).
The only feature you may want to use is the
@code{--enable-pass-arguments} or the @code{--enable-fake-main}
option(s). Normally, the configure script will determine if a program
can read process information directly from the system (for use by the
class NSProcessInfo) and enable or disable the fake-main hack
automatically, but if you find that configure is not doing this
correctly, you can force the fake-main hack to be used, which will
always work. Note that if the fake-main hack is enabled, you need to
include the header file GSConfig.h in you main program -- this is done
by default if you include NSObject.h or Foundation.h. If you want to
avoid the whole idea of the fake-main hack, you can use the
pass-arguments option. In this case you will be forced to call the
NSProcessInfo initializeWithArguments method to pass the program
arguments to NSProcessInfo (This would be done automatically if you use
the NSApplicationMain function in a gui application).
Most other important configuration options are already set when you
configure the gstep-make package.
@node Classes, Tools, Configuration, Top
@chapter Special Features of gstep-base Classes
@node Tools, Installation, Classes, Top
@chapter Special Tools Included with gstep-base
@node Installation, Implementation, Tools, Top
@chapter Installing GNU Objective-C Class Library
Type @code{make install} to install the libraries and programs
@node Implementation, Concept Index, Installation, Top
@chapter Implementation Details
@ -241,42 +200,5 @@ so you don't need to worry about releasing it yourself. However, if you wish
to store the object for any length of time, you will need to send it a retain
message, and then send it a release when you have finished with it.
@node Garbage Collection, Time Zone, Reference Counting, Implementation
@section Garbage Collection
@node Time Zone, , Garbage Collection, Implementation
@section Time Zones
If the GNUstep time zone datafiles become too out of date, one can
download an updated database from @url{ftp://elsie.nci.nih.gov/pub/} and
compile it as specified in the README file in the NSTimeZones
directory. (In fact, I believe it's about time they're updated.)
@example
Time zone names in NSDates should be GMT, MET etc. not
Europe/Berlin, America/Washington etc.
@end example
The problem with this is that various time zones may use the same
abbreviation (e.g. Australia/@{Brisbane,...@} and America/@{New_York,...@}
both use EST), and some time zones may have different rules for
daylight saving time even if the abbreviation and offsets from UTC are
the same.
The problems with depending on the OS for providing time zone info are
that some methods for the NSTimeZone classes might be difficult to
implement, and also that time zone names may vary wildly between OSes
(this could be a big problem when archiving is used between different
systems).
@node Concept Index, , Implementation, Top
@unnumbered Concept Index
@printindex cp
@summarycontents
@contents
@bye

View file

@ -18,6 +18,29 @@
<conform>NSCoding</conform>
<desc>
</desc>
<p>
If the GNUstep time zone datafiles become too out of date, one
can download an updated database from <uref
url="ftp://elsie.nci.nih.gov/pub/">ftp://elsie.nci.nih.gov/pub/</uref>
and compile it as specified in the README file in the
NSTimeZones directory.
Time zone names in NSDates should be GMT, MET etc. not
Europe/Berlin, America/Washington etc.
The problem with this is that various time zones may use the
same abbreviation (e.g. Australia/Brisbane and
America/New_York both use EST), and some time zones
may have different rules for daylight saving time even if the
abbreviation and offsets from UTC are the same.
The problems with depending on the OS for providing time zone
info are that some methods for the NSTimeZone classes might be
difficult to implement, and also that time zone names may vary
wildly between OSes (this could be a big problem when
archiving is used between different systems).
</p>
<method type="NSDictionary*" factory="yes">
<sel>abbreviationDictionary</sel>
<desc>

View file

@ -5,17 +5,129 @@
@c set the vars GNUSTEP_BASE_VERSION and GNUSTEP_BASE_GCC_VERSION
@include version.texi
@ifset TEXT_ONLY
This file documents the installation of the GNUstep Base Library,
@samp{libgnustep-base}. Copyright (C) 1993, 1994, 1996, 1997 Free Software
Foundation, Inc. You may copy, distribute, and modify it freely as long
as you preserve this copyright notice and permission notice.
@chapter Installing @samp{libgnustep-base}
@end ifset
@node Top, Introduction, (dir), (dir)
@menu
* Introduction::
* Configuration::
* Reading Command-Line Arguments::
* FFI Library::
* Compilation::
@end menu
@node Introduction, Configuration, Top, Top
@section Introduction
If you are installing this package as part of the GNUstep core
libraries, read the file GNUstep-HOWTO for more complete instructions on
how to install the entire GNUstep package (including this library).
GNUstep-HOWTO comes with the core distribution and also is located at the
same ftp sites as this library.
If you are instsalling the GNUstep libraries individually, make sure you
have installed the GNUstep Makefile package (gstep-make) already. Most of
the installation instructions for gstep-make also apply to this
library. When you configure this library, make sure you use the same
configuration options as with gstep-make. Some additional options to
configure are described below.
@node Configuration, Reading Command-Line Arguments, Introduction, Top
@section Configuration
Configuration is performed by running the @file{configure} program at a
shell prompt. You may want to use some of the optional arguments to the
@file{configure} program. Type @code{configure --help}
for a list of these. It is not likely that you will need to use the
@code{--prefix} option, since gstep-base will automatically install in
the directory specified by the @code{GNUSTEP_SYSTEM_ROOT} environment
variable (specified when you installed gstep-make).
Many configuration options for the base library need to be specified
when configuring the gstep-make package. In most cases, it is a good
idea to specify the same configuration options when configuring both
packages, just to make sure.
Also make sure you've read the machine-specific instructions for your
particular operating system and CPU. These instructions come with the
core libraries and are also located at the GNUstep web site at
@url{http://www.gnustep.org}.
@menu
* Reading Command-Line Arguments::
* FFI Library::
@end menu
@node Reading Command-Line Arguments, FFI Library, Configuration, Top
@section Reading Command-Line Arguments
There are two features that change how GNUstep gets access to
command-line arguments and environment variables (normally passed to
the program in the @code{main()} function. These features are
@code{--enable-pass-arguments} and the @code{--enable-fake-main}
option(s).
The fake-main option @emph{secretly} renames the @code{main()} function
and substitutes it's own function which stores the command line
arguments before calling the real main.
The pass-arguments option absolves GNUstep of any effort to get the
command-line arguments and instead forces the developer to insert the
line
@example
[NSProcessInfo initializeWithArguments:argv count:argc environment:env];
@end example
in the @code{main()} function of their program.
Normally, the configure script will determine if a program
can read process information directly from the system (for use by the
class NSProcessInfo) and enable or disable the fake-main hack
automatically, but if you find that configure is not doing this
correctly, you can force the fake-main hack to be used, which will
always work. Note that if the fake-main hack is enabled, you need to
include the header file GSConfig.h in you main program -- this is done
by default if you include NSObject.h or Foundation.h. If you want to
avoid the whole idea of the fake-main hack, you can use the
pass-arguments option. In this case you will be forced to call the
NSProcessInfo initializeWithArguments method to pass the program
arguments to NSProcessInfo (This would be done automatically if you use
the NSApplicationMain function in a gui application).
@node FFI Library, Compilation, Reading Command-Line Arguments, Top
@section FFI Library
GNUstep's NSInvocations and Distributed Objects code involves detailed
manipulation of function (method) calls, which does not work on all
machine architectures. A more robust and portable solution is to use a
library that implements a Foreign-Function Interface (FFI), such as the
ffcall libraries. If you use a non-x86 machine, you should seriously
consider enabling this option using @code{--enable-ffcall}.
For this to work, you need to have the ffcall libraries, which you can
get from @url{ftp://ftp.santafe.edu/pub/gnu/} or
@url{http://clisp.cons.org/~haible/}. You also need to have a special
version of the Objective-C library (as of gcc 2.95.x the required hooks
are not in the standard library). You can get this library from
@url{ftp://www.gnustep.org/pub/gnustep/contrib/libobjc-hh.tar.gz} or
patch the standard library with the patch at
@url{ftp://www.gnustep.org/pub/gnustep/contrib/libobjc-ffi.patch}.
Make sure you specify this option when configuring gstep-make also.
@node Compilation, , FFI Library, Top
@section Compilation
To compile this library, type make. After this is complete, type make
install (make sure you are the root user). Some additional options you
can use with make are @samp{debug=yes} to make a debugging version of
the library and @samp{shared=no} to make a static version of the
library. See the gstep-make package for more information on these options.
Read the file GNUstep-HOWTO for instructions on how to install the
entire GNUstep package (including the base library).
GNUstep-HOWTO comes with this distribution and also
is located at the same ftp sites as this library.
@bye

View file

@ -11,8 +11,8 @@ Here is some introductory info to get you started:
The file @samp{NEWS} has the library's feature history.
The file @samp{GNUstep-HOWTO}
give instructions for installing the library.
The files @samp{INSTALL} or @samp{GNUstep-HOWTO} (from the core package)
gives instructions for installing the library.
@section How can you help?

View file

@ -0,0 +1,51 @@
/* Interface for NSInvocation concrete classes for GNUStep
Copyright (C) 1998 Free Software Foundation, Inc.
Written: Adam Fedor <fedor@gnu.org>
Date: 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.
*/
#ifndef __GSInvocation_h_GNUSTEP_BASE_INCLUDE
#define __GSInvocation_h_GNUSTEP_BASE_INCLUDE
#include <Foundation/NSInvocation.h>
@interface GSFFIInvocation : NSInvocation
{
}
@end
@interface GSFFCallInvocation : NSInvocation
{
}
@end
@interface GSFrameInvocation : NSInvocation
{
}
@end
@interface NSInvocation (DistantCoding)
- (BOOL) encodeWithDistantCoder: (NSCoder*)coder passPointers: (BOOL)passp;
@end
extern void
GSFFCallInvokeWithTargetAndImp(NSInvocation *inv, id anObject, IMP imp);
#endif

View file

@ -36,6 +36,7 @@
@class NSPort;
@class NSPortNameServer;
@class NSData;
@class NSInvocation;
/*
* Keys for the NSDictionary returned by [NSConnection -statistics]
@ -159,6 +160,8 @@ GS_EXPORT NSString *NSConnectionProxyCount; /* Objects received */
- (retval_t) forwardForProxy: (NSDistantObject*)object
selector: (SEL)sel
argFrame: (arglist_t)frame;
- (void) forwardInvocation: (NSInvocation *)inv
forProxy: (NSDistantObject*)object;
- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target;
@end

View file

@ -31,6 +31,7 @@
{
NSMethodSignature *_sig;
arglist_t _argframe;
void *_cframe;
void *_retval;
id _target;
SEL _selector;

View file

@ -48,6 +48,12 @@
/* Define if your system has a /proc/self/exe symlink to the executable */
#undef HAVE_PROC_FS_EXE_LINK
/* Define if using the libffi library for invocations */
#undef USE_LIBFFI
/* Define if using the ffcall library for invocations */
#undef USE_FFCALL
/* The number of bytes in a double. */
#undef SIZEOF_DOUBLE
@ -135,12 +141,18 @@
/* Define if you have the vsprintf function. */
#undef HAVE_VSPRINTF
/* Define if you have the <callback> header file. */
#undef HAVE_CALLBACK
/* Define if you have the <dirent.h> header file. */
#undef HAVE_DIRENT_H
/* Define if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H
/* Define if you have the <gmp.h> header file. */
#undef HAVE_GMP_H
/* Define if you have the <grp.h> header file. */
#undef HAVE_GRP_H

104
INSTALL
View file

@ -1,5 +1,101 @@
Read the file GNUstep-HOWTO for instructions on how to install the
entire GNUstep package (including the base library). GNUstep-HOWTO
comes with this distribution and also is located at the same ftp sites
as this library.
Introduction
============
If you are installing this package as part of the GNUstep core
libraries, read the file GNUstep-HOWTO for more complete instructions on
how to install the entire GNUstep package (including this library).
GNUstep-HOWTO comes with the core distribution and also is located at
the same ftp sites as this library.
If you are instsalling the GNUstep libraries individually, make sure
you have installed the GNUstep Makefile package (gstep-make) already.
Most of the installation instructions for gstep-make also apply to this
library. When you configure this library, make sure you use the same
configuration options as with gstep-make. Some additional options to
configure are described below.
Configuration
=============
Configuration is performed by running the `configure' program at a
shell prompt. You may want to use some of the optional arguments to the
`configure' program. Type `configure --help' for a list of these. It is
not likely that you will need to use the `--prefix' option, since
gstep-base will automatically install in the directory specified by the
`GNUSTEP_SYSTEM_ROOT' environment variable (specified when you
installed gstep-make).
Many configuration options for the base library need to be specified
when configuring the gstep-make package. In most cases, it is a good
idea to specify the same configuration options when configuring both
packages, just to make sure.
Also make sure you've read the machine-specific instructions for your
particular operating system and CPU. These instructions come with the
core libraries and are also located at the GNUstep web site at
<http://www.gnustep.org>.
Reading Command-Line Arguments
==============================
There are two features that change how GNUstep gets access to
command-line arguments and environment variables (normally passed to
the program in the `main()' function. These features are
`--enable-pass-arguments' and the `--enable-fake-main' option(s).
The fake-main option _secretly_ renames the `main()' function and
substitutes it's own function which stores the command line arguments
before calling the real main.
The pass-arguments option absolves GNUstep of any effort to get the
command-line arguments and instead forces the developer to insert the
line
[NSProcessInfo initializeWithArguments:argv count:argc environment:env];
in the `main()' function of their program.
Normally, the configure script will determine if a program can read
process information directly from the system (for use by the class
NSProcessInfo) and enable or disable the fake-main hack automatically,
but if you find that configure is not doing this correctly, you can
force the fake-main hack to be used, which will always work. Note that
if the fake-main hack is enabled, you need to include the header file
GSConfig.h in you main program - this is done by default if you include
NSObject.h or Foundation.h. If you want to avoid the whole idea of the
fake-main hack, you can use the pass-arguments option. In this case you
will be forced to call the NSProcessInfo initializeWithArguments method
to pass the program arguments to NSProcessInfo (This would be done
automatically if you use the NSApplicationMain function in a gui
application).
FFI Library
===========
GNUstep's NSInvocations and Distributed Objects code involves
detailed manipulation of function (method) calls, which does not work
on all machine architectures. A more robust and portable solution is to
use a library that implements a Foreign-Function Interface (FFI), such
as the ffcall libraries. If you use a non-x86 machine, you should
seriously consider enabling this option using `--enable-ffcall'.
For this to work, you need to have the ffcall libraries, which you
can get from <ftp://ftp.santafe.edu/pub/gnu/> or
<http://clisp.cons.org/~haible/>. You also need to have a special
version of the Objective-C library (as of gcc 2.95.x the required hooks
are not in the standard library). You can get this library from
<ftp://www.gnustep.org/pub/gnustep/contrib/libobjc-hh.tar.gz> or patch
the standard library with the patch at
<ftp://www.gnustep.org/pub/gnustep/contrib/libobjc-ffi.patch>.
Make sure you specify this option when configuring gstep-make also.
Compilation
===========
To compile this library, type make. After this is complete, type make
install (make sure you are the root user). Some additional options you
can use with make are `debug=yes' to make a debugging version of the
library and `shared=no' to make a static version of the library. See
the gstep-make package for more information on these options.

4
README
View file

@ -18,8 +18,8 @@ Initial reading
The file `NEWS' has the library's feature history.
The file `GNUstep-HOWTO' give instructions for installing the
library.
The files `INSTALL' or `GNUstep-HOWTO' (from the core package) gives
instructions for installing the library.
How can you help?
=================

View file

@ -234,6 +234,15 @@ ifeq ($(HAVE_LIBXML),1)
BASE_MFILES += GSXML.m
endif
ifeq ($(WITH_FFI),libffi)
GNU_MFILES += cifframe.m
BASE_MFILES += GSFFIInvocation.m
endif
ifeq ($(WITH_FFI),ffcall)
GNU_MFILES += callframe.m
BASE_MFILES += GSFFCallInvocation.m
endif
NSVALUE_MFILES = \
NSValue0.m NSValue1.m NSValue2.m NSValue3.m \
NSValue4.m

697
Source/GSFFCallInvocation.m Normal file
View file

@ -0,0 +1,697 @@
/* Implementation of GSFFCallInvocation for GNUStep
Copyright (C) 2000 Free Software Foundation, Inc.
Written: Adam Fedor <fedor@gnu.org>
Date: 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.
*/
#ifdef __powerpc__
#define __rs6000__
#define _AIX
#endif
#include <Foundation/NSException.h>
#include <Foundation/NSCoder.h>
#include <Foundation/NSDistantObject.h>
#include <base/GSInvocation.h>
#include <config.h>
#include <objc/objc-api.h>
#include <avcall.h>
#include <callback.h>
#include "callframe.h"
typedef struct _NSInvocation_t {
@defs(NSInvocation)
} NSInvocation_t;
void
GSInvocationCallback(void *callback_data, va_alist args);
/* Callback for forwarding methods */
static void *ff_callback;
/* Callback data (which will hold the selector) */
static SEL callback_sel;
/* Recursively calculate the offset using the offset of the previous
sub-type */
static int
gs_offset(const char *type, int index)
{
int offset;
const char *subtype;
if (index == 0)
return 0;
subtype = type;
while (*subtype != _C_STRUCT_E && *subtype++ != '='); /* skip "<name>=" */
offset = (gs_offset(type, index-1) + objc_sizeof_type(&subtype[index-1])
+ objc_alignof_type(&subtype[index]) - 1)
& -(long)objc_alignof_type(&subtype[index]);
return offset;
}
/* Determines if the structure type can be returned entirely in registers.
See the avcall or vacall man pages for more info. FIXME: I'm betting
this won't work if a structure contains another structure */
int
gs_splittable(const char *type)
{
int i, numtypes;
const char *subtype;
int result;
subtype = type;
while (*subtype != _C_STRUCT_E && *subtype++ != '='); /* skip "<name>=" */
numtypes = 0;
while (*subtype != _C_STRUCT_E)
{
numtypes++;
subtype = objc_skip_typespec (subtype);
}
subtype = type;
while (*subtype != _C_STRUCT_E && *subtype++ != '='); /* skip "<name>=" */
result = 1;
for (i = 0; i < numtypes; i++)
{
result = result
&& (gs_offset(type, i)/sizeof(__avword)
== (gs_offset(type, i)+objc_sizeof_type(&subtype[i])-1)
/ sizeof(__avword));
}
//printf("Splittable for %s is %d\n", type, result);
return result;
}
@implementation GSFFCallInvocation
static IMP gs_objc_msg_lookup (id self, SEL _cmd)
{
callback_sel = _cmd;
return ff_callback;
}
static IMP gs_class_get_method (Class class, SEL _cmd)
{
callback_sel = _cmd;
return ff_callback;
}
static void gs_free_callback(void)
{
if (ff_callback)
{
free_callback(ff_callback);
ff_callback = NULL;
}
}
+ (void)load
{
ff_callback = alloc_callback(&GSInvocationCallback, &callback_sel);
__objc_msg_lookup = gs_objc_msg_lookup;
__objc_class_get_method = gs_class_get_method;
}
- (id) initWithArgframe: (arglist_t)frame selector: (SEL)aSelector
{
/* We should never get here */
NSDeallocateObject(self);
[NSException raise: NSInternalInconsistencyException
format: @"Runtime incorrectly configured to pass argframes"];
return nil;
}
/*
* This is the de-signated initialiser.
*/
- (id) initWithMethodSignature: (NSMethodSignature*)aSignature
{
_sig = RETAIN(aSignature);
_numArgs = [aSignature numberOfArguments];
_info = [aSignature methodInfo];
_cframe = callframe_from_info(_info, _numArgs, &_retval);
if (_retval == 0 && _info[0].size > 0)
{
_retval = NSZoneMalloc(NSDefaultMallocZone(), _info[0].size);
}
return self;
}
/* This is implemented as a function so it can be used by other
routines (like the DO forwarding)
*/
void
GSFFCallInvokeWithTargetAndImp(NSInvocation_t *inv, id anObject, IMP imp)
{
int i;
av_alist alist;
/* Do an av call starting with the return type */
#undef CASE_TYPE
#define CASE_TYPE(_T, _V, _F) \
case _T: \
_F(alist, imp, inv->_retval); \
break;
switch (*inv->_info[0].type)
{
case _C_ID:
av_start_ptr(alist, imp, id, inv->_retval);
break;
case _C_CLASS:
av_start_ptr(alist, imp, Class, inv->_retval);
break;
case _C_SEL:
av_start_ptr(alist, imp, SEL, inv->_retval);
break;
case _C_PTR:
av_start_ptr(alist, imp, void *, inv->_retval);
break;
case _C_CHARPTR:
av_start_ptr(alist, imp, char *, inv->_retval);
break;
CASE_TYPE(_C_CHR, char, av_start_char)
CASE_TYPE(_C_UCHR, unsigned char, av_start_uchar)
CASE_TYPE(_C_SHT, short, av_start_short)
CASE_TYPE(_C_USHT, unsigned short, av_start_ushort)
CASE_TYPE(_C_INT, int, av_start_int)
CASE_TYPE(_C_UINT, unsigned int, av_start_uint)
CASE_TYPE(_C_LNG, long, av_start_long)
CASE_TYPE(_C_ULNG, unsigned long, av_start_ulong)
CASE_TYPE(_C_LNG_LNG, long long, av_start_longlong)
CASE_TYPE(_C_ULNG_LNG, unsigned long long, av_start_ulonglong)
CASE_TYPE(_C_FLT, float, av_start_float)
CASE_TYPE(_C_DBL, double, av_start_double)
case _C_STRUCT_B:
{
int split = 0;
if (inv->_info[0].size > sizeof(long) && inv->_info[0].size <= 2*sizeof(long))
split = gs_splittable(inv->_info[0].type);
_av_start_struct(alist, imp, inv->_info[0].size, split, inv->_retval);
break;
}
case _C_VOID:
av_start_void(alist, imp);
break;
default:
NSCAssert1(0, @"GSFFCallInvocation: Return Type '%s' not implemented",
inv->_info[0].type);
break;
}
/* Set target and selector */
av_ptr(alist, id, anObject);
av_ptr(alist, SEL, inv->_selector);
/* Set the rest of the arguments */
for (i = 2; i < inv->_numArgs; i++)
{
const char *type = inv->_info[i+1].type;
unsigned size = inv->_info[i+1].size;
void *datum;
datum = callframe_arg_addr((callframe_t *)inv->_cframe, i);
#undef CASE_TYPE
#define CASE_TYPE(_T, _V, _F) \
case _T: \
{ \
_V c; \
memcpy(&c, datum, size); \
_F(alist, c); \
break; \
}
switch (*type)
{
case _C_ID:
{
id obj;
memcpy(&obj, datum, size);
av_ptr(alist, id, obj);
break;
}
case _C_CLASS:
{
Class obj;
memcpy(&obj, datum, size);
av_ptr(alist, Class, obj);
break;
}
case _C_SEL:
{
SEL sel;
memcpy(&sel, datum, size);
av_ptr(alist, SEL, sel);
break;
}
case _C_PTR:
{
void *ptr;
memcpy(&ptr, datum, size);
av_ptr(alist, void *, ptr);
break;
}
case _C_CHARPTR:
{
char *ptr;
memcpy(&ptr, datum, size);
av_ptr(alist, char *, ptr);
break;
}
CASE_TYPE(_C_CHR, char, av_char)
CASE_TYPE(_C_UCHR, unsigned char, av_uchar)
CASE_TYPE(_C_SHT, short, av_short)
CASE_TYPE(_C_USHT, unsigned short, av_ushort)
CASE_TYPE(_C_INT, int, av_int)
CASE_TYPE(_C_UINT, unsigned int, av_uint)
CASE_TYPE(_C_LNG, long, av_long)
CASE_TYPE(_C_ULNG, unsigned long, av_ulong)
CASE_TYPE(_C_LNG_LNG, long long, av_longlong)
CASE_TYPE(_C_ULNG_LNG, unsigned long long, av_ulonglong)
CASE_TYPE(_C_FLT, float, av_float)
CASE_TYPE(_C_DBL, double, av_double)
case _C_STRUCT_B:
_av_struct(alist, size, inv->_info[i+1].align, datum);
break;
default:
NSCAssert1(0, @"GSFFCallInvocation: Type '%s' not implemented", type);
break;
}
}
/* Do it */
av_call(alist);
}
- (void) invokeWithTarget: (id)anObject
{
id old_target;
IMP imp;
/*
* A message to a nil object returns nil.
*/
if (anObject == nil)
{
memset(_retval, '\0', _info[0].size); /* Clear return value */
return;
}
NSAssert(_selector != 0, @"you must set the selector before invoking");
/*
* Temporarily set new target and copy it (and the selector) into the
* _argframe.
*/
old_target = RETAIN(_target);
[self setTarget: anObject];
callframe_set_arg((callframe_t *)_cframe, 0, &_target, _info[1].size);
callframe_set_arg((callframe_t *)_cframe, 1, &_selector, _info[2].size);
imp = method_get_imp(object_is_instance(_target) ?
class_get_instance_method(
((struct objc_class*)_target)->class_pointer, _selector)
: class_get_class_method(
((struct objc_class*)_target)->class_pointer, _selector));
/*
* If fast lookup failed, we may be forwarding or something ...
*/
if (imp == 0)
imp = objc_msg_lookup(_target, _selector);
[self setTarget: old_target];
RELEASE(old_target);
GSFFCallInvokeWithTargetAndImp(self, anObject, imp);
_validReturn = YES;
}
- (void*) returnFrame: (arglist_t)argFrame
{
return _retval;
}
@end
void GSInvocationCallback(void *callback_data, va_alist args)
{
id obj;
SEL callback_sel, selector;
int i, num_args;
void *retval;
const char *callback_type;
NSArgumentInfo *info;
GSFFCallInvocation *invocation;
NSMethodSignature *sig;
callback_sel = *(SEL *)callback_data;
callback_type = sel_get_type(callback_sel);
callback_type = objc_skip_type_qualifiers(callback_type);
#undef CASE_TYPE
#define CASE_TYPE(_T, _V, _F) \
case _T: \
_F(args); \
break;
switch (*callback_type)
{
case _C_ID:
va_start_ptr(args, id);
break;
case _C_CLASS:
va_start_ptr(args, Class);
break;
case _C_SEL:
va_start_ptr(args, SEL);
break;
case _C_PTR:
va_start_ptr(args, void *);
break;
case _C_CHARPTR:
va_start_ptr(args, char *);
break;
CASE_TYPE(_C_CHR, char, va_start_char)
CASE_TYPE(_C_UCHR, unsigned char, va_start_uchar)
CASE_TYPE(_C_SHT, short, va_start_short)
CASE_TYPE(_C_USHT, unsigned short, va_start_ushort)
CASE_TYPE(_C_INT, int, va_start_int)
CASE_TYPE(_C_UINT, unsigned int, va_start_uint)
CASE_TYPE(_C_LNG, long, va_start_long)
CASE_TYPE(_C_ULNG, unsigned long, va_start_ulong)
CASE_TYPE(_C_LNG_LNG, long long, va_start_longlong)
CASE_TYPE(_C_ULNG_LNG, unsigned long long, va_start_ulonglong)
CASE_TYPE(_C_FLT, float, va_start_float)
CASE_TYPE(_C_DBL, double, va_start_double)
case _C_STRUCT_B:
{
int split, ssize;
ssize = objc_sizeof_type(callback_type);
if (ssize > sizeof(long) && ssize <= 2*sizeof(long))
split = gs_splittable(callback_type);
_va_start_struct(args, ssize, objc_alignof_type(callback_type), split);
break;
}
case _C_VOID:
va_start_void(args);
break;
default:
NSCAssert1(0, @"GSFFCallInvocation: Return Type '%s' not implemented",
callback_type);
break;
}
obj = va_arg_ptr(args, id);
selector = va_arg_ptr(args, SEL);
/* Invoking a NSDistantObject method is likely to cause infinite recursion.
So make sure we really can't find the selector locally .*/
sig = nil;
if ([obj isKindOfClass: [NSDistantObject class]])
{
const char *type = sel_get_type(selector);
if (type)
sig = [NSMethodSignature signatureWithObjCTypes: type];
}
if (!sig)
{
NSLog(@"looking up sel %@", NSStringFromSelector(selector));
sig = [obj methodSignatureForSelector: selector];
}
NSCAssert1(sig, @"No signature for selector %@",
NSStringFromSelector(selector));
invocation = [[GSFFCallInvocation alloc] initWithMethodSignature: sig];
AUTORELEASE(invocation);
[invocation setTarget: obj];
[invocation setSelector: selector];
/* Set the rest of the arguments */
num_args = [sig numberOfArguments];
info = [sig methodInfo];
for (i = 2; i < num_args; i++)
{
const char *type = info[i+1].type;
unsigned size = info[i+1].size;
#undef CASE_TYPE
#define CASE_TYPE(_T, _V, _F) \
case _T: \
{ \
_V c = _F(args); \
[invocation setArgument: &c atIndex: i]; \
break; \
}
switch (*type)
{
case _C_ID:
{
id obj = va_arg_ptr (args, id);
[invocation setArgument: &obj atIndex: i];
break;
}
case _C_CLASS:
{
Class obj = va_arg_ptr (args, Class);
[invocation setArgument: &obj atIndex: i];
break;
}
case _C_SEL:
{
SEL sel = va_arg_ptr (args, SEL);
[invocation setArgument: &sel atIndex: i];
break;
}
case _C_PTR:
{
void *ptr = va_arg_ptr (args, void *);
[invocation setArgument: &ptr atIndex: i];
break;
}
case _C_CHARPTR:
{
char *ptr = va_arg_ptr (args, char *);
[invocation setArgument: &ptr atIndex: i];
break;
}
CASE_TYPE(_C_CHR, char, va_arg_char)
CASE_TYPE(_C_UCHR, unsigned char, va_arg_uchar)
CASE_TYPE(_C_SHT, short, va_arg_short)
CASE_TYPE(_C_USHT, unsigned short, va_arg_ushort)
CASE_TYPE(_C_INT, int, va_arg_int)
CASE_TYPE(_C_UINT, unsigned int, va_arg_uint)
CASE_TYPE(_C_LNG, long, va_arg_long)
CASE_TYPE(_C_ULNG, unsigned long, va_arg_ulong)
CASE_TYPE(_C_LNG_LNG, long long, va_arg_longlong)
CASE_TYPE(_C_ULNG_LNG, unsigned long long, va_arg_ulonglong)
CASE_TYPE(_C_FLT, float, va_arg_float)
CASE_TYPE(_C_DBL, double, va_arg_double)
case _C_STRUCT_B:
{
/* Here we actually get a ptr to the struct */
void *ptr = _va_arg_struct(args, size, info[i+1].align);
[invocation setArgument: ptr atIndex: i];
break;
}
default:
NSCAssert1(0, @"GSFFCallInvocation: Type '%s' not implemented", type);
break;
}
}
/* Now do it */
[obj forwardInvocation: invocation];
/* Return the proper type */
retval = [invocation returnFrame: NULL];
#undef CASE_TYPE
#define CASE_TYPE(_T, _V, _F) \
case _T: \
_F(args, *(_V *)retval); \
break;
switch (*info[0].type)
{
case _C_ID:
va_return_ptr(args, id, *(id *)retval);
break;
case _C_CLASS:
va_return_ptr(args, Class, *(Class *)retval);
break;
case _C_SEL:
va_return_ptr(args, SEL, *(SEL *)retval);
break;
case _C_PTR:
va_return_ptr(args, void *, *(void **)retval);
break;
case _C_CHARPTR:
va_return_ptr(args, char *, *(char **)retval);
break;
CASE_TYPE(_C_CHR, char, va_return_char)
CASE_TYPE(_C_UCHR, unsigned char, va_return_uchar)
CASE_TYPE(_C_SHT, short, va_return_short)
CASE_TYPE(_C_USHT, unsigned short, va_return_ushort)
CASE_TYPE(_C_INT, int, va_return_int)
CASE_TYPE(_C_UINT, unsigned int, va_return_uint)
CASE_TYPE(_C_LNG, long, va_return_long)
CASE_TYPE(_C_ULNG, unsigned long, va_return_ulong)
CASE_TYPE(_C_LNG_LNG, long long, va_return_longlong)
CASE_TYPE(_C_ULNG_LNG, unsigned long long, va_return_ulonglong)
CASE_TYPE(_C_FLT, float, va_return_float)
CASE_TYPE(_C_DBL, double, va_return_double)
case _C_STRUCT_B:
_va_return_struct(args, info[0].size, info[0].align, retval);
break;
case _C_VOID:
va_return_void(args);
break;
default:
NSCAssert1(0, @"GSFFCallInvocation: Return Type '%s' not implemented", info[0].type);
break;
}
}
@implementation NSInvocation (DistantCoding)
/* An internal method used to help NSConnections code invocations
to send over the wire */
- (BOOL) encodeWithDistantCoder: (NSCoder*)coder passPointers: (BOOL)passp
{
int i;
BOOL out_parameters = NO;
[coder encodeValueOfObjCType: @encode(char*) at: &_info[0].type];
for (i = 0; i < _numArgs; i++)
{
int flags = _info[i+1].qual;
const char *type = _info[i+1].type;
void *datum;
if (i == 0)
datum = &_target;
else if (i == 1)
datum = &_selector;
else
datum = callframe_arg_addr((callframe_t *)_cframe, i);
/* Decide how, (or whether or not), to encode the argument
depending on its FLAGS and TYPE. 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. */
switch (*type)
{
case _C_ID:
if (flags & _F_BYCOPY)
[coder encodeBycopyObject: *(id*)datum];
#ifdef _F_BYREF
else if (flags & _F_BYREF)
[coder encodeByrefObject: *(id*)datum];
#endif
else
[coder encodeObject: *(id*)datum];
break;
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 encode
it. */
if ((flags & _F_IN) || !(flags & _F_OUT))
[coder encodeValueOfObjCType: type at: datum];
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;
if (passp)
{
if ((flags & _F_IN) || !(flags & _F_OUT))
[coder encodeValueOfObjCType: type at: datum];
}
else
{
/* 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. */
type++;
/* If the pointer's value is qualified as an IN parameter,
or not explicity qualified as an OUT parameter, then
encode it. */
if ((flags & _F_IN) || !(flags & _F_OUT))
[coder encodeValueOfObjCType: type at: *(void**)datum];
}
break;
case _C_STRUCT_B:
case _C_UNION_B:
case _C_ARY_B:
/* Handle struct and array arguments. */
/* Whether DATUM points to the data, or points to a pointer
that points to the data, depends on the value of
CALLFRAME_STRUCT_BYREF. Do the right thing
so that ENCODER gets a pointer to directly to the data. */
[coder encodeValueOfObjCType: type at: datum];
break;
default:
/* Handle arguments of all other types. */
[coder encodeValueOfObjCType: type at: datum];
}
}
/* Return a BOOL indicating whether or not there are parameters that
were passed by reference; we will need to get those values again
after the method has finished executing because the execution of
the method may have changed them.*/
return out_parameters;
}

101
Source/GSFFIInvocation.m Normal file
View file

@ -0,0 +1,101 @@
/* Implementation of GSFFIInvocation for GNUStep
Copyright (C) 2000 Free Software Foundation, Inc.
Written: Adam Fedor <fedor@gnu.org>
Date: 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 <Foundation/NSException.h>
#include <Foundation/NSCoder.h>
#include <base/GSInvocation.h>
#include <config.h>
#include <mframe.h>
#include "cifframe.h"
/* The FFI library doesn't have closures (well it does, but only for ix86), so
we still use a lot of the argframe (mframe) functions for things like
forwarding
*/
@implementation GSFFIInvocation
- (id) initWithArgframe: (arglist_t)frame selector: (SEL)aSelector
{
const char *types;
NSMethodSignature *newSig;
types = sel_get_type(aSelector);
if (types == 0)
{
types = sel_get_type(sel_get_any_typed_uid(sel_get_name(aSelector)));
}
if (types == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"Couldn't find encoding type for selector %s.",
sel_get_name(aSelector)];
}
newSig = [NSMethodSignature signatureWithObjCTypes: types];
self = [self initWithMethodSignature: newSig];
if (self)
{
[self setSelector: aSelector];
/*
* Copy the _argframe we were given.
*/
if (frame)
{
int i;
mframe_get_arg(frame, &_info[1], &_target);
for (i = 1; i <= _numArgs; i++)
{
mframe_get_arg(frame, &_info[i],
((cifframe_t *)_cframe)->values[i-1]);
}
}
}
return self;
}
/*
* This is the de_signated initialiser.
*/
- (id) initWithMethodSignature: (NSMethodSignature*)aSignature
{
_sig = RETAIN(aSignature);
_numArgs = [aSignature numberOfArguments];
_info = [aSignature methodInfo];
_argframe = mframe_create_argframe([_sig methodType], &_retval);
_cframe = cifframe_from_sig([_sig methodType], &_retval);
if (_retval == 0 && _info[0].size > 0)
{
_retval = NSZoneMalloc(NSDefaultMallocZone(), _info[0].size);
}
return self;
}
- (void*) returnFrame: (arglist_t)argFrame
{
return mframe_handle_return(_info[0].type, _retval, argFrame);
}
@end

View file

@ -28,6 +28,11 @@
#include <config.h>
#include <base/preface.h>
#include <mframe.h>
#if defined(USE_LIBFFI)
#include "cifframe.h"
#elif defined(USE_FFCALL)
#include "callframe.h"
#endif
/*
* Setup for inline operation of pointer map tables.
@ -65,6 +70,7 @@
#include <Foundation/NSPortNameServer.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSDebug.h>
#include <base/GSInvocation.h>
#define F_LOCK(X) {NSDebugFLLog(@"GSConnection",@"Lock %@",X);[X lock];}
#define F_UNLOCK(X) {NSDebugFLLog(@"GSConnection",@"Unlock %@",X);[X unlock];}
@ -1560,6 +1566,142 @@ static BOOL multi_threaded = NO;
return retframe;
}
/*
* NSDistantObject's -forwardInvocation: method calls this to send the message
* over the wire.
*/
- (void) forwardInvocation: (NSInvocation *)inv
forProxy: (NSDistantObject*)object
{
NSPortCoder *op;
BOOL outParams;
BOOL needsResponse;
const char *type;
int seq_num;
/* Encode the method on an RMC, and send it. */
NSParameterAssert (_isValid);
/* get the method types from the selector */
type = [[inv methodSignature] methodType];
if (type == 0 || *type == '\0')
{
type = [[object methodSignatureForSelector: [inv selector]] methodType];
if (type)
{
sel_register_typed_name(sel_get_name([inv selector]), type);
}
}
NSParameterAssert(type);
NSParameterAssert(*type);
op = [self _makeOutRmc: 0 generate: &seq_num reply: YES];
if (debug_connection > 4)
NSLog(@"building packet seq %d", seq_num);
outParams = [inv encodeWithDistantCoder: op passPointers: YES];
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: op type: METHOD_REQUEST];
NSDebugMLLog(@"NSConnection", @"Sent message to 0x%x", (gsaddr)self);
if (needsResponse == NO)
{
/*
* Since we don't need a response, we can remove the placeholder from
* the _replyMap.
*/
M_LOCK(_refGate);
GSIMapRemoveKey(_replyMap, (GSIMapKey)seq_num);
M_UNLOCK(_refGate);
}
else
{
NSPortCoder *ip = nil;
BOOL is_exception = NO;
void decoder(int argnum, void *datum, const char *type, int flags)
{
if (type == 0)
{
if (ip != nil)
{
DESTROY(ip);
/* this must be here to avoid trashing alloca'ed retframe */
ip = (id)-1;
_repInCount++; /* received a reply */
}
return;
}
/* If we didn't get the reply packet yet, get it now. */
if (!ip)
{
if (!_isValid)
{
[NSException raise: NSGenericException
format: @"connection waiting for request was shut down"];
}
ip = [self _getReplyRmc: seq_num];
/*
* Find out if the server is returning an exception instead
* of the return values.
*/
[ip decodeValueOfObjCType: @encode(BOOL) at: &is_exception];
if (is_exception)
{
/* Decode the exception object, and raise it. */
id exc;
[ip decodeValueOfObjCType: @encode(id) at: &exc];
DESTROY(ip);
ip = (id)-1;
/* xxx Is there anything else to clean up in
dissect_method_return()? */
[exc raise];
}
}
[ip decodeValueOfObjCType: type at: datum];
if (*type == _C_ID)
AUTORELEASE(*(id*)datum);
}
#ifdef USE_FFCALL
callframe_build_return (inv, type, outParams, decoder);
#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(ip == (id)-1 || ip == nil, NSInternalInconsistencyException);
}
}
- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target
{
id op, ip;
@ -1811,6 +1953,9 @@ static BOOL multi_threaded = NO;
}
[aRmc decodeValueOfObjCType: type at: datum];
#ifdef USE_FFCALL
if (*type == _C_ID)
#else
/* -decodeValueOfObjCType: at: malloc's new memory
for char*'s. We need to make sure it gets freed eventually
so we don't have a memory leak. Request here that it be
@ -1818,6 +1963,7 @@ static BOOL multi_threaded = NO;
if ((*type == _C_CHARPTR || *type == _C_PTR) && *(void**)datum != 0)
[NSData dataWithBytesNoCopy: *(void**)datum length: 1];
else if (*type == _C_ID)
#endif
AUTORELEASE(*(id*)datum);
}
@ -1871,7 +2017,13 @@ static BOOL multi_threaded = NO;
if (debug_connection > 1)
NSLog(@"Handling message from 0x%x", (gsaddr)self);
_reqInCount++; /* Handling an incoming request. */
#if defined(USE_LIBFFI)
cifframe_do_call (forward_type, decoder, encoder);
#elif defined(USE_FFCALL)
callframe_do_call (forward_type, decoder, encoder);
#else
mframe_do_call (forward_type, decoder, encoder);
#endif
if (op != nil)
{
[self _sendOutRmc: op type: METHOD_REPLY];

View file

@ -31,6 +31,16 @@
#include <Foundation/NSException.h>
#include <Foundation/NSObjCRuntime.h>
#define DO_FORWARD_INVOCATION(_SELX, _ARG1) \
sig = [self methodSignatureForSelector: @selector(_SELX)]; \
inv = [NSInvocation invocationWithMethodSignature: sig]; \
[inv setSelector: @selector(_SELX)]; \
[inv setTarget: self]; \
[inv setArgument: _ARG1 atIndex: 2]; \
[self forwardInvocation: inv]; \
[inv getReturnValue: &m]
static int debug_proxy = 0;
static Class placeHolder = 0;
static Class distantObjectClass = 0;
@ -482,8 +492,17 @@ enum
*/
- (void) forwardInvocation: (NSInvocation*)anInvocation
{
[NSException raise: NSInvalidArgumentException
format: @"Not yet implemented '%s'", sel_get_name(_cmd)];
if (debug_proxy)
NSLog(@"NSDistantObject forwardInvocation: %@\n", anInvocation);
if (![_connection isValid])
[NSException
raise: NSGenericException
format: @"Trying to send message to an invalid Proxy.\n"
@"You should request NSConnectionDidDieNotification's and\n"
@"release all references to the proxy's of invalid Connections."];
[_connection forwardInvocation: anInvocation forProxy: self];
}
- (id) initWithCoder: (NSCoder*)aCoder
@ -778,6 +797,10 @@ enum
{
id m;
const char *types;
#ifdef USE_FFCALL
id inv, sig;
DO_FORWARD_INVOCATION(_cmd, aSelector);
#else
arglist_t args;
void *retframe;
@ -792,6 +815,7 @@ enum
args = __builtin_apply_args();
retframe = [self forward: _cmd : args];
m = retframe_id(retframe);
#endif
types = [m methodType];
return [NSMethodSignature signatureWithObjCTypes: types];
@ -901,6 +925,12 @@ static inline BOOL class_is_kind_of (Class self, Class aClassObject)
- (BOOL) conformsToProtocol: (Protocol*)aProtocol
{
#ifdef USE_FFCALL
BOOL m;
id inv, sig;
DO_FORWARD_INVOCATION(_cmd, aProtocol);
return m;
#else
arglist_t args;
void *retframe;
@ -915,10 +945,17 @@ static inline BOOL class_is_kind_of (Class self, Class aClassObject)
args = __builtin_apply_args();
retframe = [self forward: _cmd : args];
return retframe_bool(retframe);
#endif
}
- (BOOL) respondsToSelector: (SEL)aSelector
{
#ifdef USE_FFCALL
BOOL m;
id inv, sig;
DO_FORWARD_INVOCATION(_cmd, aSelector);
return m;
#else
arglist_t args;
void *retframe;
@ -933,6 +970,7 @@ static inline BOOL class_is_kind_of (Class self, Class aClassObject)
args = __builtin_apply_args();
retframe = [self forward: _cmd : args];
return retframe_bool(retframe);
#endif
}
- (id) replacementObjectForCoder: (NSCoder*)aCoder

View file

@ -22,18 +22,114 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#include "config.h"
#include <Foundation/NSException.h>
#include <Foundation/NSCoder.h>
#include <Foundation/NSInvocation.h>
#include <base/GSInvocation.h>
#include <config.h>
#include <mframe.h>
#if defined(USE_LIBFFI)
#include "cifframe.h"
#elif defined(USE_FFCALL)
#include "callframe.h"
#endif
static Class NSInvocation_abstract_class;
static Class NSInvocation_concrete_class;
@implementation NSInvocation
#ifdef USE_LIBFFI
static inline void
_get_arg(NSInvocation *inv, int index, void *buffer)
{
cifframe_get_arg((cifframe_t *)inv->_cframe, index, buffer);
}
static inline void
_set_arg(NSInvocation *inv, int index, void *buffer)
{
cifframe_set_arg((cifframe_t *)inv->_cframe, index, buffer);
}
static inline void *
_arg_addr(NSInvocation *inv, int index)
{
return cifframe_arg_addr((cifframe_t *)inv->_cframe, index);
}
#elif defined(USE_FFCALL)
static inline void
_get_arg(NSInvocation *inv, int index, void *buffer)
{
callframe_get_arg((callframe_t *)inv->_cframe, index, buffer,
inv->_info[index+1].size);
}
static inline void
_set_arg(NSInvocation *inv, int index, void *buffer)
{
callframe_set_arg((callframe_t *)inv->_cframe, index, buffer,
inv->_info[index+1].size);
}
static inline void *
_arg_addr(NSInvocation *inv, int index)
{
return callframe_arg_addr((callframe_t *)inv->_cframe, index);
}
#else
_get_arg(NSInvocation *inv, int index, void *buffer)
{
mframe_get_arg(inv->_argframe, &inv->_info[index+1], &buffer);
}
static inline void
_set_arg(NSInvocation *inv, int index, void *buffer)
{
mframe_set_arg(inv->_argframe, &inv->_info[index+1], buffer);
}
static inline void *
_arg_addr(NSInvocation *inv, int index)
{
return mframe_arg_addr(inv->_argframe, &inv->_info[index+1]);
}
#endif
+ (id) allocWithZone: (NSZone*)aZone
{
if (self == NSInvocation_abstract_class)
{
return NSAllocateObject(NSInvocation_concrete_class, 0, aZone);
}
else
{
return NSAllocateObject(self, 0, aZone);
}
}
+ (void) initialize
{
if (self == [NSInvocation class])
{
NSInvocation_abstract_class = self;
#if defined(USE_LIBFFI)
NSInvocation_concrete_class = [GSFFIInvocation class];
#elif defined(USE_FFCALL)
NSInvocation_concrete_class = [GSFFCallInvocation class];
#else
NSInvocation_concrete_class = [GSFrameInvocation class];
#endif
}
}
+ (NSInvocation*) invocationWithMethodSignature: (NSMethodSignature*)_signature
{
return AUTORELEASE([[NSInvocation alloc]
return AUTORELEASE([[NSInvocation_concrete_class alloc]
initWithMethodSignature: _signature]);
}
@ -53,19 +149,28 @@
{
char *str;
mframe_get_arg(_argframe, &_info[i], &str);
_get_arg(self, i-1, &str);
NSZoneFree(NSDefaultMallocZone(), str);
}
else if (*_info[i].type == _C_ID)
{
id obj;
mframe_get_arg(_argframe, &_info[i], &obj);
_get_arg(self, i-1, &obj);
RELEASE(obj);
}
}
}
}
#ifdef USE_LIBFFI
if (_cframe)
cifframe_free((cifframe_t *)_cframe);
#else
#ifdef USE_FFCALL
if (_cframe)
callframe_free((callframe_t *)_cframe);
#endif
#endif
if (_argframe)
{
mframe_destroy_argframe([_sig methodType], _argframe);
@ -100,8 +205,7 @@
}
else
{
index++; /* Allow offset for return type _info. */
mframe_get_arg(_argframe, &_info[index], buffer);
_get_arg(self, index, buffer);
}
}
@ -160,8 +264,8 @@
{
id old;
mframe_get_arg(_argframe, &_info[i], &old);
mframe_set_arg(_argframe, &_info[i], buffer);
_get_arg(self, index, &old);
_set_arg(self, index, buffer);
IF_NO_GC(RETAIN(*(id*)buffer));
if (old != nil)
{
@ -173,10 +277,10 @@
char *oldstr;
char *newstr = *(char**)buffer;
mframe_get_arg(_argframe, &_info[i], &oldstr);
_get_arg(self, index, &oldstr);
if (newstr == 0)
{
mframe_set_arg(_argframe, &_info[i], buffer);
_set_arg(self, index, buffer);
}
else
{
@ -184,7 +288,7 @@
tmp = NSZoneMalloc(NSDefaultMallocZone(), strlen(newstr)+1);
strcpy(tmp, newstr);
mframe_set_arg(_argframe, &_info[i], tmp);
_set_arg(self, index, tmp);
}
if (oldstr != 0)
{
@ -194,7 +298,7 @@
}
else
{
mframe_set_arg(_argframe, &_info[i], buffer);
_set_arg(self, index, buffer);
}
}
}
@ -270,7 +374,7 @@
{
id old;
mframe_get_arg(_argframe, &_info[i], &old);
_get_arg(self, i-1, &old);
if (old != nil)
{
IF_NO_GC(RETAIN(old));
@ -280,14 +384,14 @@
{
char *str;
mframe_get_arg(_argframe, &_info[i], &str);
_get_arg(self, i-1, &str);
if (str != 0)
{
char *tmp;
tmp = NSZoneMalloc(NSDefaultMallocZone(), strlen(str)+1);
strcpy(tmp, str);
mframe_set_arg(_argframe, &_info[i], &tmp);
_set_arg(self, i-1, &tmp);
}
}
}
@ -329,9 +433,8 @@
old_target = RETAIN(_target);
[self setTarget: anObject];
mframe_set_arg(_argframe, &_info[1], &_target);
mframe_set_arg(_argframe, &_info[2], &_selector);
_set_arg(self, 0, &_target);
_set_arg(self, 1, &_selector);
imp = method_get_imp(object_is_instance(_target) ?
class_get_instance_method(
@ -349,11 +452,20 @@
stack_argsize = [_sig frameLength];
#ifdef USE_LIBFFI
ffi_call(&((cifframe_t *)_cframe)->cif, (void(*)(void))imp, _retval,
((cifframe_t *)_cframe)->values);
if (_info[0].size)
{
cifframe_decode_return(_info[0].type, _retval);
}
#else
returned = __builtin_apply((void(*)(void))imp, _argframe, stack_argsize);
if (_info[0].size)
{
mframe_decode_return(_info[0].type, _retval, returned);
}
#endif
_validReturn = YES;
}
@ -402,17 +514,19 @@
const char *type = _info[i].type;
void *datum;
datum = mframe_arg_addr(_argframe, &_info[i]);
datum = _arg_addr(self, i-1);
if (*type == _C_ID)
{
[aCoder encodeObject: *(id*)datum];
}
#if !defined(USE_LIBFFI) && !defined(USE_FFCALL)
#if MFRAME_STRUCT_BYREF
else if (*type == _C_STRUCT_B || *type == _C_UNION_B || *type == _C_ARY_B)
{
[aCoder encodeValueOfObjCType: type at: *(void**)datum];
}
#endif
#endif
else
{
@ -439,7 +553,10 @@
[aCoder decodeValueOfObjCType: @encode(char*) at: &types];
newSig = [NSMethodSignature signatureWithObjCTypes: types];
NSZoneFree(NSDefaultMallocZone(), (void*)types);
self = [self initWithMethodSignature: newSig];
RELEASE(self);
self = [NSInvocation invocationWithMethodSignature: newSig];
RETAIN(self);
[aCoder decodeValueOfObjCType: @encode(id) at: &_target];
@ -447,7 +564,8 @@
for (i = 3; i <= _numArgs; i++)
{
datum = mframe_arg_addr(_argframe, &_info[i]);
datum = _arg_addr(self, i-1);
#if !defined(USE_LIBFFI) && !defined(USE_FFCALL)
#if MFRAME_STRUCT_BYREF
{
const char *t = _info[i].type;
@ -457,6 +575,7 @@
datum = *(void**)datum;
}
}
#endif
#endif
[aCoder decodeValueOfObjCType: _info[i].type at: datum];
}
@ -472,12 +591,144 @@
return self;
}
@end
@implementation NSInvocation (GNUstep)
- (id) initWithArgframe: (arglist_t)frame selector: (SEL)aSelector
{
[self subclassResponsibility: _cmd];
return nil;
}
/*
* This is the de_signated initialiser.
*/
- (id) initWithMethodSignature: (NSMethodSignature*)aSignature
{
[self subclassResponsibility: _cmd];
return nil;
}
- (id) initWithSelector: (SEL)aSelector
{
return [self initWithArgframe: 0 selector: aSelector];
}
- (id) initWithTarget: anObject selector: (SEL)aSelector, ...
{
va_list ap;
self = [self initWithArgframe: 0 selector: aSelector];
if (self)
{
int i;
[self setTarget: anObject];
va_start (ap, aSelector);
for (i = 3; i <= _numArgs; i++)
{
const char *type = _info[i].type;
unsigned size = _info[i].size;
void *datum;
#ifdef USE_LIBFFI
size = ((cifframe_t *)_cframe)->args[i-1]->size;
#endif
datum = _arg_addr(self, i-1);
#define CASE_TYPE(_C,_T) case _C: *(_T*)datum = va_arg (ap, _T); break
switch (*type)
{
case _C_ID:
*(id*)datum = va_arg (ap, id);
if (_argsRetained)
{
IF_NO_GC(RETAIN(*(id*)datum));
}
break;
case _C_CHARPTR:
*(char**)datum = va_arg (ap, char*);
if (_argsRetained)
{
char *old = *(char**)datum;
if (old != 0)
{
char *tmp;
tmp = NSZoneMalloc(NSDefaultMallocZone(),strlen(old)+1);
strcpy(tmp, old);
*(char**)datum = tmp;
}
}
break;
CASE_TYPE(_C_CLASS, Class);
CASE_TYPE(_C_SEL, SEL);
CASE_TYPE(_C_LNG, long);
CASE_TYPE(_C_ULNG, unsigned long);
CASE_TYPE(_C_INT, int);
CASE_TYPE(_C_UINT, unsigned int);
case _C_SHT:
*(short*)datum = (short)va_arg(ap, int);
break;
case _C_USHT:
*(unsigned short*)datum = (unsigned short)va_arg(ap, int);
break;
case _C_CHR:
*(char*)datum = (char)va_arg(ap, int);
break;
case _C_UCHR:
*(unsigned char*)datum = (unsigned char)va_arg(ap, int);
break;
case _C_FLT:
*(float*)datum = (float)va_arg(ap, double);
break;
CASE_TYPE(_C_DBL, double);
CASE_TYPE(_C_PTR, void*);
case _C_STRUCT_B:
default:
#if !defined(USE_LIBFFI) && !defined(USE_FFCALL)
#if defined(sparc) || defined(powerpc)
/* FIXME: This only appears on sparc and ppc machines so far.
structures appear to be aligned on word boundaries.
Hopefully there is a more general way to figure this out */
size = (size<sizeof(int))?4:size;
#endif
#endif
NSLog(@"Unsafe handling of type of %d argument.", i-1);
memcpy(datum, ap, size);
{
struct {
char x[size];
} dummy;
dummy = va_arg(ap, typeof(dummy));
}
break;
}
}
}
return self;
}
- (void*) returnFrame: (arglist_t)argFrame
{
[self subclassResponsibility: _cmd];
return NULL;
}
@end
@implementation NSInvocation (BackwardCompatibility)
- (void) invokeWithObject: (id)obj
{
[self invokeWithTarget: (id)obj];
}
@end
@implementation GSFrameInvocation
- (id) initWithArgframe: (arglist_t)frame selector: (SEL)aSelector
{
const char *types;
@ -532,114 +783,8 @@
return self;
}
- (id) initWithSelector: (SEL)aSelector
{
return [self initWithArgframe: 0 selector: aSelector];
}
- (id) initWithTarget: anObject selector: (SEL)aSelector, ...
{
va_list ap;
self = [self initWithArgframe: 0 selector: aSelector];
if (self)
{
int i;
[self setTarget: anObject];
va_start (ap, aSelector);
for (i = 3; i <= _numArgs; i++)
{
const char *type = _info[i].type;
unsigned size = _info[i].size;
void *datum;
datum = mframe_arg_addr(_argframe, &_info[i]);
#define CASE_TYPE(_C,_T) case _C: *(_T*)datum = va_arg (ap, _T); break
switch (*type)
{
case _C_ID:
*(id*)datum = va_arg (ap, id);
if (_argsRetained)
{
IF_NO_GC(RETAIN(*(id*)datum));
}
break;
case _C_CHARPTR:
*(char**)datum = va_arg (ap, char*);
if (_argsRetained)
{
char *old = *(char**)datum;
if (old != 0)
{
char *tmp;
tmp = NSZoneMalloc(NSDefaultMallocZone(),strlen(old)+1);
strcpy(tmp, old);
*(char**)datum = tmp;
}
}
break;
CASE_TYPE(_C_CLASS, Class);
CASE_TYPE(_C_SEL, SEL);
CASE_TYPE(_C_LNG, long);
CASE_TYPE(_C_ULNG, unsigned long);
CASE_TYPE(_C_INT, int);
CASE_TYPE(_C_UINT, unsigned int);
case _C_SHT:
*(short*)datum = (short)va_arg(ap, int);
break;
case _C_USHT:
*(unsigned short*)datum = (unsigned short)va_arg(ap, int);
break;
case _C_CHR:
*(char*)datum = (char)va_arg(ap, int);
break;
case _C_UCHR:
*(unsigned char*)datum = (unsigned char)va_arg(ap, int);
break;
case _C_FLT:
*(float*)datum = (float)va_arg(ap, double);
break;
CASE_TYPE(_C_DBL, double);
CASE_TYPE(_C_PTR, void*);
case _C_STRUCT_B:
default:
#if defined(sparc) || defined(powerpc)
/* FIXME: This only appears on sparc and ppc machines so far.
structures appear to be aligned on word boundaries.
Hopefully there is a more general way to figure this out */
size = (size<sizeof(int))?4:size;
#endif
NSLog(@"Unsafe handling of type of %d argument.", i-1);
memcpy(datum, ap, size);
{
struct {
char x[size];
} dummy;
dummy = va_arg(ap, typeof(dummy));
}
break;
}
}
}
return self;
}
- (void*) returnFrame: (arglist_t)argFrame
{
return mframe_handle_return(_info[0].type, _retval, argFrame);
}
@end
@implementation NSInvocation (BackwardCompatibility)
- (void) invokeWithObject: (id)obj
{
[self invokeWithTarget: (id)obj];
}
@end

55
Source/callframe.h Normal file
View file

@ -0,0 +1,55 @@
/* callframe - Wrapper/Objective-C interface for ffcall function interface
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.
*/
#ifndef callframe_h_INCLUDE
#define callframe_h_INCLUDE
#include <Foundation/NSMethodSignature.h>
@class NSInvocation;
typedef struct _callframe_t {
int nargs;
void *rtype;
void **args;
} callframe_t;
extern callframe_t *callframe_from_info (NSArgumentInfo *info, int numargs,
void **retval);
extern void callframe_free(callframe_t *cframe);
extern void callframe_set_arg(callframe_t *cframe, int index, void *buffer,
int size);
extern void callframe_get_arg(callframe_t *cframe, int index, void *buffer,
int size);
extern void *callframe_arg_addr(callframe_t *cframe, int index);
extern void callframe_do_call (const char *encoded_types,
void(*decoder)(int,void*,const char*),
void(*encoder)(int,void*,const char*,int));
extern void callframe_build_return (NSInvocation *inv,
const char *type,
BOOL out_parameters,
void(*decoder)(int,void*,const char*,int));
#endif

766
Source/callframe.m Normal file
View file

@ -0,0 +1,766 @@
/* callframe.m - Wrapper/Objective-C interface for ffcall function interface
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
/* Return the number of arguments that the method MTH expects. Note
that all methods need two implicit arguments `self' and `_cmd'.
From mframe.m */
extern int method_types_get_number_of_arguments (const char *type);
extern BOOL sel_types_match(const char* t1, const char* t2);
callframe_t *
callframe_from_info (NSArgumentInfo *info, int numargs, void **retval)
{
int i;
callframe_t *cframe;
cframe = malloc(sizeof(callframe_t));
cframe->nargs = numargs;
if (cframe)
{
cframe->args = malloc(cframe->nargs * sizeof(void *));
for (i = 0; i < cframe->nargs; i++)
cframe->args[i] = malloc(info[i+1].size);
}
if (retval)
{
*retval = NSZoneMalloc(NSDefaultMallocZone(),
MAX(info[0].size, sizeof(smallret_t)) );
}
return cframe;
}
void
callframe_free(callframe_t *cframe)
{
int i;
for (i = 0; i < cframe->nargs; i++)
{
free(cframe->args[i]);
cframe->args[i] = 0;
}
cframe->nargs = 0;
free(cframe->args);
free(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()
This function decodes the arguments of method call, builds an
argframe of type arglist_t, and invokes the method using
__builtin_apply; then it encodes the return value and any
pass-by-reference arguments.
ENCODED_TYPES 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 with callframe_dissect_call()---callframe_do_call() uses
ENCODED_TYPES to determine which variable types it should decode.
ENCODED_TYPES 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 ENCODED_TYPES may come from a machine
of a different architecture. Having the original ENCODED_TYPES 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 (int argnum, void *data, const char *type)
ARGNUM is the number of the argument, beginning at 0.
DATA is a pointer to the memory where the value should be placed.
TYPE is a pointer to the type string of this value.
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 DATA.
callframe_do_call() calls this function once with ARGNUM -1, DATA 0,
and 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 (int argnum, void *data, const char *type, int flags)
ARGNUM is the number of the argument; this will be -1 for the
return value, and the argument index for the pass-by-reference
values; the indices start at 0.
DATA is a pointer to the memory where the value can be found.
TYPE is a pointer to the type string of this value.
FLAGS is a copy of the type qualifier flags for this argument;
(see <objc/objc-api.h>).
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 DATA wherever the user wants to
record the ARGNUM'th return value.
PASS_POINTERS is a flag saying whether pointers should be passed
as pointers (for local stuff) or should be assumed to point to a
single data item (for distributed objects).
*/
void
callframe_do_call_opts (const char *encoded_types,
void(*decoder)(int,void*,const char*),
void(*encoder)(int,void*,const char*,int),
BOOL pass_pointers)
{
/* 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;
/* A pointer into the ARGFRAME; points at individual arguments. */
char *datum;
/* Type qualifier flags; see <objc/objc-api.h>. */
unsigned flags;
/* Which argument number are we processing now? */
int argnum;
/* A pointer to the memory holding the return value of the method. */
void *retval;
/* 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;
/* Decode the object, (which is always the first argument to a method),
into the local variable OBJECT. */
(*decoder) (0, &object, @encode(id));
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 ":" */
(*decoder) (1, &selector, ":");
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 NeXT_runtime
{
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);
/* 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);
datum = callframe_arg_addr(cframe, argnum);
/* 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) (argnum, datum, tmptype);
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;
if (pass_pointers)
{
if ((flags & _F_IN) || !(flags & _F_OUT))
(*decoder) (argnum, datum, tmptype);
}
else
{
/* 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++;
/* 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**)datum = alloca (objc_sizeof_type (tmptype));
/* 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) (argnum, *(void**)datum, tmptype);
}
break;
case _C_STRUCT_B:
case _C_UNION_B:
case _C_ARY_B:
/* Handle struct and array arguments. */
(*decoder) (argnum, datum, tmptype);
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) (argnum, datum, tmptype);
}
}
/* End of the for() loop that enumerates the method's arguments. */
(*decoder) (-1, 0, 0);
/* 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 rencode 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];
GSFFCallInvokeWithTargetAndImp((NSInvocation *)inv, object,
method_implementation);
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. */
/* If there is a return value, encode it. */
switch (*tmptype)
{
case _C_VOID:
if ((flags & _F_ONEWAY) == 0)
{
int dummy = 0;
(*encoder) (-1, (void*)&dummy, @encode(int), 0);
}
/* No return value to encode; do nothing. */
break;
case _C_PTR:
if (pass_pointers)
{
(*encoder) (-1, retval, tmptype, flags);
}
else
{
/* The argument is a pointer to something; increment TYPE
so we can see what it is a pointer to. */
tmptype++;
/* Encode the value that was pointed to. */
(*encoder) (-1, *(void**)retval, tmptype, flags);
}
break;
case _C_STRUCT_B:
case _C_UNION_B:
case _C_ARY_B:
/* The argument is a structure or array returned by value.
(In C, are array's allowed to be returned by value?) */
(*encoder)(-1, retval, tmptype, flags);
break;
case _C_FLT:
{
(*encoder) (-1, retval, tmptype, flags);
break;
}
case _C_DBL:
{
(*encoder) (-1, retval, tmptype, flags);
break;
}
case _C_SHT:
case _C_USHT:
{
(*encoder) (-1, retval, tmptype, flags);
break;
}
case _C_CHR:
case _C_UCHR:
{
(*encoder) (-1, retval, tmptype, flags);
break;
}
default:
/* case _C_INT: case _C_UINT: case _C_LNG: case _C_ULNG:
case _C_CHARPTR: case: _C_ID: */
/* xxx I think this assumes that sizeof(int)==sizeof(void*) */
(*encoder) (-1, retval, tmptype, flags);
}
/* 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. */
datum = callframe_arg_addr(cframe, argnum);
if ((*tmptype == _C_PTR)
&& ((flags & _F_OUT) || !(flags & _F_IN)))
{
/* 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.*/
/* Encode it. */
(*encoder) (argnum, datum, tmptype, flags);
}
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. */
(*encoder) (argnum, datum, tmptype, flags);
}
}
}
return;
}
void
callframe_do_call (const char *encoded_types,
void(*decoder)(int,void*,const char*),
void(*encoder)(int,void*,const char*,int))
{
callframe_do_call_opts(encoded_types, decoder, encoder, YES);
}
/* 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 nul 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_opts (NSInvocation *inv,
const char *type,
BOOL out_parameters,
void(*decoder)(int,void*,const char*,int),
BOOL pass_pointers)
{
/* The size, in bytes, of memory pointed to by RETFRAME. */
int retsize;
/* 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);
/* 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)
{
switch (*tmptype)
{
case _C_PTR:
if (pass_pointers)
{
(*decoder) (-1, retval, tmptype, flags);
}
else
{
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 =
NSZoneMalloc(NSDefaultMallocZone(), retLength);
/* We are responsible for making sure this memory gets free'd
eventually. Ask NSData class to autorelease it. */
[NSData dataWithBytesNoCopy: *(void**)retval
length: retLength];
/* Decode the return value into the memory we allocated. */
(*decoder) (-1, *(void**)retval, tmptype, flags);
}
break;
case _C_STRUCT_B:
case _C_UNION_B:
case _C_ARY_B:
/* Decode the return value into the memory we allocated. */
(*decoder) (-1, retval, tmptype, flags);
break;
case _C_FLT:
case _C_DBL:
(*decoder) (-1, ((char*)retval), tmptype, flags);
break;
case _C_VOID:
{
(*decoder) (-1, retval, @encode(int), 0);
}
break;
default:
/* (Among other things, _C_CHARPTR is handled here). */
/* Special case BOOL (and other types smaller than int)
because retval doesn't actually point to the char */
/* xxx What about structures smaller than int's that
are passed by reference on true structure reference-
passing architectures? */
/* xxx Is this the right test? Use sizeof(int) instead? */
if (retsize < sizeof(void*))
{
#if 1
/* Frith-Macdonald said this worked better 21 Nov 96. */
(*decoder) (-1, retval, tmptype, flags);
#else
*(void**)retval = 0;
(*decoder) (-1, ((char*)retval)+sizeof(void*)-retsize,
tmptype, flags);
#endif
}
else
{
(*decoder) (-1, retval, tmptype, flags);
}
}
}
[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);
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.*/
(*decoder) (argnum, datum, tmptype, flags);
/* Copy the pointed-to data back to the original
pointer */
[inv getArgument: &ptr atIndex: argnum];
memcpy(ptr, *(void **)datum, objc_sizeof_type(tmptype+1));
}
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) (argnum, datum, tmptype, flags);
[inv setArgument: datum atIndex: argnum];
}
}
}
(*decoder) (0, 0, 0, 0); /* Tell it we have finished. */
}
callframe_free(cframe);
}
void
callframe_build_return (NSInvocation *inv,
const char *type,
BOOL out_parameters,
void(*decoder)(int,void*,const char*,int))
{
callframe_build_return_opts(inv, type, out_parameters, decoder, YES);
}

50
Source/cifframe.h Normal file
View file

@ -0,0 +1,50 @@
/* cifframe - Wrapper/Objective-C interface for ffi function interface
Copyright (C) 1999, Free Software Foundation, Inc.
Written by: Adam Fedor <fedor@gnu.org>
Created: Feb 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.
*/
#ifndef cifframe_h_INCLUDE
#define cifframe_h_INCLUDE
#include <ffi.h>
#include <base/preface.h>
typedef struct _cifframe_t {
ffi_cif cif;
int nargs;
ffi_type *rtype;
ffi_type **args;
void **values;
} cifframe_t;
extern cifframe_t *cifframe_from_sig (const char *typePtr, void **retval);
extern void cifframe_free(cifframe_t *cframe);
extern void cifframe_set_arg(cifframe_t *cframe, int index, void *buffer);
extern void cifframe_get_arg(cifframe_t *cframe, int index, void *buffer);
extern void *cifframe_arg_addr(cifframe_t *cframe, int index);
extern BOOL cifframe_decode_return (const char *type, void* buffer);
extern void cifframe_do_call (const char *encoded_types,
void(*decoder)(int,void*,const char*),
void(*encoder)(int,void*,const char*,int));
#endif

847
Source/cifframe.m Normal file
View file

@ -0,0 +1,847 @@
/* cifframe.m - Wrapper/Objective-C interface for ffi function interface
Copyright (C) 1999, Free Software Foundation, Inc.
Written by: Adam Fedor <fedor@gnu.org>
Created: Dec 1999
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 "cifframe.h"
#include <Foundation/NSException.h>
#if defined(ALPHA) || (defined(MIPS) && (_MIPS_SIM == _ABIN32))
typedef long long smallret_t;
#else
typedef int smallret_t;
#endif
/* Return the number of arguments that the method MTH expects. Note
that all methods need two implicit arguments `self' and `_cmd'.
From mframe.m */
extern int method_types_get_number_of_arguments (const char *type);
extern BOOL sel_types_match(const char* t1, const char* t2);
const char *cifframe_next_arg(const char *typePtr, ffi_type **ftype_ret);
cifframe_t *
cifframe_from_sig (const char *typePtr, void **retval)
{
int i;
cifframe_t *cframe;
cframe = malloc(sizeof(cifframe_t));
cframe->nargs = method_types_get_number_of_arguments(typePtr);
cframe->args = malloc(cframe->nargs * sizeof(ffi_type));
typePtr = cifframe_next_arg(typePtr, &cframe->rtype);
for (i = 0; i < cframe->nargs; i++)
typePtr = cifframe_next_arg(typePtr, &cframe->args[i]);
if (ffi_prep_cif(&cframe->cif, FFI_DEFAULT_ABI, cframe->nargs,
cframe->rtype, cframe->args) != FFI_OK)
{
free(cframe->args);
free(cframe);
cframe = NULL;
}
if (cframe)
{
cframe->values = malloc(cframe->nargs * sizeof(void *));
for (i = 0; i < cframe->nargs; i++)
cframe->values[i] = malloc(cframe->args[i]->size);
}
if (retval)
{
*retval = NSZoneMalloc(NSDefaultMallocZone(),
MAX(cframe->rtype->size, sizeof(smallret_t)) );
}
return cframe;
}
void
cifframe_free(cifframe_t *cframe)
{
int i;
if (cframe->rtype->type == FFI_TYPE_STRUCT)
free(cframe->rtype->elements);
for (i = 0; i < cframe->nargs; i++)
{
free(cframe->values[i]);
cframe->values[i] = 0;
if (cframe->args[i]->type == FFI_TYPE_STRUCT)
free(cframe->rtype->elements);
}
cframe->nargs = 0;
free(cframe->args);
free(cframe->values);
free(cframe);
}
void
cifframe_set_arg(cifframe_t *cframe, int index, void *buffer)
{
if (index < 0 || index >= cframe->nargs)
return;
memcpy(cframe->values[index], buffer, cframe->args[index]->size);
}
void
cifframe_get_arg(cifframe_t *cframe, int index, void *buffer)
{
if (index < 0 || index >= cframe->nargs)
return;
memcpy(buffer, cframe->values[index], cframe->args[index]->size);
}
void *
cifframe_arg_addr(cifframe_t *cframe, int index)
{
if (index < 0 || index >= cframe->nargs)
return NULL;
return cframe->values[index];
}
/*
* Step through method encoding information extracting details.
*/
const char *
cifframe_next_arg(const char *typePtr, ffi_type **ftype_ret)
{
BOOL flag;
const char *type;
ffi_type *ftype;
/*
* Skip past any type qualifiers
*/
flag = YES;
while (flag)
{
switch (*typePtr)
{
case _C_CONST:
case _C_IN:
case _C_INOUT:
case _C_OUT:
case _C_BYCOPY:
#ifdef _C_BYREF
case _C_BYREF:
#endif
case _C_ONEWAY:
#ifdef _C_GCINVISIBLE
case _C_GCINVISIBLE:
#endif
break;
default: flag = NO;
}
if (flag)
{
typePtr++;
}
}
type = typePtr;
/*
* Scan for size and alignment information.
*/
switch (*typePtr++)
{
case _C_ID: ftype = &ffi_type_pointer;
break;
case _C_CLASS: ftype = &ffi_type_pointer;
break;
case _C_SEL: ftype = &ffi_type_pointer;
break;
case _C_CHR: ftype = &ffi_type_schar;
break;
case _C_UCHR: ftype = &ffi_type_uchar;
break;
case _C_SHT: ftype = &ffi_type_sshort;
break;
case _C_USHT: ftype = &ffi_type_ushort;
break;
case _C_INT: ftype = &ffi_type_sint;
break;
case _C_UINT: ftype = &ffi_type_uint;
break;
case _C_LNG: ftype = &ffi_type_slong;
break;
case _C_ULNG: ftype = &ffi_type_ulong;
break;
#ifdef _C_LNG_LNG
case _C_LNG_LNG: ftype = 0;
NSCAssert(ftype, @"long long encoding not implemented");
break;
case _C_ULNG_LNG: ftype = 0;
NSCAssert(ftype, @"long long encoding not implemented");
break;
#endif
case _C_FLT: ftype = &ffi_type_float;
break;
case _C_DBL: ftype = &ffi_type_double;
break;
case _C_PTR:
ftype = &ffi_type_pointer;
if (*typePtr == '?')
{
typePtr++;
}
else
{
ffi_type *local;
typePtr = cifframe_next_arg(typePtr, &local);
}
break;
case _C_ATOM:
case _C_CHARPTR:
ftype = &ffi_type_pointer;
break;
case _C_ARY_B:
{
ffi_type *local;
ftype = &ffi_type_pointer;
while (isdigit(*typePtr))
{
typePtr++;
}
typePtr = cifframe_next_arg(typePtr, &local);
typePtr++; /* Skip end-of-array */
}
break;
case _C_STRUCT_B:
{
int types, maxtypes;
ffi_type *local;
ftype = malloc(sizeof(ffi_type));
ftype->size = 0;
ftype->alignment = 0;
ftype->type = FFI_TYPE_STRUCT;
/*
* Skip "<name>=" stuff.
*/
while (*typePtr != _C_STRUCT_E)
{
if (*typePtr++ == '=')
{
break;
}
}
types = 0;
maxtypes = 4;
ftype->elements = malloc(maxtypes*sizeof(ffi_type));
/*
* Continue accumulating structure size.
*/
while (*typePtr != _C_STRUCT_E)
{
typePtr = cifframe_next_arg(typePtr, &local);
NSCAssert(typePtr, @"End of signature while parsing");
ftype->elements[types++] = local;
if (types >= maxtypes)
{
maxtypes *=2;
ftype->elements = realloc(ftype->elements,
maxtypes*sizeof(ffi_type));
}
}
ftype->elements[types] = NULL;
typePtr++; /* Skip end-of-struct */
}
break;
case _C_UNION_B:
{
ffi_type *local;
int max_align = 0;
/*
* Skip "<name>=" stuff.
*/
while (*typePtr != _C_UNION_E)
{
if (*typePtr++ == '=')
{
break;
}
}
ftype = NULL;
while (*typePtr != _C_UNION_E)
{
int align = objc_alignof_type(typePtr);
typePtr = cifframe_next_arg(typePtr, &local);
NSCAssert(typePtr, @"End of signature while parsing");
if (align > max_align)
{
if (ftype && ftype->type == FFI_TYPE_STRUCT)
free(ftype->elements);
ftype = local;
max_align = align;
}
}
typePtr++; /* Skip end-of-union */
}
break;
case _C_VOID: ftype = &ffi_type_void;
break;
default:
NSCAssert(0, @"Unknown type in sig");
}
/* Skip past any offset information, if there is any */
if (*type != _C_PTR || *type == '?')
{
if (*typePtr == '+')
typePtr++;
if (*typePtr == '-')
typePtr++;
while (isdigit(*typePtr))
typePtr++;
}
NSCAssert(typePtr, @"Unfinished signature");
*ftype_ret = ftype;
return typePtr;
}
/* Some return types actually get coded differently. We need to convert
back to the expected return type */
BOOL
cifframe_decode_return (const char *type, void* buffer)
{
int size = 0;
type = objc_skip_type_qualifiers(type);
NSGetSizeAndAlignment(type, &size, 0);
switch (*type)
{
case _C_ID:
break;
case _C_CLASS:
break;
case _C_SEL:
break;
case _C_CHR:
case _C_UCHR:
{
*(unsigned char*)buffer = (unsigned char)(*((smallret_t *)buffer));
break;
}
case _C_SHT:
case _C_USHT:
{
*(unsigned short*)buffer = (unsigned short)(*((smallret_t *)buffer));
break;
}
case _C_INT:
case _C_UINT:
{
*(unsigned int*)buffer = (unsigned int)(*((smallret_t *)buffer));
break;
}
case _C_LNG:
case _C_ULNG:
break;
case _C_LNG_LNG:
case _C_ULNG_LNG:
break;
case _C_FLT:
break;
case _C_DBL:
break;
case _C_PTR:
case _C_ATOM:
case _C_CHARPTR:
break;
case _C_ARY_B:
case _C_STRUCT_B:
case _C_UNION_B:
break;
case _C_VOID:
break;
default:
return NO; /* Unknown type. */
}
return YES;
}
/*-------------------------------------------------------------------------*/
/* Functions for handling sending and receiving messages accross a
connection
*/
/* cifframe_do_call()
This function decodes the arguments of method call, builds an
argframe of type arglist_t, and invokes the method using
__builtin_apply; then it encodes the return value and any
pass-by-reference arguments.
ENCODED_TYPES 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 with cifframe_dissect_call()---cifframe_do_call() uses
ENCODED_TYPES to determine which variable types it should decode.
ENCODED_TYPES 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 ENCODED_TYPES may come from a machine
of a different architecture. Having the original ENCODED_TYPES 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 (int argnum, void *data, const char *type)
ARGNUM is the number of the argument, beginning at 0.
DATA is a pointer to the memory where the value should be placed.
TYPE is a pointer to the type string of this value.
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 DATA.
cifframe_do_call() calls this function once with ARGNUM -1, DATA 0,
and 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 (int argnum, void *data, const char *type, int flags)
ARGNUM is the number of the argument; this will be -1 for the
return value, and the argument index for the pass-by-reference
values; the indices start at 0.
DATA is a pointer to the memory where the value can be found.
TYPE is a pointer to the type string of this value.
FLAGS is a copy of the type qualifier flags for this argument;
(see <objc/objc-api.h>).
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 DATA wherever the user wants to
record the ARGNUM'th return value.
PASS_POINTERS is a flag saying whether pointers should be passed
as pointers (for local stuff) or should be assumed to point to a
single data item (for distributed objects).
*/
void
cifframe_do_call_opts (const char *encoded_types,
void(*decoder)(int,void*,const char*),
void(*encoder)(int,void*,const char*,int),
BOOL pass_pointers)
{
/* 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;
/* A pointer into the ARGFRAME; points at individual arguments. */
char *datum;
/* Type qualifier flags; see <objc/objc-api.h>. */
unsigned flags;
/* Which argument number are we processing now? */
int argnum;
/* A pointer to the memory holding the return value of the method. */
void *retval;
/* 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;
/* Decode the object, (which is always the first argument to a method),
into the local variable OBJECT. */
(*decoder) (0, &object, @encode(id));
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 ":" */
(*decoder) (1, &selector, ":");
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 NeXT_runtime
{
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 */
cframe = cifframe_from_sig(type, &retval);
/* 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);
/* 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);
/* 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);
datum = cifframe_arg_addr(cframe, argnum);
/* 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) (argnum, datum, tmptype);
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;
if (pass_pointers)
{
if ((flags & _F_IN) || !(flags & _F_OUT))
(*decoder) (argnum, datum, tmptype);
}
else
{
/* 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++;
/* 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**)datum = alloca (objc_sizeof_type (tmptype));
/* 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) (argnum, *(void**)datum, tmptype);
}
break;
case _C_STRUCT_B:
case _C_UNION_B:
case _C_ARY_B:
/* Handle struct and array arguments. */
(*decoder) (argnum, datum, tmptype);
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) (argnum, datum, tmptype);
}
}
/* End of the for() loop that enumerates the method's arguments. */
(*decoder) (-1, 0, 0);
/* 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 rencode any pass-by-reference info */
ffi_call(&(cframe->cif), FFI_FN(method_implementation), retval,
cframe->values);
/* 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. */
/* If there is a return value, encode it. */
cifframe_decode_return(tmptype, retval);
switch (*tmptype)
{
case _C_VOID:
if ((flags & _F_ONEWAY) == 0)
{
int dummy = 0;
(*encoder) (-1, (void*)&dummy, @encode(int), 0);
}
/* No return value to encode; do nothing. */
break;
case _C_PTR:
if (pass_pointers)
{
(*encoder) (-1, retval, tmptype, flags);
}
else
{
/* The argument is a pointer to something; increment TYPE
so we can see what it is a pointer to. */
tmptype++;
/* Encode the value that was pointed to. */
(*encoder) (-1, *(void**)retval, tmptype, flags);
}
break;
case _C_STRUCT_B:
case _C_UNION_B:
case _C_ARY_B:
/* The argument is a structure or array returned by value.
(In C, are array's allowed to be returned by value?) */
(*encoder)(-1, retval, tmptype, flags);
break;
case _C_FLT:
{
(*encoder) (-1, retval, tmptype, flags);
break;
}
case _C_DBL:
{
(*encoder) (-1, retval, tmptype, flags);
break;
}
case _C_SHT:
case _C_USHT:
{
(*encoder) (-1, retval, tmptype, flags);
break;
}
case _C_CHR:
case _C_UCHR:
{
(*encoder) (-1, retval, tmptype, flags);
break;
}
default:
/* case _C_INT: case _C_UINT: case _C_LNG: case _C_ULNG:
case _C_CHARPTR: case: _C_ID: */
/* xxx I think this assumes that sizeof(int)==sizeof(void*) */
(*encoder) (-1, retval, tmptype, flags);
}
/* 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. */
datum = cifframe_arg_addr(cframe, argnum);
if ((*tmptype == _C_PTR)
&& ((flags & _F_OUT) || !(flags & _F_IN)))
{
/* 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.*/
/* The argument is a pointer to something; increment TYPE
so we can see what it is a pointer to. */
tmptype++;
/* Encode it. */
(*encoder) (argnum, *(void**)datum, tmptype, flags);
}
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. */
(*encoder) (argnum, datum, tmptype, flags);
}
}
}
return;
}
void
cifframe_do_call (const char *encoded_types,
void(*decoder)(int,void*,const char*),
void(*encoder)(int,void*,const char*,int))
{
cifframe_do_call_opts(encoded_types, decoder, encoder, NO);
}

View file

@ -88,7 +88,7 @@ int con_data (id prx)
printf(send, var); \
varr = [prx msg1 var]; \
printf(got, varr); \
if (varr != var+ADD_CONST) \
if (varr != (var+ADD_CONST)) \
printf(" *** ERROR ***\n"); \
else \
printf(" ...ok\n"); \
@ -96,20 +96,37 @@ int con_data (id prx)
printf(sendp, varr); \
[prx msg2 &varr]; \
printf(got, varr); \
if (varr != var+ADD_CONST) \
if (varr != (var+ADD_CONST)) \
printf(" *** ERROR ***\n"); \
else \
printf(" ...ok\n");
TEST_CALL("UChar:\n", " sending %x", " got %x", " sending ptr to %x",
#define TEST_FCALL(test, send, got, sendp, var, varr, val, msg1, msg2) \
printf(test); \
var = val; \
printf(send, var); \
varr = [prx msg1 var]; \
printf(got, varr); \
if (varr - (var+ADD_CONST) > 1e-3) \
printf(" *** ERROR ***\n"); \
else \
printf(" ...ok\n"); \
varr = var = val+1; \
printf(sendp, varr); \
[prx msg2 &varr]; \
printf(got, varr); \
if (varr - (var+ADD_CONST) > 1e-3) \
printf(" *** ERROR ***\n"); \
else \
printf(" ...ok\n");
TEST_CALL("UChar:\n", " sending %d", " got %d", " sending ptr to %d",
uc, ucr, 23, sendUChar:, getUChar:)
printf(" (should get error returning ptr)\n");
TEST_CALL("Char:\n", " sending %x", " got %x", " sending ptr to %x",
TEST_CALL("Char:\n", " sending %d", " got %d", " sending ptr to %d",
c, cr, 23, sendChar:, getChar:)
printf(" (should get error returning ptr)\n");
TEST_CALL("Short:\n", " sending %d", " got %d", " sending ptr to %d",
TEST_CALL("Short:\n", " sending %hd", " got %hd", " sending ptr to %hd",
s, sr, 23, sendShort:, getShort:)
TEST_CALL("Int:\n", " sending %d", " got %d", " sending ptr to %d",
@ -118,10 +135,10 @@ int con_data (id prx)
TEST_CALL("Long:\n", " sending %ld", " got %ld", " sending ptr to %ld",
l, lr, 23, sendLong:, getLong:)
TEST_CALL("Float:\n", " sending %f", " got %f", " sending ptr to %f",
TEST_FCALL("Float:\n", " sending %f", " got %f", " sending ptr to %f",
flt, fltr, 23.2, sendFloat:, getFloat:)
TEST_CALL("Double:\n", " sending %g", " got %g", " sending ptr to %g",
TEST_FCALL("Double:\n", " sending %g", " got %g", " sending ptr to %g",
dbl, dblr, 23.2, sendDouble:, getDouble:)
flt = 2.718;

View file

@ -74,7 +74,7 @@
- (BOOL) sendBoolean: (BOOL)b
{
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd), b, !b);
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd), (int)b, (int)(!b));
fflush(stdout);
return !b;
}
@ -82,16 +82,18 @@
/* This causes problems, because the runtime encodes this as "*", a string! */
- (void) getBoolean: (BOOL*)bp
{
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd), *bp, !(*bp));
BOOL rbp = !(*bp);
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd),
(int)*bp, (int)rbp);
fflush(stdout);
*bp = !(*bp);
*bp = rbp;
}
/* This causes problems, because the runtime encodes this as "*", a string! */
- (unsigned char) sendUChar: (unsigned char)num
{
unsigned char rnum = num + ADD_CONST;
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd), num, rnum);
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd),
(int)num, (int)rnum);
fflush(stdout);
return rnum;
}
@ -100,7 +102,8 @@
- (void) getUChar: (unsigned char *)num
{
unsigned char rnum = *num + ADD_CONST;
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd), *num, rnum);
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd),
(int)(*num), (int)rnum);
*num = rnum;
fflush(stdout);
}
@ -108,7 +111,8 @@
- (char) sendChar: (char)num
{
char rnum = num + ADD_CONST;
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd), num, rnum);
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd),
(int)num, (int)rnum);
fflush(stdout);
return rnum;
}
@ -116,7 +120,8 @@
- (void) getChar: (char *)num
{
char rnum = *num + ADD_CONST;
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd), *num, rnum);
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd),
(int)(*num), (int)rnum);
*num = rnum;
fflush(stdout);
}
@ -124,7 +129,8 @@
- (short) sendShort: (short)num
{
short rnum = num + ADD_CONST;
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd), num, rnum);
printf("(%s) got %hd, returning %hd\n", sel_get_name(_cmd),
num, rnum);
fflush(stdout);
return rnum;
}
@ -132,7 +138,8 @@
- (void) getShort: (short *)num
{
short rnum = *num + ADD_CONST;
printf("(%s) got %d, returning %d\n", sel_get_name(_cmd), *num, rnum);
printf("(%s) got %hd, returning %hd\n", sel_get_name(_cmd),
(*num), rnum);
*num = rnum;
fflush(stdout);
}

View file

@ -138,3 +138,10 @@
/* Define if your system has a /proc/self/exe symlink to the executable */
#undef HAVE_PROC_FS_EXE_LINK
/* Define if using the libffi library for invocations */
#undef USE_LIBFFI
/* Define if using the ffcall library for invocations */
#undef USE_FFCALL

View file

@ -8,3 +8,4 @@ DYNAMIC_LINKER=@DYNAMIC_LINKER@
HAVE_LIBXML=@HAVE_LIBXML@
WITH_FFI=@WITH_FFI@

1033
configure vendored

File diff suppressed because it is too large Load diff

View file

@ -736,7 +736,7 @@ fi
#--------------------------------------------------------------------
AC_MSG_CHECKING("use of pass-through arguments")
AC_ARG_ENABLE(pass-arguments,
[ --enable-pass-arguments User main calls NSProcessInfo initialization],,
[ --enable-pass-arguments Force user main call to NSProcessInfo initialize],,
enable_pass_arguments=no)
case "$target_os" in
@ -752,7 +752,7 @@ AC_MSG_RESULT($enable_pass_arguments)
AC_MSG_CHECKING("use of fake-main definition")
AC_ARG_ENABLE(fake-main,
[ --enable-fake-main Force use of redefine of the main function],,
[ --enable-fake-main Force redefine of user main function],,
enable_fake_main=no)
if test "$enable_pass_arguments" = "no"; then
@ -785,6 +785,39 @@ esac
AC_SUBST(GS_FAKE_MAIN)
AC_MSG_RESULT($enable_fake_main)
#--------------------------------------------------------------------
# Check for FFI interface libraries for invocations
#--------------------------------------------------------------------
AC_ARG_ENABLE(libffi,
[ --enable-libffi Enable use of libffi library],,
enable_libffi=no)
AC_ARG_ENABLE(ffcall,
[ --enable-ffcall Enable use of ffcall library],,
enable_ffcall=no)
AC_CHECK_HEADER(ffi.h, , enable_libffi=no)
AC_TRY_CPP([#include "objc/objc-api.h"
#ifndef OBJC_METHOD_LOOKUP_HOOKS
#error
#endif], , enable_ffcall=no)
AC_CHECK_HEADERS(callback.h, , enable_ffcall=no)
AC_MSG_CHECKING("FFI library usage")
WITH_FFI=none
if test $enable_libffi = yes; then
AC_DEFINE(USE_LIBFFI)
WITH_FFI=libffi
AC_MSG_RESULT(libffi)
elif test $enable_ffcall = yes; then
AC_DEFINE(USE_FFCALL)
WITH_FFI=ffcall
AC_MSG_RESULT(ffcall)
else
AC_MSG_RESULT(none)
fi
AC_SUBST(WITH_FFI)
#--------------------------------------------------------------------
# Check recent libxlm for Properytlists, GSXML, GSDoc etc.
#--------------------------------------------------------------------