mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-30 16:30:41 +00:00
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:
parent
68bda2e25c
commit
948cbdcbb3
28 changed files with 3985 additions and 730 deletions
37
ChangeLog
37
ChangeLog
|
@ -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>
|
2000-12-07 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* Source/NSGSet.m: renamed to GSSet.m for consistency, tidied.
|
* Source/NSGSet.m: renamed to GSSet.m for consistency, tidied.
|
||||||
|
|
|
@ -31,7 +31,7 @@ include ../Version
|
||||||
include ../config.mak
|
include ../config.mak
|
||||||
|
|
||||||
# The documents to be generated
|
# The documents to be generated
|
||||||
DOCUMENT_NAME = gnustep-base coding-standards
|
DOCUMENT_NAME = coding-standards
|
||||||
|
|
||||||
# The text documents to be generated
|
# The text documents to be generated
|
||||||
DOCUMENT_TEXT_NAME = \
|
DOCUMENT_TEXT_NAME = \
|
||||||
|
|
|
@ -114,47 +114,6 @@ site @url{http://www.gnustep.org/information}.
|
||||||
Documentation for individual classes is included in gsdoc (and html) format
|
Documentation for individual classes is included in gsdoc (and html) format
|
||||||
in the gsdoc directory.
|
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
|
@node Implementation, Concept Index, Installation, Top
|
||||||
@chapter Implementation Details
|
@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
|
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.
|
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
|
@bye
|
||||||
|
|
|
@ -18,6 +18,29 @@
|
||||||
<conform>NSCoding</conform>
|
<conform>NSCoding</conform>
|
||||||
<desc>
|
<desc>
|
||||||
</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">
|
<method type="NSDictionary*" factory="yes">
|
||||||
<sel>abbreviationDictionary</sel>
|
<sel>abbreviationDictionary</sel>
|
||||||
<desc>
|
<desc>
|
||||||
|
|
|
@ -5,17 +5,129 @@
|
||||||
@c set the vars GNUSTEP_BASE_VERSION and GNUSTEP_BASE_GCC_VERSION
|
@c set the vars GNUSTEP_BASE_VERSION and GNUSTEP_BASE_GCC_VERSION
|
||||||
@include version.texi
|
@include version.texi
|
||||||
|
|
||||||
@ifset TEXT_ONLY
|
@node Top, Introduction, (dir), (dir)
|
||||||
This file documents the installation of the GNUstep Base Library,
|
|
||||||
@samp{libgnustep-base}. Copyright (C) 1993, 1994, 1996, 1997 Free Software
|
@menu
|
||||||
Foundation, Inc. You may copy, distribute, and modify it freely as long
|
* Introduction::
|
||||||
as you preserve this copyright notice and permission notice.
|
* Configuration::
|
||||||
@chapter Installing @samp{libgnustep-base}
|
* Reading Command-Line Arguments::
|
||||||
@end ifset
|
* 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
|
@bye
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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{NEWS} has the library's feature history.
|
||||||
|
|
||||||
The file @samp{GNUstep-HOWTO}
|
The files @samp{INSTALL} or @samp{GNUstep-HOWTO} (from the core package)
|
||||||
give instructions for installing the library.
|
gives instructions for installing the library.
|
||||||
|
|
||||||
@section How can you help?
|
@section How can you help?
|
||||||
|
|
||||||
|
|
51
Headers/gnustep/base/GSInvocation.h
Normal file
51
Headers/gnustep/base/GSInvocation.h
Normal 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
|
|
@ -36,6 +36,7 @@
|
||||||
@class NSPort;
|
@class NSPort;
|
||||||
@class NSPortNameServer;
|
@class NSPortNameServer;
|
||||||
@class NSData;
|
@class NSData;
|
||||||
|
@class NSInvocation;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keys for the NSDictionary returned by [NSConnection -statistics]
|
* Keys for the NSDictionary returned by [NSConnection -statistics]
|
||||||
|
@ -159,6 +160,8 @@ GS_EXPORT NSString *NSConnectionProxyCount; /* Objects received */
|
||||||
- (retval_t) forwardForProxy: (NSDistantObject*)object
|
- (retval_t) forwardForProxy: (NSDistantObject*)object
|
||||||
selector: (SEL)sel
|
selector: (SEL)sel
|
||||||
argFrame: (arglist_t)frame;
|
argFrame: (arglist_t)frame;
|
||||||
|
- (void) forwardInvocation: (NSInvocation *)inv
|
||||||
|
forProxy: (NSDistantObject*)object;
|
||||||
- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target;
|
- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
{
|
{
|
||||||
NSMethodSignature *_sig;
|
NSMethodSignature *_sig;
|
||||||
arglist_t _argframe;
|
arglist_t _argframe;
|
||||||
|
void *_cframe;
|
||||||
void *_retval;
|
void *_retval;
|
||||||
id _target;
|
id _target;
|
||||||
SEL _selector;
|
SEL _selector;
|
||||||
|
|
|
@ -48,6 +48,12 @@
|
||||||
/* Define if your system has a /proc/self/exe symlink to the executable */
|
/* Define if your system has a /proc/self/exe symlink to the executable */
|
||||||
#undef HAVE_PROC_FS_EXE_LINK
|
#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. */
|
/* The number of bytes in a double. */
|
||||||
#undef SIZEOF_DOUBLE
|
#undef SIZEOF_DOUBLE
|
||||||
|
|
||||||
|
@ -135,12 +141,18 @@
|
||||||
/* Define if you have the vsprintf function. */
|
/* Define if you have the vsprintf function. */
|
||||||
#undef HAVE_VSPRINTF
|
#undef HAVE_VSPRINTF
|
||||||
|
|
||||||
|
/* Define if you have the <callback> header file. */
|
||||||
|
#undef HAVE_CALLBACK
|
||||||
|
|
||||||
/* Define if you have the <dirent.h> header file. */
|
/* Define if you have the <dirent.h> header file. */
|
||||||
#undef HAVE_DIRENT_H
|
#undef HAVE_DIRENT_H
|
||||||
|
|
||||||
/* Define if you have the <getopt.h> header file. */
|
/* Define if you have the <getopt.h> header file. */
|
||||||
#undef HAVE_GETOPT_H
|
#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. */
|
/* Define if you have the <grp.h> header file. */
|
||||||
#undef HAVE_GRP_H
|
#undef HAVE_GRP_H
|
||||||
|
|
||||||
|
|
104
INSTALL
104
INSTALL
|
@ -1,5 +1,101 @@
|
||||||
Read the file GNUstep-HOWTO for instructions on how to install the
|
Introduction
|
||||||
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.
|
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
4
README
|
@ -18,8 +18,8 @@ Initial reading
|
||||||
|
|
||||||
The file `NEWS' has the library's feature history.
|
The file `NEWS' has the library's feature history.
|
||||||
|
|
||||||
The file `GNUstep-HOWTO' give instructions for installing the
|
The files `INSTALL' or `GNUstep-HOWTO' (from the core package) gives
|
||||||
library.
|
instructions for installing the library.
|
||||||
|
|
||||||
How can you help?
|
How can you help?
|
||||||
=================
|
=================
|
||||||
|
|
|
@ -234,6 +234,15 @@ ifeq ($(HAVE_LIBXML),1)
|
||||||
BASE_MFILES += GSXML.m
|
BASE_MFILES += GSXML.m
|
||||||
endif
|
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 = \
|
NSVALUE_MFILES = \
|
||||||
NSValue0.m NSValue1.m NSValue2.m NSValue3.m \
|
NSValue0.m NSValue1.m NSValue2.m NSValue3.m \
|
||||||
NSValue4.m
|
NSValue4.m
|
||||||
|
|
697
Source/GSFFCallInvocation.m
Normal file
697
Source/GSFFCallInvocation.m
Normal 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
101
Source/GSFFIInvocation.m
Normal 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
|
||||||
|
|
|
@ -28,6 +28,11 @@
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <base/preface.h>
|
#include <base/preface.h>
|
||||||
#include <mframe.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.
|
* Setup for inline operation of pointer map tables.
|
||||||
|
@ -65,6 +70,7 @@
|
||||||
#include <Foundation/NSPortNameServer.h>
|
#include <Foundation/NSPortNameServer.h>
|
||||||
#include <Foundation/NSNotification.h>
|
#include <Foundation/NSNotification.h>
|
||||||
#include <Foundation/NSDebug.h>
|
#include <Foundation/NSDebug.h>
|
||||||
|
#include <base/GSInvocation.h>
|
||||||
|
|
||||||
#define F_LOCK(X) {NSDebugFLLog(@"GSConnection",@"Lock %@",X);[X lock];}
|
#define F_LOCK(X) {NSDebugFLLog(@"GSConnection",@"Lock %@",X);[X lock];}
|
||||||
#define F_UNLOCK(X) {NSDebugFLLog(@"GSConnection",@"Unlock %@",X);[X unlock];}
|
#define F_UNLOCK(X) {NSDebugFLLog(@"GSConnection",@"Unlock %@",X);[X unlock];}
|
||||||
|
@ -1560,6 +1566,142 @@ static BOOL multi_threaded = NO;
|
||||||
return retframe;
|
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
|
- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target
|
||||||
{
|
{
|
||||||
id op, ip;
|
id op, ip;
|
||||||
|
@ -1811,6 +1953,9 @@ static BOOL multi_threaded = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
[aRmc decodeValueOfObjCType: type at: datum];
|
[aRmc decodeValueOfObjCType: type at: datum];
|
||||||
|
#ifdef USE_FFCALL
|
||||||
|
if (*type == _C_ID)
|
||||||
|
#else
|
||||||
/* -decodeValueOfObjCType: at: malloc's new memory
|
/* -decodeValueOfObjCType: at: malloc's new memory
|
||||||
for char*'s. We need to make sure it gets freed eventually
|
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
|
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)
|
if ((*type == _C_CHARPTR || *type == _C_PTR) && *(void**)datum != 0)
|
||||||
[NSData dataWithBytesNoCopy: *(void**)datum length: 1];
|
[NSData dataWithBytesNoCopy: *(void**)datum length: 1];
|
||||||
else if (*type == _C_ID)
|
else if (*type == _C_ID)
|
||||||
|
#endif
|
||||||
AUTORELEASE(*(id*)datum);
|
AUTORELEASE(*(id*)datum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1871,7 +2017,13 @@ static BOOL multi_threaded = NO;
|
||||||
if (debug_connection > 1)
|
if (debug_connection > 1)
|
||||||
NSLog(@"Handling message from 0x%x", (gsaddr)self);
|
NSLog(@"Handling message from 0x%x", (gsaddr)self);
|
||||||
_reqInCount++; /* Handling an incoming request. */
|
_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);
|
mframe_do_call (forward_type, decoder, encoder);
|
||||||
|
#endif
|
||||||
if (op != nil)
|
if (op != nil)
|
||||||
{
|
{
|
||||||
[self _sendOutRmc: op type: METHOD_REPLY];
|
[self _sendOutRmc: op type: METHOD_REPLY];
|
||||||
|
|
|
@ -31,6 +31,16 @@
|
||||||
#include <Foundation/NSException.h>
|
#include <Foundation/NSException.h>
|
||||||
#include <Foundation/NSObjCRuntime.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 int debug_proxy = 0;
|
||||||
static Class placeHolder = 0;
|
static Class placeHolder = 0;
|
||||||
static Class distantObjectClass = 0;
|
static Class distantObjectClass = 0;
|
||||||
|
@ -482,8 +492,17 @@ enum
|
||||||
*/
|
*/
|
||||||
- (void) forwardInvocation: (NSInvocation*)anInvocation
|
- (void) forwardInvocation: (NSInvocation*)anInvocation
|
||||||
{
|
{
|
||||||
[NSException raise: NSInvalidArgumentException
|
if (debug_proxy)
|
||||||
format: @"Not yet implemented '%s'", sel_get_name(_cmd)];
|
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
|
- (id) initWithCoder: (NSCoder*)aCoder
|
||||||
|
@ -778,6 +797,10 @@ enum
|
||||||
{
|
{
|
||||||
id m;
|
id m;
|
||||||
const char *types;
|
const char *types;
|
||||||
|
#ifdef USE_FFCALL
|
||||||
|
id inv, sig;
|
||||||
|
DO_FORWARD_INVOCATION(_cmd, aSelector);
|
||||||
|
#else
|
||||||
arglist_t args;
|
arglist_t args;
|
||||||
void *retframe;
|
void *retframe;
|
||||||
|
|
||||||
|
@ -792,6 +815,7 @@ enum
|
||||||
args = __builtin_apply_args();
|
args = __builtin_apply_args();
|
||||||
retframe = [self forward: _cmd : args];
|
retframe = [self forward: _cmd : args];
|
||||||
m = retframe_id(retframe);
|
m = retframe_id(retframe);
|
||||||
|
#endif
|
||||||
types = [m methodType];
|
types = [m methodType];
|
||||||
|
|
||||||
return [NSMethodSignature signatureWithObjCTypes: types];
|
return [NSMethodSignature signatureWithObjCTypes: types];
|
||||||
|
@ -901,6 +925,12 @@ static inline BOOL class_is_kind_of (Class self, Class aClassObject)
|
||||||
|
|
||||||
- (BOOL) conformsToProtocol: (Protocol*)aProtocol
|
- (BOOL) conformsToProtocol: (Protocol*)aProtocol
|
||||||
{
|
{
|
||||||
|
#ifdef USE_FFCALL
|
||||||
|
BOOL m;
|
||||||
|
id inv, sig;
|
||||||
|
DO_FORWARD_INVOCATION(_cmd, aProtocol);
|
||||||
|
return m;
|
||||||
|
#else
|
||||||
arglist_t args;
|
arglist_t args;
|
||||||
void *retframe;
|
void *retframe;
|
||||||
|
|
||||||
|
@ -915,10 +945,17 @@ static inline BOOL class_is_kind_of (Class self, Class aClassObject)
|
||||||
args = __builtin_apply_args();
|
args = __builtin_apply_args();
|
||||||
retframe = [self forward: _cmd : args];
|
retframe = [self forward: _cmd : args];
|
||||||
return retframe_bool(retframe);
|
return retframe_bool(retframe);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) respondsToSelector: (SEL)aSelector
|
- (BOOL) respondsToSelector: (SEL)aSelector
|
||||||
{
|
{
|
||||||
|
#ifdef USE_FFCALL
|
||||||
|
BOOL m;
|
||||||
|
id inv, sig;
|
||||||
|
DO_FORWARD_INVOCATION(_cmd, aSelector);
|
||||||
|
return m;
|
||||||
|
#else
|
||||||
arglist_t args;
|
arglist_t args;
|
||||||
void *retframe;
|
void *retframe;
|
||||||
|
|
||||||
|
@ -933,6 +970,7 @@ static inline BOOL class_is_kind_of (Class self, Class aClassObject)
|
||||||
args = __builtin_apply_args();
|
args = __builtin_apply_args();
|
||||||
retframe = [self forward: _cmd : args];
|
retframe = [self forward: _cmd : args];
|
||||||
return retframe_bool(retframe);
|
return retframe_bool(retframe);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id) replacementObjectForCoder: (NSCoder*)aCoder
|
- (id) replacementObjectForCoder: (NSCoder*)aCoder
|
||||||
|
|
|
@ -22,18 +22,114 @@
|
||||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include <Foundation/NSException.h>
|
#include <Foundation/NSException.h>
|
||||||
#include <Foundation/NSCoder.h>
|
#include <Foundation/NSCoder.h>
|
||||||
#include <Foundation/NSInvocation.h>
|
#include <Foundation/NSInvocation.h>
|
||||||
|
#include <base/GSInvocation.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <mframe.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
|
@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
|
+ (NSInvocation*) invocationWithMethodSignature: (NSMethodSignature*)_signature
|
||||||
{
|
{
|
||||||
return AUTORELEASE([[NSInvocation alloc]
|
return AUTORELEASE([[NSInvocation_concrete_class alloc]
|
||||||
initWithMethodSignature: _signature]);
|
initWithMethodSignature: _signature]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,19 +149,28 @@
|
||||||
{
|
{
|
||||||
char *str;
|
char *str;
|
||||||
|
|
||||||
mframe_get_arg(_argframe, &_info[i], &str);
|
_get_arg(self, i-1, &str);
|
||||||
NSZoneFree(NSDefaultMallocZone(), str);
|
NSZoneFree(NSDefaultMallocZone(), str);
|
||||||
}
|
}
|
||||||
else if (*_info[i].type == _C_ID)
|
else if (*_info[i].type == _C_ID)
|
||||||
{
|
{
|
||||||
id obj;
|
id obj;
|
||||||
|
|
||||||
mframe_get_arg(_argframe, &_info[i], &obj);
|
_get_arg(self, i-1, &obj);
|
||||||
RELEASE(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)
|
if (_argframe)
|
||||||
{
|
{
|
||||||
mframe_destroy_argframe([_sig methodType], _argframe);
|
mframe_destroy_argframe([_sig methodType], _argframe);
|
||||||
|
@ -100,8 +205,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
index++; /* Allow offset for return type _info. */
|
_get_arg(self, index, buffer);
|
||||||
mframe_get_arg(_argframe, &_info[index], buffer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,8 +264,8 @@
|
||||||
{
|
{
|
||||||
id old;
|
id old;
|
||||||
|
|
||||||
mframe_get_arg(_argframe, &_info[i], &old);
|
_get_arg(self, index, &old);
|
||||||
mframe_set_arg(_argframe, &_info[i], buffer);
|
_set_arg(self, index, buffer);
|
||||||
IF_NO_GC(RETAIN(*(id*)buffer));
|
IF_NO_GC(RETAIN(*(id*)buffer));
|
||||||
if (old != nil)
|
if (old != nil)
|
||||||
{
|
{
|
||||||
|
@ -173,10 +277,10 @@
|
||||||
char *oldstr;
|
char *oldstr;
|
||||||
char *newstr = *(char**)buffer;
|
char *newstr = *(char**)buffer;
|
||||||
|
|
||||||
mframe_get_arg(_argframe, &_info[i], &oldstr);
|
_get_arg(self, index, &oldstr);
|
||||||
if (newstr == 0)
|
if (newstr == 0)
|
||||||
{
|
{
|
||||||
mframe_set_arg(_argframe, &_info[i], buffer);
|
_set_arg(self, index, buffer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -184,7 +288,7 @@
|
||||||
|
|
||||||
tmp = NSZoneMalloc(NSDefaultMallocZone(), strlen(newstr)+1);
|
tmp = NSZoneMalloc(NSDefaultMallocZone(), strlen(newstr)+1);
|
||||||
strcpy(tmp, newstr);
|
strcpy(tmp, newstr);
|
||||||
mframe_set_arg(_argframe, &_info[i], tmp);
|
_set_arg(self, index, tmp);
|
||||||
}
|
}
|
||||||
if (oldstr != 0)
|
if (oldstr != 0)
|
||||||
{
|
{
|
||||||
|
@ -194,7 +298,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mframe_set_arg(_argframe, &_info[i], buffer);
|
_set_arg(self, index, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,7 +374,7 @@
|
||||||
{
|
{
|
||||||
id old;
|
id old;
|
||||||
|
|
||||||
mframe_get_arg(_argframe, &_info[i], &old);
|
_get_arg(self, i-1, &old);
|
||||||
if (old != nil)
|
if (old != nil)
|
||||||
{
|
{
|
||||||
IF_NO_GC(RETAIN(old));
|
IF_NO_GC(RETAIN(old));
|
||||||
|
@ -280,14 +384,14 @@
|
||||||
{
|
{
|
||||||
char *str;
|
char *str;
|
||||||
|
|
||||||
mframe_get_arg(_argframe, &_info[i], &str);
|
_get_arg(self, i-1, &str);
|
||||||
if (str != 0)
|
if (str != 0)
|
||||||
{
|
{
|
||||||
char *tmp;
|
char *tmp;
|
||||||
|
|
||||||
tmp = NSZoneMalloc(NSDefaultMallocZone(), strlen(str)+1);
|
tmp = NSZoneMalloc(NSDefaultMallocZone(), strlen(str)+1);
|
||||||
strcpy(tmp, str);
|
strcpy(tmp, str);
|
||||||
mframe_set_arg(_argframe, &_info[i], &tmp);
|
_set_arg(self, i-1, &tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,9 +433,8 @@
|
||||||
old_target = RETAIN(_target);
|
old_target = RETAIN(_target);
|
||||||
[self setTarget: anObject];
|
[self setTarget: anObject];
|
||||||
|
|
||||||
mframe_set_arg(_argframe, &_info[1], &_target);
|
_set_arg(self, 0, &_target);
|
||||||
|
_set_arg(self, 1, &_selector);
|
||||||
mframe_set_arg(_argframe, &_info[2], &_selector);
|
|
||||||
|
|
||||||
imp = method_get_imp(object_is_instance(_target) ?
|
imp = method_get_imp(object_is_instance(_target) ?
|
||||||
class_get_instance_method(
|
class_get_instance_method(
|
||||||
|
@ -349,11 +452,20 @@
|
||||||
|
|
||||||
stack_argsize = [_sig frameLength];
|
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);
|
returned = __builtin_apply((void(*)(void))imp, _argframe, stack_argsize);
|
||||||
if (_info[0].size)
|
if (_info[0].size)
|
||||||
{
|
{
|
||||||
mframe_decode_return(_info[0].type, _retval, returned);
|
mframe_decode_return(_info[0].type, _retval, returned);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
_validReturn = YES;
|
_validReturn = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,17 +514,19 @@
|
||||||
const char *type = _info[i].type;
|
const char *type = _info[i].type;
|
||||||
void *datum;
|
void *datum;
|
||||||
|
|
||||||
datum = mframe_arg_addr(_argframe, &_info[i]);
|
datum = _arg_addr(self, i-1);
|
||||||
|
|
||||||
if (*type == _C_ID)
|
if (*type == _C_ID)
|
||||||
{
|
{
|
||||||
[aCoder encodeObject: *(id*)datum];
|
[aCoder encodeObject: *(id*)datum];
|
||||||
}
|
}
|
||||||
|
#if !defined(USE_LIBFFI) && !defined(USE_FFCALL)
|
||||||
#if MFRAME_STRUCT_BYREF
|
#if MFRAME_STRUCT_BYREF
|
||||||
else if (*type == _C_STRUCT_B || *type == _C_UNION_B || *type == _C_ARY_B)
|
else if (*type == _C_STRUCT_B || *type == _C_UNION_B || *type == _C_ARY_B)
|
||||||
{
|
{
|
||||||
[aCoder encodeValueOfObjCType: type at: *(void**)datum];
|
[aCoder encodeValueOfObjCType: type at: *(void**)datum];
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -439,7 +553,10 @@
|
||||||
[aCoder decodeValueOfObjCType: @encode(char*) at: &types];
|
[aCoder decodeValueOfObjCType: @encode(char*) at: &types];
|
||||||
newSig = [NSMethodSignature signatureWithObjCTypes: types];
|
newSig = [NSMethodSignature signatureWithObjCTypes: types];
|
||||||
NSZoneFree(NSDefaultMallocZone(), (void*)types);
|
NSZoneFree(NSDefaultMallocZone(), (void*)types);
|
||||||
self = [self initWithMethodSignature: newSig];
|
|
||||||
|
RELEASE(self);
|
||||||
|
self = [NSInvocation invocationWithMethodSignature: newSig];
|
||||||
|
RETAIN(self);
|
||||||
|
|
||||||
[aCoder decodeValueOfObjCType: @encode(id) at: &_target];
|
[aCoder decodeValueOfObjCType: @encode(id) at: &_target];
|
||||||
|
|
||||||
|
@ -447,7 +564,8 @@
|
||||||
|
|
||||||
for (i = 3; i <= _numArgs; i++)
|
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
|
#if MFRAME_STRUCT_BYREF
|
||||||
{
|
{
|
||||||
const char *t = _info[i].type;
|
const char *t = _info[i].type;
|
||||||
|
@ -457,6 +575,7 @@
|
||||||
datum = *(void**)datum;
|
datum = *(void**)datum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
[aCoder decodeValueOfObjCType: _info[i].type at: datum];
|
[aCoder decodeValueOfObjCType: _info[i].type at: datum];
|
||||||
}
|
}
|
||||||
|
@ -472,12 +591,144 @@
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation NSInvocation (GNUstep)
|
@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
|
- (id) initWithArgframe: (arglist_t)frame selector: (SEL)aSelector
|
||||||
{
|
{
|
||||||
const char *types;
|
const char *types;
|
||||||
|
@ -532,114 +783,8 @@
|
||||||
return self;
|
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
|
- (void*) returnFrame: (arglist_t)argFrame
|
||||||
{
|
{
|
||||||
return mframe_handle_return(_info[0].type, _retval, argFrame);
|
return mframe_handle_return(_info[0].type, _retval, argFrame);
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation NSInvocation (BackwardCompatibility)
|
|
||||||
|
|
||||||
- (void) invokeWithObject: (id)obj
|
|
||||||
{
|
|
||||||
[self invokeWithTarget: (id)obj];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
55
Source/callframe.h
Normal file
55
Source/callframe.h
Normal 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
766
Source/callframe.m
Normal 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
50
Source/cifframe.h
Normal 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
847
Source/cifframe.m
Normal 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);
|
||||||
|
}
|
|
@ -88,7 +88,7 @@ int con_data (id prx)
|
||||||
printf(send, var); \
|
printf(send, var); \
|
||||||
varr = [prx msg1 var]; \
|
varr = [prx msg1 var]; \
|
||||||
printf(got, varr); \
|
printf(got, varr); \
|
||||||
if (varr != var+ADD_CONST) \
|
if (varr != (var+ADD_CONST)) \
|
||||||
printf(" *** ERROR ***\n"); \
|
printf(" *** ERROR ***\n"); \
|
||||||
else \
|
else \
|
||||||
printf(" ...ok\n"); \
|
printf(" ...ok\n"); \
|
||||||
|
@ -96,20 +96,37 @@ int con_data (id prx)
|
||||||
printf(sendp, varr); \
|
printf(sendp, varr); \
|
||||||
[prx msg2 &varr]; \
|
[prx msg2 &varr]; \
|
||||||
printf(got, varr); \
|
printf(got, varr); \
|
||||||
if (varr != var+ADD_CONST) \
|
if (varr != (var+ADD_CONST)) \
|
||||||
printf(" *** ERROR ***\n"); \
|
printf(" *** ERROR ***\n"); \
|
||||||
else \
|
else \
|
||||||
printf(" ...ok\n");
|
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:)
|
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:)
|
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:)
|
s, sr, 23, sendShort:, getShort:)
|
||||||
|
|
||||||
TEST_CALL("Int:\n", " sending %d", " got %d", " sending ptr to %d",
|
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",
|
TEST_CALL("Long:\n", " sending %ld", " got %ld", " sending ptr to %ld",
|
||||||
l, lr, 23, sendLong:, getLong:)
|
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:)
|
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:)
|
dbl, dblr, 23.2, sendDouble:, getDouble:)
|
||||||
|
|
||||||
flt = 2.718;
|
flt = 2.718;
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
|
|
||||||
- (BOOL) sendBoolean: (BOOL)b
|
- (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);
|
fflush(stdout);
|
||||||
return !b;
|
return !b;
|
||||||
}
|
}
|
||||||
|
@ -82,16 +82,18 @@
|
||||||
/* This causes problems, because the runtime encodes this as "*", a string! */
|
/* This causes problems, because the runtime encodes this as "*", a string! */
|
||||||
- (void) getBoolean: (BOOL*)bp
|
- (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);
|
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) sendUChar: (unsigned char)num
|
||||||
{
|
{
|
||||||
unsigned char rnum = num + ADD_CONST;
|
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);
|
fflush(stdout);
|
||||||
return rnum;
|
return rnum;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +102,8 @@
|
||||||
- (void) getUChar: (unsigned char *)num
|
- (void) getUChar: (unsigned char *)num
|
||||||
{
|
{
|
||||||
unsigned char rnum = *num + ADD_CONST;
|
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;
|
*num = rnum;
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
@ -108,7 +111,8 @@
|
||||||
- (char) sendChar: (char)num
|
- (char) sendChar: (char)num
|
||||||
{
|
{
|
||||||
char rnum = num + ADD_CONST;
|
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);
|
fflush(stdout);
|
||||||
return rnum;
|
return rnum;
|
||||||
}
|
}
|
||||||
|
@ -116,7 +120,8 @@
|
||||||
- (void) getChar: (char *)num
|
- (void) getChar: (char *)num
|
||||||
{
|
{
|
||||||
char rnum = *num + ADD_CONST;
|
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;
|
*num = rnum;
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
@ -124,7 +129,8 @@
|
||||||
- (short) sendShort: (short)num
|
- (short) sendShort: (short)num
|
||||||
{
|
{
|
||||||
short rnum = num + ADD_CONST;
|
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);
|
fflush(stdout);
|
||||||
return rnum;
|
return rnum;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +138,8 @@
|
||||||
- (void) getShort: (short *)num
|
- (void) getShort: (short *)num
|
||||||
{
|
{
|
||||||
short rnum = *num + ADD_CONST;
|
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;
|
*num = rnum;
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,3 +138,10 @@
|
||||||
|
|
||||||
/* Define if your system has a /proc/self/exe symlink to the executable */
|
/* Define if your system has a /proc/self/exe symlink to the executable */
|
||||||
#undef HAVE_PROC_FS_EXE_LINK
|
#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
|
||||||
|
|
||||||
|
|
|
@ -8,3 +8,4 @@ DYNAMIC_LINKER=@DYNAMIC_LINKER@
|
||||||
|
|
||||||
HAVE_LIBXML=@HAVE_LIBXML@
|
HAVE_LIBXML=@HAVE_LIBXML@
|
||||||
|
|
||||||
|
WITH_FFI=@WITH_FFI@
|
||||||
|
|
37
configure.in
37
configure.in
|
@ -736,7 +736,7 @@ fi
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
AC_MSG_CHECKING("use of pass-through arguments")
|
AC_MSG_CHECKING("use of pass-through arguments")
|
||||||
AC_ARG_ENABLE(pass-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)
|
enable_pass_arguments=no)
|
||||||
|
|
||||||
case "$target_os" in
|
case "$target_os" in
|
||||||
|
@ -752,7 +752,7 @@ AC_MSG_RESULT($enable_pass_arguments)
|
||||||
|
|
||||||
AC_MSG_CHECKING("use of fake-main definition")
|
AC_MSG_CHECKING("use of fake-main definition")
|
||||||
AC_ARG_ENABLE(fake-main,
|
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)
|
enable_fake_main=no)
|
||||||
|
|
||||||
if test "$enable_pass_arguments" = "no"; then
|
if test "$enable_pass_arguments" = "no"; then
|
||||||
|
@ -785,6 +785,39 @@ esac
|
||||||
AC_SUBST(GS_FAKE_MAIN)
|
AC_SUBST(GS_FAKE_MAIN)
|
||||||
AC_MSG_RESULT($enable_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.
|
# Check recent libxlm for Properytlists, GSXML, GSDoc etc.
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue