diff --git a/ChangeLog b/ChangeLog index 8bb1c87cc..c86ed6d7d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2000-07-04 Richard Frith-Macdonald + + * Makefile.preamble: define GS_NEW_DO to specify whether the + new or the old DO code is to be used. Change the define and + rebuild the library for the new DO code. + Advantages of new code - + MacOS-X compatibly + Should work between different processor architectures and word sizes + Might work for inter-thread communication + Approx 25% faster + Permit packet authentication/encryption + Disadvantages - + not well tested - development version + removed many old methods. + * Headers/gnustep/base/GSConnection.h: removed - merged into + NSConnection.h + * Headers/gnustep/base/GSPortCoder.h: removed - merged into + NSPortCoder.h + * Source/GSConnection.m: removed - merged into NSConnection.m + * Source/GSPortCoder.m: removed - merged into NSPortCoder.m + * Source/NSPortNameServer.m: Use GS_NEW_DO define to determine + default port class + 2000-07-04 Richard Frith-Macdonald * Source/GSConnection.m: Improved coder caching and added code for diff --git a/Headers/gnustep/base/GSConnection.h b/Headers/gnustep/base/GSConnection.h deleted file mode 100644 index 1c79f60a9..000000000 --- a/Headers/gnustep/base/GSConnection.h +++ /dev/null @@ -1,230 +0,0 @@ -/* Interface for GNU Objective-C version of NSConnection - Copyright (C) 1997,2000 Free Software Foundation, Inc. - - Original by: Andrew Kachites McCallum - Version for OPENSTEP by: Richard Frith-Macdonald - Created: August 1997, updated June 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 __NSConnection_h_GNUSTEP_BASE_INCLUDE -#define __NSConnection_h_GNUSTEP_BASE_INCLUDE - -#include -#include -#include -#include -#include -#include - -@class NSDistantObject; -@class NSPort; -@class NSPortNameServer; -@class NSData; - -/* - * Keys for the NSDictionary returned by [NSConnection -statistics] - */ -/* These in OPENSTEP 4.2 */ -GS_EXPORT NSString *NSConnectionRepliesReceived; -GS_EXPORT NSString *NSConnectionRepliesSent; -GS_EXPORT NSString *NSConnectionRequestsReceived; -GS_EXPORT NSString *NSConnectionRequestsSent; -/* These Are GNUstep extras */ -GS_EXPORT NSString *NSConnectionLocalCount; /* Objects sent out */ -GS_EXPORT NSString *NSConnectionProxyCount; /* Objects received */ - - - -/* - * NSConnection class interface. - * - * A few methods are in the specification but not yet implemented. - */ -@interface NSConnection : NSObject -{ -@private - BOOL _isValid; - BOOL _independentQueueing; - BOOL _authenticateIn; - BOOL _authenticateOut; - BOOL _multipleThreads; - NSPort *_receivePort; - NSPort *_sendPort; - unsigned _requestDepth; - unsigned _messageCount; - unsigned _reqOutCount; - unsigned _reqInCount; - unsigned _repOutCount; - unsigned _repInCount; - NSMapTable *_localObjects; - NSMapTable *_localTargets; - NSMapTable *_remoteProxies; - NSMapTable *_replyMap; - NSTimeInterval _replyTimeout; - NSTimeInterval _requestTimeout; - NSMutableArray *_requestModes; - NSMutableArray *_runLoops; - NSMutableArray *_requestQueue; - id _delegate; - NSRecursiveLock *_refGate; - NSMutableArray *_cachedDecoders; - NSMutableArray *_cachedEncoders; -} - -+ (NSArray*) allConnections; -+ (NSConnection*) connectionWithReceivePort: (NSPort*)r - sendPort: (NSPort*)s; -+ (NSConnection*) connectionWithRegisteredName: (NSString*)n - host: (NSString*)h; -+ (NSConnection*) connectionWithRegisteredName: (NSString*)n - host: (NSString*)h - usingNameServer: (NSPortNameServer*)s; -+ (id) currentConversation; -+ (NSConnection*) defaultConnection; -+ (NSDistantObject*) rootProxyForConnectionWithRegisteredName: (NSString*)name - host: (NSString*)host; -+ (NSDistantObject*) rootProxyForConnectionWithRegisteredName: (NSString*)name - host: (NSString*)host usingNameServer: (NSPortNameServer*)s; - - -- (void) addRequestMode: (NSString*)mode; -- (void) addRunLoop: (NSRunLoop*)runloop; -- (id) delegate; -- (void) enableMultipleThreads; -- (BOOL) independentConversationQueueing; -- (id) initWithReceivePort: (NSPort*)r - sendPort: (NSPort*)s; -- (void) invalidate; -- (BOOL) isValid; -- (NSArray*)localObjects; -- (BOOL) multipleThreadsEnabled; -- (NSPort*) receivePort; -- (BOOL) registerName: (NSString*)name; -- (BOOL) registerName: (NSString*)name withNameServer: (NSPortNameServer*)svr; -- (NSArray*) remoteObjects; -- (void) removeRequestMode: (NSString*)mode; -- (void) removeRunLoop: (NSRunLoop *)runloop; -- (NSTimeInterval) replyTimeout; -- (NSArray*) requestModes; -- (NSTimeInterval) requestTimeout; -- (id) rootObject; -- (NSDistantObject*) rootProxy; -- (void) runInNewThread; -- (NSPort*) sendPort; -- (void) setDelegate: anObj; -- (void) setIndependentConversationQueueing: (BOOL)flag; -- (void) setReplyTimeout: (NSTimeInterval)seconds; -- (void) setRequestMode: (NSString*)mode; -- (void) setRequestTimeout: (NSTimeInterval)seconds; -- (void) setRootObject: anObj; -- (NSDictionary*) statistics; -@end - - -/* - * This catagory contains legacy methods from the original GNU 'Connection' - * class, and useful extensions to NSConnection. - */ -@interface NSConnection (GNUstepExtensions) - -+ (NSConnection*) newRegisteringAtName: (NSString*)n - withRootObject: (id)anObject; - -- (void) gcFinalize; - -- (retval_t) forwardForProxy: (NSDistantObject*)object - selector: (SEL)sel - argFrame: (arglist_t)frame; -- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target; - -@end - -GS_EXPORT NSString *ConnectionBecameInvalidNotification; - -@interface Object (NSConnectionDelegate) -/* - * This method may be used to ask a delegates permission to create - * a new connection from the old one. - * This method should be implemented in preference to the - * [makeNewConnection:sender:] which is obsolete. - */ -- (BOOL) connection: (NSConnection*)parent - shouldMakeNewConnection: (NSConnection*)newConnection; - -/* - * This is the old way of doing the same thing as - * [connection:shouldMakeNewConnection:] - * It is obsolete - don't use it. - */ -- (BOOL) makeNewConnection: (NSConnection*)newConnection - sender: (NSConnection*)parent; - -/* - * If the delegate responds to this method, it will be used to ask the - * delegate's permission to establish a new connection from the old one. - * Often this is used so that the delegate can register for invalidation - * notification on new child connections. - * This is a GNUstep extension - * Normally return newConn. - */ -- (NSConnection*) connection: (NSConnection*)ancestorConn - didConnect: (NSConnection*)newConn; - - -- (BOOL) authenticateComponents: (NSArray*)components - withData: (NSData*)authenticationData; -- (NSData*) authenticationDataForComponents: (NSArray*)components; - -@end - -@interface Object (NSPortCoder) -- (Class) classForPortCoder; -/* - * Must return the class that will be created on the remote side - * of the connection. If the class to be created is not the same - * as that of the object returned by replacementObjectForPortCoder: - * then the class must be capable of recognising the object it - * actually gets in its initWithCoder: method. - * The default operation is to return NSDistantObject unless the - * object is being sent bycopy, in which case the objects actual - * class is returned. To force bycopy operation the object should - * return its own class. - */ -- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder; -/* - * This message is sent to an object about to be encoded for sending - * over the wire. The default action is to return an NSDistantObject - * which is a local proxy for the object unless the object is being - * sent bycopy, in which case the actual object is returned. - * To force bycopy, an object should return itsself. - */ - -@end - -#define CONNECTION_DEFAULT_TIMEOUT 15.0 /* in seconds */ - -/* - * NSRunLoop mode, NSNotification name and NSException strings. - */ -GS_EXPORT NSString *NSConnectionReplyMode; -GS_EXPORT NSString *NSConnectionDidDieNotification; -GS_EXPORT NSString *NSConnectionDidInitializeNotification; /* OPENSTEP */ -GS_EXPORT NSString *NSFailedAuthenticationException; /* MacOS-X */ - -#endif /* __NSConnection_h_GNUSTEP_BASE_INCLUDE */ diff --git a/Headers/gnustep/base/GSPortCoder.h b/Headers/gnustep/base/GSPortCoder.h deleted file mode 100644 index f21645bd4..000000000 --- a/Headers/gnustep/base/GSPortCoder.h +++ /dev/null @@ -1,102 +0,0 @@ -/* Interface for NSPortCoder object for distributed objects - Copyright (C) 2000 Free Software Foundation, Inc. - - Written by: Richard Frith-Macdonald - Date: June 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 __NSPortCoder_h -#define __NSPortCoder_h - -#include - -@class NSConnection; -@class NSPort; - -@interface NSPortCoder : NSCoder -{ -@private - NSMutableArray *_comp; - NSConnection *_conn; - BOOL _is_by_copy; - BOOL _is_by_ref; -// Encoding - BOOL _encodingRoot; - BOOL _initialPass; - id _dst; /* Serialization destination. */ - IMP _eSerImp; /* Method to serialize with. */ - IMP _eTagImp; /* Serialize a type tag. */ - IMP _xRefImp; /* Serialize a crossref. */ - IMP _eObjImp; /* Method to encode an id. */ - IMP _eValImp; /* Method to encode others. */ -#ifndef _IN_PORT_CODER_M -#define GSIMapTable void* -#endif - GSIMapTable _clsMap; /* Class cross references. */ - GSIMapTable _cIdMap; /* Conditionally coded. */ - GSIMapTable _uIdMap; /* Unconditionally coded. */ - GSIMapTable _ptrMap; /* Constant pointers. */ -#ifndef _IN_PORT_CODER_M -#undef GSIMapTable -#endif - unsigned _xRefC; /* Counter for cross-reference. */ - unsigned _xRefO; /* Counter for cross-reference. */ - unsigned _xRefP; /* Counter for cross-reference. */ -// Decoding - id _src; /* Deserialization source. */ - IMP _dDesImp; /* Method to deserialize with. */ - void (*_dTagImp)(id,SEL,unsigned char*,unsigned*,unsigned*); - IMP _dValImp; /* Method to decode data with. */ -#ifndef _IN_PORT_CODER_M -#define GSIArray void* -#endif - GSIArray _clsAry; /* Class crossreference map. */ - GSIArray _objAry; /* Object crossreference map. */ - GSIArray _ptrAry; /* Pointer crossreference map. */ -#ifndef _IN_PORT_CODER_M -#undef GSIArray -#endif - NSMutableDictionary *_cInfo; /* Class version information. */ - unsigned _cursor; /* Position in data buffer. */ - unsigned _version; /* Version of archiver used. */ - NSZone *_zone; /* Zone for allocating objs. */ -} - -+ (NSPortCoder*) portCoderWithReceivePort: (NSPort*)recv - sendPort: (NSPort*)send - components: (NSArray*)comp; -- (id) initWithReceivePort: (NSPort*)recv - sendPort: (NSPort*)send - components: (NSArray*)comp; - -- (NSConnection*) connection; -- (NSPort*) decodePortObject; -- (void) dispatch; -- (void) encodePortObject: (NSPort*)aPort; -- (BOOL) isBycopy; -- (BOOL) isByref; - -@end - -@interface NSPortCoder (Private) -- (NSMutableArray*) _components; -@end - - -#endif /* __NSPortCoder_h */ diff --git a/Headers/gnustep/base/NSConnection.h b/Headers/gnustep/base/NSConnection.h index b5ac50f14..ae082989d 100644 --- a/Headers/gnustep/base/NSConnection.h +++ b/Headers/gnustep/base/NSConnection.h @@ -1,3 +1,235 @@ +#if GS_NEW_DO +/* Interface for GNU Objective-C version of NSConnection + Copyright (C) 1997,2000 Free Software Foundation, Inc. + + Original by: Andrew Kachites McCallum + Version for OPENSTEP by: Richard Frith-Macdonald + Created: August 1997, updated June 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 __NSConnection_h_GNUSTEP_BASE_INCLUDE +#define __NSConnection_h_GNUSTEP_BASE_INCLUDE + +#include +#include +#include +#include +#include +#include + +@class NSDistantObject; +@class NSPort; +@class NSPortNameServer; +@class NSData; + +/* + * Keys for the NSDictionary returned by [NSConnection -statistics] + */ +/* These in OPENSTEP 4.2 */ +GS_EXPORT NSString *NSConnectionRepliesReceived; +GS_EXPORT NSString *NSConnectionRepliesSent; +GS_EXPORT NSString *NSConnectionRequestsReceived; +GS_EXPORT NSString *NSConnectionRequestsSent; +/* These Are GNUstep extras */ +GS_EXPORT NSString *NSConnectionLocalCount; /* Objects sent out */ +GS_EXPORT NSString *NSConnectionProxyCount; /* Objects received */ + + + +/* + * NSConnection class interface. + * + * A few methods are in the specification but not yet implemented. + */ +@interface NSConnection : NSObject +{ +@private + BOOL _isValid; + BOOL _independentQueueing; + BOOL _authenticateIn; + BOOL _authenticateOut; + BOOL _multipleThreads; + NSPort *_receivePort; + NSPort *_sendPort; + unsigned _requestDepth; + unsigned _messageCount; + unsigned _reqOutCount; + unsigned _reqInCount; + unsigned _repOutCount; + unsigned _repInCount; + NSMapTable *_localObjects; + NSMapTable *_localTargets; + NSMapTable *_remoteProxies; + NSMapTable *_replyMap; + NSTimeInterval _replyTimeout; + NSTimeInterval _requestTimeout; + NSMutableArray *_requestModes; + NSMutableArray *_runLoops; + NSMutableArray *_requestQueue; + id _delegate; + NSRecursiveLock *_refGate; + NSMutableArray *_cachedDecoders; + NSMutableArray *_cachedEncoders; +} + ++ (NSArray*) allConnections; ++ (NSConnection*) connectionWithReceivePort: (NSPort*)r + sendPort: (NSPort*)s; ++ (NSConnection*) connectionWithRegisteredName: (NSString*)n + host: (NSString*)h; ++ (NSConnection*) connectionWithRegisteredName: (NSString*)n + host: (NSString*)h + usingNameServer: (NSPortNameServer*)s; ++ (id) currentConversation; ++ (NSConnection*) defaultConnection; ++ (NSDistantObject*) rootProxyForConnectionWithRegisteredName: (NSString*)name + host: (NSString*)host; ++ (NSDistantObject*) rootProxyForConnectionWithRegisteredName: (NSString*)name + host: (NSString*)host usingNameServer: (NSPortNameServer*)s; + + +- (void) addRequestMode: (NSString*)mode; +- (void) addRunLoop: (NSRunLoop*)runloop; +- (id) delegate; +- (void) enableMultipleThreads; +- (BOOL) independentConversationQueueing; +- (id) initWithReceivePort: (NSPort*)r + sendPort: (NSPort*)s; +- (void) invalidate; +- (BOOL) isValid; +- (NSArray*)localObjects; +- (BOOL) multipleThreadsEnabled; +- (NSPort*) receivePort; +- (BOOL) registerName: (NSString*)name; +- (BOOL) registerName: (NSString*)name withNameServer: (NSPortNameServer*)svr; +- (NSArray*) remoteObjects; +- (void) removeRequestMode: (NSString*)mode; +- (void) removeRunLoop: (NSRunLoop *)runloop; +- (NSTimeInterval) replyTimeout; +- (NSArray*) requestModes; +- (NSTimeInterval) requestTimeout; +- (id) rootObject; +- (NSDistantObject*) rootProxy; +- (void) runInNewThread; +- (NSPort*) sendPort; +- (void) setDelegate: anObj; +- (void) setIndependentConversationQueueing: (BOOL)flag; +- (void) setReplyTimeout: (NSTimeInterval)seconds; +- (void) setRequestMode: (NSString*)mode; +- (void) setRequestTimeout: (NSTimeInterval)seconds; +- (void) setRootObject: anObj; +- (NSDictionary*) statistics; +@end + + +/* + * This catagory contains legacy methods from the original GNU 'Connection' + * class, and useful extensions to NSConnection. + */ +@interface NSConnection (GNUstepExtensions) + ++ (NSConnection*) newRegisteringAtName: (NSString*)n + withRootObject: (id)anObject; + +- (void) gcFinalize; + +- (retval_t) forwardForProxy: (NSDistantObject*)object + selector: (SEL)sel + argFrame: (arglist_t)frame; +- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target; + +@end + +GS_EXPORT NSString *ConnectionBecameInvalidNotification; + +@interface Object (NSConnectionDelegate) +/* + * This method may be used to ask a delegates permission to create + * a new connection from the old one. + * This method should be implemented in preference to the + * [makeNewConnection:sender:] which is obsolete. + */ +- (BOOL) connection: (NSConnection*)parent + shouldMakeNewConnection: (NSConnection*)newConnection; + +/* + * This is the old way of doing the same thing as + * [connection:shouldMakeNewConnection:] + * It is obsolete - don't use it. + */ +- (BOOL) makeNewConnection: (NSConnection*)newConnection + sender: (NSConnection*)parent; + +/* + * If the delegate responds to this method, it will be used to ask the + * delegate's permission to establish a new connection from the old one. + * Often this is used so that the delegate can register for invalidation + * notification on new child connections. + * This is a GNUstep extension + * Normally return newConn. + */ +- (NSConnection*) connection: (NSConnection*)ancestorConn + didConnect: (NSConnection*)newConn; + + +- (BOOL) authenticateComponents: (NSArray*)components + withData: (NSData*)authenticationData; +- (NSData*) authenticationDataForComponents: (NSArray*)components; + +@end + +@interface Object (NSPortCoder) +- (Class) classForPortCoder; +/* + * Must return the class that will be created on the remote side + * of the connection. If the class to be created is not the same + * as that of the object returned by replacementObjectForPortCoder: + * then the class must be capable of recognising the object it + * actually gets in its initWithCoder: method. + * The default operation is to return NSDistantObject unless the + * object is being sent bycopy, in which case the objects actual + * class is returned. To force bycopy operation the object should + * return its own class. + */ +- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder; +/* + * This message is sent to an object about to be encoded for sending + * over the wire. The default action is to return an NSDistantObject + * which is a local proxy for the object unless the object is being + * sent bycopy, in which case the actual object is returned. + * To force bycopy, an object should return itsself. + */ + +@end + +#define CONNECTION_DEFAULT_TIMEOUT 15.0 /* in seconds */ + +/* + * NSRunLoop mode, NSNotification name and NSException strings. + */ +GS_EXPORT NSString *NSConnectionReplyMode; +GS_EXPORT NSString *NSConnectionDidDieNotification; +GS_EXPORT NSString *NSConnectionDidInitializeNotification; /* OPENSTEP */ +GS_EXPORT NSString *NSFailedAuthenticationException; /* MacOS-X */ + +#endif /* __NSConnection_h_GNUSTEP_BASE_INCLUDE */ +#else /* Interface for GNU Objective-C version of NSConnection Copyright (C) 1997 Free Software Foundation, Inc. @@ -311,3 +543,4 @@ GS_EXPORT NSString *NSConnectionDidInitializeNotification; /* OPENSTEP */ #define ConnectionWasCreatedNotification NSConnectionDidInitializeNotification #endif /* __NSConnection_h_GNUSTEP_BASE_INCLUDE */ +#endif diff --git a/Headers/gnustep/base/NSObject.h b/Headers/gnustep/base/NSObject.h index 89c831ecc..9bc876db5 100644 --- a/Headers/gnustep/base/NSObject.h +++ b/Headers/gnustep/base/NSObject.h @@ -95,6 +95,9 @@ Class isa; } +#if GS_WITH_GC ++ (BOOL) requiresTypedMemory; +#endif + (void) initialize; + (id) allocWithZone: (NSZone*)z; + (id) alloc; @@ -132,7 +135,7 @@ - (id) replacementObjectForPortCoder: (NSPortCoder*)anEncoder; -+ setVersion: (int)aVersion; ++ (id) setVersion: (int)aVersion; + (int) version; @end diff --git a/Headers/gnustep/base/NSPortCoder.h b/Headers/gnustep/base/NSPortCoder.h index b65e73685..0a5c2f853 100644 --- a/Headers/gnustep/base/NSPortCoder.h +++ b/Headers/gnustep/base/NSPortCoder.h @@ -1,3 +1,109 @@ +#if GS_NEW_DO +/* Interface for NSPortCoder object for distributed objects + Copyright (C) 2000 Free Software Foundation, Inc. + + Written by: Richard Frith-Macdonald + Date: June 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 __NSPortCoder_h +#define __NSPortCoder_h + +#include + +@class NSMutableArray; +@class NSMutableDictionary; +@class NSConnection; +@class NSPort; + +@interface NSPortCoder : NSCoder +{ +@private + NSMutableArray *_comp; + NSConnection *_conn; + BOOL _is_by_copy; + BOOL _is_by_ref; +// Encoding + BOOL _encodingRoot; + BOOL _initialPass; + id _dst; /* Serialization destination. */ + IMP _eSerImp; /* Method to serialize with. */ + IMP _eTagImp; /* Serialize a type tag. */ + IMP _xRefImp; /* Serialize a crossref. */ + IMP _eObjImp; /* Method to encode an id. */ + IMP _eValImp; /* Method to encode others. */ +#ifndef _IN_PORT_CODER_M +#define GSIMapTable void* +#endif + GSIMapTable _clsMap; /* Class cross references. */ + GSIMapTable _cIdMap; /* Conditionally coded. */ + GSIMapTable _uIdMap; /* Unconditionally coded. */ + GSIMapTable _ptrMap; /* Constant pointers. */ +#ifndef _IN_PORT_CODER_M +#undef GSIMapTable +#endif + unsigned _xRefC; /* Counter for cross-reference. */ + unsigned _xRefO; /* Counter for cross-reference. */ + unsigned _xRefP; /* Counter for cross-reference. */ +// Decoding + id _src; /* Deserialization source. */ + IMP _dDesImp; /* Method to deserialize with. */ + void (*_dTagImp)(id,SEL,unsigned char*,unsigned*,unsigned*); + IMP _dValImp; /* Method to decode data with. */ +#ifndef _IN_PORT_CODER_M +#define GSIArray void* +#endif + GSIArray _clsAry; /* Class crossreference map. */ + GSIArray _objAry; /* Object crossreference map. */ + GSIArray _ptrAry; /* Pointer crossreference map. */ +#ifndef _IN_PORT_CODER_M +#undef GSIArray +#endif + NSMutableDictionary *_cInfo; /* Class version information. */ + unsigned _cursor; /* Position in data buffer. */ + unsigned _version; /* Version of archiver used. */ + NSZone *_zone; /* Zone for allocating objs. */ +} + ++ (NSPortCoder*) portCoderWithReceivePort: (NSPort*)recv + sendPort: (NSPort*)send + components: (NSArray*)comp; +- (id) initWithReceivePort: (NSPort*)recv + sendPort: (NSPort*)send + components: (NSArray*)comp; + +- (NSConnection*) connection; +- (NSPort*) decodePortObject; +- (void) dispatch; +- (void) encodePortObject: (NSPort*)aPort; +- (BOOL) isBycopy; +- (BOOL) isByref; + +@end + +@interface NSPortCoder (Private) +- (NSMutableArray*) _components; +@end + + +#endif /* __NSPortCoder_h */ +#else /* Interface for NSPortCoder object for distributed objects Copyright (C) 1997 Free Software Foundation, Inc. @@ -43,3 +149,4 @@ #endif /* __NSPortCoder_h */ +#endif diff --git a/Source/GSConnection.m b/Source/GSConnection.m deleted file mode 100644 index e78cbf8cf..000000000 --- a/Source/GSConnection.m +++ /dev/null @@ -1,2443 +0,0 @@ -/* Implementation of connection object for remote object messaging - Copyright (C) 1994, 1995, 1996, 1997, 2000 Free Software Foundation, Inc. - - Created by: Andrew Kachites McCallum - Date: July 1994 - Minor rewrite for OPENSTEP by: Richard Frith-Macdonald - Date: August 1997 - Major rewrite for MACOSX by: Richard Frith-Macdonald - Date: 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 -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define F_LOCK(X) {NSDebugFLLog(@"GSConnection",@"Lock %@",X);[X lock];} -#define F_UNLOCK(X) {NSDebugFLLog(@"GSConnection",@"Unlock %@",X);[X unlock];} -#define M_LOCK(X) {NSDebugMLLog(@"GSConnection",@"Lock %@",X);[X lock];} -#define M_UNLOCK(X) {NSDebugMLLog(@"GSConnection",@"Unlock %@",X);[X unlock];} - -static Class connectionClass; -static Class dateClass; -static Class distantObjectClass; -static Class portCoderClass; -static Class runLoopClass; - -static NSString* -stringFromMsgType(int type) -{ - switch (type) - { - case METHOD_REQUEST: - return @"method request"; - case METHOD_REPLY: - return @"method reply"; - case ROOTPROXY_REQUEST: - return @"root proxy request"; - case ROOTPROXY_REPLY: - return @"root proxy reply"; - case CONNECTION_SHUTDOWN: - return @"connection shutdown"; - case METHODTYPE_REQUEST: - return @"methodtype request"; - case METHODTYPE_REPLY: - return @"methodtype reply"; - case PROXY_RELEASE: - return @"proxy release"; - case PROXY_RETAIN: - return @"proxy retain"; - case RETAIN_REPLY: - return @"retain replay"; - default: - return @"unknown operation type!"; - } -} - -@interface NSDistantObject (NSConnection) -- (id) localForProxy; -- (void) setProxyTarget: (unsigned)target; -- (unsigned) targetForProxy; -@end - -@implementation NSDistantObject (NSConnection) -- (id) localForProxy -{ - return _object; -} -- (void) setProxyTarget: (unsigned)target -{ - _handle = target; -} -- (unsigned) targetForProxy -{ - return _handle; -} -@end - -/* - * GSLocalCounter is a trivial class to keep track of how - * many different connections a particular local object is vended - * over. This is required so that we know when to remove an object - * from the global list when it is removed from the list of objects - * vended on a particular connection. - */ -@interface GSLocalCounter : NSObject -{ -@public - unsigned ref; - unsigned target; - id object; -} -+ (id) newWithObject: (id)ob; -@end - -@implementation GSLocalCounter - -static unsigned local_object_counter = 0; - -+ (id) newWithObject: (id)obj -{ - GSLocalCounter *counter; - - counter = (GSLocalCounter*)NSAllocateObject(self, 0, NSDefaultMallocZone()); - counter->ref = 1; - counter->object = RETAIN(obj); - counter->target = ++local_object_counter; - return counter; -} -- (void) dealloc -{ - RELEASE(object); - NSDeallocateObject(self); -} -@end - - - -/* - * CachedLocalObject is a trivial class to keep track of how - * many different connections a particular local object is vended - * over. This is required so that we know when to remove an object - * from the global list when it is removed from the list of objects - * vended on a particular connection. - */ -@interface CachedLocalObject : NSObject -{ - id obj; - int time; -} -- (BOOL)countdown; -- (id) obj; -+ (id) newWithObject: (id)o time: (int)t; -@end - -@implementation CachedLocalObject - -+ (id) newWithObject: (id)o time: (int)t -{ - CachedLocalObject *item; - - item = (CachedLocalObject*)NSAllocateObject(self, 0, NSDefaultMallocZone()); - item->obj = RETAIN(o); - item->time = t; - return item; -} - -- (void) dealloc -{ - RELEASE(obj); - NSDeallocateObject(self); -} - -- (BOOL) countdown -{ - if (time-- > 0) - return YES; - return NO; -} - -- (id) obj -{ - return obj; -} - -@end - - - -@interface NSConnection (Private) -- (void) handlePortMessage: (NSPortMessage*)msg; -- (void) _runInNewThread; -+ (void) setDebug: (int)val; - -- (void) _doneInRmc: (NSPortCoder*)c; -- (NSPortCoder*) _getReplyRmc: (int)sn; -- (NSPortCoder*) _makeInRmc: (NSMutableArray*)components; -- (NSPortCoder*) _makeOutRmc: (int)sequence; -- (int) _newMsgNumber; -- (void) _sendOutRmc: (NSPortCoder*)c type: (int)msgid; - -- (void) _service_forwardForProxy: (NSPortCoder*)rmc; -- (void) _service_release: (NSPortCoder*)rmc; -- (void) _service_retain: (NSPortCoder*)rmc; -- (void) _service_rootObject: (NSPortCoder*)rmc; -- (void) _service_shutdown: (NSPortCoder*)rmc; -- (void) _service_typeForSelector: (NSPortCoder*)rmc; -@end - -#define _proxiesGate _refGate -#define _queueGate _refGate -#define sequenceNumberGate _refGate - - -/* class defaults */ -static NSTimer *timer; - -static int debug_connection = 0; - -static NSHashTable *connection_table; -static NSLock *connection_table_gate; - -/* - * Locate an existing connection with the specified send and receive ports. - * nil ports act as wildcards and return the first match. - */ -static NSConnection* -existingConnection(NSPort *receivePort, NSPort *sendPort) -{ - NSHashEnumerator enumerator; - NSConnection *c; - - F_LOCK(connection_table_gate); - enumerator = NSEnumerateHashTable(connection_table); - while ((c = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) - { - if ((sendPort == nil || [sendPort isEqual: [c sendPort]]) - && (receivePort == nil || [receivePort isEqual: [c receivePort]])) - { - /* - * We don't want this connection to be destroyed by another thread - * between now and when it's returned from this function and used! - */ - AUTORELEASE(RETAIN(c)); - break; - } - } - F_UNLOCK(connection_table_gate); - return c; -} - -static NSMapTable *root_object_map; -static NSLock *root_object_map_gate; - -static id -rootObjectForInPort(NSPort *aPort) -{ - id rootObject; - - F_LOCK(root_object_map_gate); - rootObject = (id)NSMapGet(root_object_map, (void*)(gsaddr)aPort); - F_UNLOCK(root_object_map_gate); - return rootObject; -} - -/* Pass nil to remove any reference keyed by aPort. */ -static void -setRootObjectForInPort(id anObj, NSPort *aPort) -{ - id oldRootObject; - - F_LOCK(root_object_map_gate); - oldRootObject = (id)NSMapGet(root_object_map, (void*)(gsaddr)aPort); - if (oldRootObject != anObj) - { - if (anObj != nil) - { - NSMapInsert(root_object_map, (void*)(gsaddr)aPort, - (void*)(gsaddr)anObj); - } - else /* anObj == nil && oldRootObject != nil */ - { - NSMapRemove(root_object_map, (void*)(gsaddr)aPort); - } - } - F_UNLOCK(root_object_map_gate); -} - -static NSMapTable *all_connections_local_objects = NULL; -static NSMapTable *all_connections_local_targets = NULL; -static NSMapTable *all_connections_local_cached = NULL; -static NSLock *global_proxies_gate; - - - - -@implementation NSConnection - -+ (NSArray*) allConnections -{ - return NSAllHashTableObjects(connection_table); -} - -+ (NSConnection*) connectionWithReceivePort: (NSPort*)r - sendPort: (NSPort*)s -{ - NSConnection *c = existingConnection(r, s); - - if (c == nil) - { - c = [self allocWithZone: NSDefaultMallocZone()]; - c = [c initWithReceivePort: r sendPort: s]; - AUTORELEASE(c); - } - return c; -} - -+ (NSConnection*) connectionWithRegisteredName: (NSString*)n - host: (NSString*)h -{ - NSPortNameServer *s; - - s = [NSPortNameServer systemDefaultPortNameServer]; - return [self connectionWithRegisteredName: n - host: h - usingNameServer: s]; -} - -+ (NSConnection*) connectionWithRegisteredName: (NSString*)n - host: (NSString*)h - usingNameServer: (NSPortNameServer*)s -{ - NSConnection *con = nil; - - if (s != nil) - { - NSPort *sendPort = [s portForName: n onHost: h]; - - if (sendPort != nil) - { - con = existingConnection(nil, sendPort); - if (con == nil) - { - NSPort *recvPort; - - recvPort = [[self defaultConnection] receivePort]; - con = [self connectionWithReceivePort: recvPort - sendPort: sendPort]; - } - } - } - return con; -} - -+ (id) currentConversation -{ - [self notImplemented: _cmd]; - return self; -} - -/* - * Get the default connection for a thread. - * Possible problem - if the connection is invalidated, it won't be - * cleaned up until this thread calls this method again. The connection - * and it's ports could hang around for a very long time. - */ -+ (NSConnection*) defaultConnection -{ - static NSString *tkey = @"NSConnectionThreadKey"; - NSConnection *c; - NSMutableDictionary *d; - - d = GSCurrentThreadDictionary(); - c = (NSConnection*)[d objectForKey: tkey]; - if (c != nil && [c isValid] == NO) - { - /* - * If the default connection for this thread has been invalidated - - * release it and create a new one. - */ - [d removeObjectForKey: tkey]; - c = nil; - } - if (c == nil) - { - NSPort *port; - - c = [self alloc]; - port = [NSPort port]; - c = [c initWithReceivePort: port sendPort: nil]; - [d setObject: c forKey: tkey]; - RELEASE(c); - } - return c; -} - -+ (void) initialize -{ - if (self == [NSConnection class]) - { - connectionClass = self; - dateClass = [NSDate class]; - distantObjectClass = [NSDistantObject class]; - portCoderClass = [NSPortCoder class]; - runLoopClass = [NSRunLoop class]; - - connection_table = - NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); - connection_table_gate = [NSLock new]; - /* xxx When NSHashTable's are working, change this. */ - all_connections_local_objects = - NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); - all_connections_local_targets = - NSCreateMapTable(NSIntMapKeyCallBacks, - NSNonOwnedPointerMapValueCallBacks, 0); - all_connections_local_cached = - NSCreateMapTable(NSIntMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); - global_proxies_gate = [NSLock new]; - root_object_map = - NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); - root_object_map_gate = [NSLock new]; - } -} - -+ (id) new -{ - /* - * Undocumented feature of OPENSTEP/MacOS-X - * +new returns the default connection. - */ - return RETAIN([self defaultConnection]); -} - -+ (NSDistantObject*) rootProxyForConnectionWithRegisteredName: (NSString*)n - host: (NSString*)h -{ - NSConnection *connection; - NSDistantObject *proxy = nil; - - connection = [self connectionWithRegisteredName: n host: h]; - if (connection != nil) - { - proxy = [connection rootProxy]; - } - - return proxy; -} - -+ (NSDistantObject*) rootProxyForConnectionWithRegisteredName: (NSString*)n - host: (NSString*)h usingNameServer: (NSPortNameServer*)s -{ - NSConnection *connection; - NSDistantObject *proxy = nil; - - connection = [self connectionWithRegisteredName: n - host: h - usingNameServer: s]; - if (connection != nil) - { - proxy = [connection rootProxy]; - } - - return proxy; -} - -+ (void) _timeout: (NSTimer*)t -{ - NSArray *cached_locals; - int i; - - cached_locals = NSAllMapTableValues(all_connections_local_cached); - for (i = [cached_locals count]; i > 0; i--) - { - CachedLocalObject *item = [cached_locals objectAtIndex: i-1]; - - if ([item countdown] == NO) - { - GSLocalCounter *counter = [item obj]; - NSMapRemove(all_connections_local_cached, (void*)counter->target); - } - } - if ([cached_locals count] == 0) - { - [t invalidate]; - timer = nil; - } -} - -- (void) addRequestMode: (NSString*)mode -{ - M_LOCK(_refGate); - if ([self isValid] == YES) - { - if ([_requestModes containsObject: mode] == NO) - { - unsigned c = [_runLoops count]; - - while (c-- > 0) - { - NSRunLoop *loop = [_runLoops objectAtIndex: c]; - - [loop addPort: _receivePort forMode: mode]; - } - [_requestModes addObject: mode]; - } - } - M_UNLOCK(_refGate); -} - -- (void) addRunLoop: (NSRunLoop*)loop -{ - M_LOCK(_refGate); - if ([self isValid] == YES) - { - if ([_runLoops indexOfObjectIdenticalTo: loop] == NSNotFound) - { - unsigned c = [_requestModes count]; - - while (c-- > 0) - { - NSString *mode = [_requestModes objectAtIndex: c]; - - [loop addPort: _receivePort forMode: mode]; - } - [_runLoops addObject: loop]; - } - } - M_UNLOCK(_refGate); -} - -- (void) dealloc -{ - if (debug_connection) - NSLog(@"deallocating 0x%x", (gsaddr)self); - [super dealloc]; -} - -- (id) delegate -{ - return GS_GC_UNHIDE(_delegate); -} - -- (void) enableMultipleThreads -{ - _multipleThreads = YES; -} - -- (BOOL) independentConversationQueueing -{ - return _independentQueueing; -} - -- (id) init -{ - /* - * Undocumented feature of OPENSTEP/MacOS-X - * -init returns the default connection. - */ - RELEASE(self); - return RETAIN([connectionClass defaultConnection]); -} - -/* This is the designated initializer for NSConnection */ -- (id) initWithReceivePort: (NSPort*)r - sendPort: (NSPort*)s -{ - NSNotificationCenter *nCenter; - NSConnection *parent; - NSConnection *conn; - NSRunLoop *loop; - id del; - - /* - * If the receive port is nil, deallocate connection and return nil. - */ - if (r == nil) - { - if (debug_connection > 2) - { - NSLog(@"Asked to create connection with nil receive port"); - } - DESTROY(self); - return self; - } - - /* - * If the send port is nil, set it to the same as the receive port - * This connection will then only be useful to act as a server. - */ - if (s == nil) - { - s = r; - } - - conn = existingConnection(r, s); - - /* - * If the send and receive ports match an existing connection - * deallocate the new one and retain and return the old one. - */ - if (conn != nil) - { - RELEASE(self); - self = RETAIN(conn); - if (debug_connection > 2) - { - NSLog(@"Found existing connection (0x%x) for \n\t%@\n\t%@", - (gsaddr)conn, r, s); - } - return self; - } - - /* - * The parent connection is the one whose send and receive ports are - * both the same as our receive port. - */ - parent = existingConnection(r, r); - - if (debug_connection) - { - NSLog(@"Initialising new connection with parent 0x%x, 0x%x\n\t%@\n\t%@", - (gsaddr)parent, (gsaddr)self, r, s); - } - - M_LOCK(connection_table_gate); - - _isValid = YES; - _receivePort = RETAIN(r); - _sendPort = RETAIN(s); - _messageCount = 0; - _repOutCount = 0; - _reqOutCount = 0; - _repInCount = 0; - _reqInCount = 0; - - /* - * These arrays cache NSPortCoder objects - */ - _cachedDecoders = [NSMutableArray new]; - _cachedEncoders = [NSMutableArray new]; - - /* - * This is used to queue up incoming NSPortMessages representing requests - * that can't immediately be dealt with. - */ - _requestQueue = [NSMutableArray new]; - - /* - * This maps request sequence numbers to the NSPortCoder objects representing - * replies arriving from the remote connection. - */ - _replyMap = - NSCreateMapTable(NSIntMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); - - /* - * This maps (void*)obj to (id)obj. The obj's are retained. - * We use this instead of an NSHashTable because we only care about - * the object's address, and don't want to send the -hash message to it. - */ - _localObjects = - NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); - - /* - * This maps handles for local objects to their local proxies. - */ - _localTargets = - NSCreateMapTable(NSIntMapKeyCallBacks, - NSNonOwnedPointerMapValueCallBacks, 0); - - /* - * This maps [proxy targetForProxy] to proxy. The proxy's are retained. - */ - _remoteProxies = - NSCreateMapTable(NSIntMapKeyCallBacks, - NSNonOwnedPointerMapValueCallBacks, 0); - - /* - * Some attributes are inherited from the parent if possible. - */ - if (parent != nil) - { - _independentQueueing = parent->_independentQueueing; - _replyTimeout = parent->_replyTimeout; - _requestTimeout = parent->_requestTimeout; - } - else - { - _independentQueueing = NO; - _replyTimeout = CONNECTION_DEFAULT_TIMEOUT; - _requestTimeout = CONNECTION_DEFAULT_TIMEOUT; - } - - _requestDepth = 0; - _delegate = nil; - _refGate = [NSRecursiveLock new]; - - /* - * Set up request modes array and make sure the receiving port is - * added to the run loop to get data. - */ - loop = [runLoopClass currentRunLoop]; - _runLoops = [[NSMutableArray alloc] initWithObjects: &loop count: 1]; - _requestModes = [[NSMutableArray alloc] initWithCapacity: 2]; - [self addRequestMode: NSDefaultRunLoopMode]; - [self addRequestMode: NSConnectionReplyMode]; - - /* Ask the delegate for permission, (OpenStep-style and GNUstep-style). */ - - /* Preferred MacOS-X version, which just allows the returning of BOOL */ - del = [parent delegate]; - if ([del respondsTo: @selector(connection:shouldMakeNewConnection:)]) - { - if ([del connection: parent shouldMakeNewConnection: self] == NO) - { - M_UNLOCK(connection_table_gate); - RELEASE(self); - return nil; - } - } - /* Deprecated OpenStep version, which just allows the returning of BOOL */ - if ([del respondsTo: @selector(makeNewConnection:sender:)]) - { - if (![del makeNewConnection: self sender: parent]) - { - M_UNLOCK(connection_table_gate); - RELEASE(self); - return nil; - } - } - /* Here is the GNUstep version, which allows the delegate to specify - a substitute. Note: The delegate is responsible for freeing - newConn if it returns something different. */ - if ([del respondsTo: @selector(connection:didConnect:)]) - self = [del connection: parent didConnect: self]; - - /* - * If we have no parent, we must handle incoming packets on our - * receive port ourself - so we set ourself up as the port delegate. - */ - if (parent == nil) - { - [_receivePort setDelegate: self]; - } - - /* Register ourselves for invalidation notification when the - ports become invalid. */ - nCenter = [NSNotificationCenter defaultCenter]; - [nCenter addObserver: self - selector: @selector(portIsInvalid:) - name: NSPortDidBecomeInvalidNotification - object: r]; - if (s != nil) - [nCenter addObserver: self - selector: @selector(portIsInvalid:) - name: NSPortDidBecomeInvalidNotification - object: s]; - - /* In order that connections may be deallocated - there is an - implementation of [-release] to automatically remove the connection - from this array when it is the only thing retaining it. */ - NSHashInsert(connection_table, (void*)self); - M_UNLOCK(connection_table_gate); - - [[NSNotificationCenter defaultCenter] - postNotificationName: NSConnectionDidInitializeNotification - object: self]; - - return self; -} - -- (void) invalidate -{ - BOOL wasValid; - - M_LOCK(_refGate); - wasValid = _isValid; - _isValid = NO; - M_UNLOCK(_refGate); - - if (wasValid == NO) - { - return; - } - - /* - * Don't need notifications any more - so remove self as observer. - */ - [[NSNotificationCenter defaultCenter] removeObserver: self]; - - /* - * Withdraw from run loops. - */ - [self setRequestMode: nil]; - - RETAIN(self); - - if (debug_connection) - { - NSLog(@"Invalidating connection 0x%x\n\t%@\n\t%@", - (gsaddr)self, _receivePort, _sendPort); - } - /* - * We need to notify any watchers of our death - but if we are already - * in the deallocation process, we can't have a notification retaining - * and autoreleasing us later once we are deallocated - so we do the - * notification with a local autorelease pool to ensure that any release - * is done before the deallocation completes. - */ - { - CREATE_AUTORELEASE_POOL(arp); - - [[NSNotificationCenter defaultCenter] - postNotificationName: NSConnectionDidDieNotification - object: self]; - RELEASE(arp); - } - - /* - * If we have been invalidated, we don't need to retain proxies - * for local objects any more. In fact, we want to get rid of - * these proxies in case they are keeping us retained when we - * might otherwise de deallocated. - */ - { - NSArray *targets; - unsigned i; - - M_LOCK(_proxiesGate); - targets = NSAllMapTableValues(_localTargets); - IF_NO_GC(RETAIN(targets)); - for (i = 0; i < [targets count]; i++) - { - id t = [[targets objectAtIndex: i] localForProxy]; - - [self removeLocalObject: t]; - } - [targets release]; - M_UNLOCK(_proxiesGate); - } - - RELEASE(self); -} - -- (BOOL) isValid -{ - return _isValid; -} - -- (NSArray*) localObjects -{ - NSArray *c; - - /* Don't assert (_isValid); */ - M_LOCK(_proxiesGate); - c = NSAllMapTableValues(_localObjects); - M_UNLOCK(_proxiesGate); - return c; -} - -- (BOOL) multipleThreadsEnabled -{ - return _multipleThreads; -} - -- (NSPort*) receivePort -{ - return _receivePort; -} - -- (BOOL) registerName: (NSString*)name -{ - NSPortNameServer *svr = [NSPortNameServer systemDefaultPortNameServer]; - - return [self registerName: name withNameServer: svr]; -} - -- (BOOL) registerName: (NSString*)name withNameServer: (NSPortNameServer*)svr -{ - NSArray *names = [svr namesForPort: _receivePort]; - BOOL result = YES; - unsigned c; - - if (name != nil) - { - result = [svr registerPort: _receivePort forName: name]; - } - if (result == YES && (c = [names count]) > 0) - { - unsigned i; - - for (i = 0; i < c; i++) - { - NSString *tmp = [names objectAtIndex: i]; - - if ([tmp isEqualToString: name] == NO) - { - [svr removePort: _receivePort forName: name]; - } - } - } - return result; -} - -- (void) release -{ - /* - * If this would cause the connection to be deallocated then we - * must perform all necessary work (done in [-gcFinalize]). - * We bracket the code with a retain and release so that any - * retain/release pairs in the code won't cause recursion. - */ - if ([self retainCount] == 1) - { - [super retain]; - [self gcFinalize]; - [super release]; - } - [super release]; -} - -- (NSArray *) remoteObjects -{ - NSArray *c; - - /* Don't assert (_isValid); */ - M_LOCK(_proxiesGate); - c = NSAllMapTableValues(_remoteProxies); - M_UNLOCK(_proxiesGate); - return c; -} - -- (void) removeRequestMode: (NSString*)mode -{ - M_LOCK(_refGate); - if ([_requestModes containsObject: mode]) - { - unsigned c = [_runLoops count]; - - while (c-- > 0) - { - NSRunLoop *loop = [_runLoops objectAtIndex: c]; - - [loop removePort: _receivePort forMode: mode]; - } - [_requestModes removeObject: mode]; - } - M_UNLOCK(_refGate); -} - -- (void) removeRunLoop: (NSRunLoop*)loop -{ - unsigned pos; - - M_LOCK(_refGate); - pos = [_runLoops indexOfObjectIdenticalTo: loop]; - if (pos != NSNotFound) - { - unsigned c = [_requestModes count]; - - while (c-- > 0) - { - NSString *mode = [_requestModes objectAtIndex: c]; - - [loop removePort: _receivePort forMode: mode]; - } - [_runLoops removeObjectAtIndex: pos]; - } - M_UNLOCK(_refGate); -} - -- (NSTimeInterval) replyTimeout -{ - return _replyTimeout; -} - -- (NSArray*) requestModes -{ - return AUTORELEASE([_requestModes copy]); -} - -- (NSTimeInterval) requestTimeout -{ - return _requestTimeout; -} - -- (id) rootObject -{ - return rootObjectForInPort(_receivePort); -} - -- (NSDistantObject*) rootProxy -{ - NSPortCoder *op; - NSPortCoder *ip; - NSDistantObject *newProxy = nil; - int seq_num; - - NSParameterAssert(_receivePort); - NSParameterAssert(_isValid); - - seq_num = [self _newMsgNumber]; - op = [self _makeOutRmc: seq_num]; - [self _sendOutRmc: op type: ROOTPROXY_REQUEST]; - - ip = [self _getReplyRmc: seq_num]; - [ip decodeValueOfObjCType: @encode(id) at: &newProxy]; - DESTROY(ip); - return AUTORELEASE(newProxy); -} - -- (void) runInNewThread -{ - [self removeRunLoop: [runLoopClass currentRunLoop]]; - [NSThread detachNewThreadSelector: @selector(_runInNewThread) - toTarget: self - withObject: nil]; -} - -- (NSPort*) sendPort -{ - return _sendPort; -} - -- (void) setDelegate: (id)anObj -{ - _delegate = GS_GC_HIDE(anObj); - _authenticateIn = - [anObj respondsToSelector: @selector(authenticateComponents:withData:)]; - _authenticateOut = - [anObj respondsToSelector: @selector(authenticationDataForComponents:)]; -} - -- (void) setIndependentConversationQueueing: (BOOL)flag -{ - _independentQueueing = flag; -} - -- (void) setReplyTimeout: (NSTimeInterval)to -{ - _replyTimeout = to; -} - -- (void) setRequestMode: (NSString*)mode -{ - M_LOCK(_refGate); - while ([_requestModes count] > 0 && [_requestModes objectAtIndex: 0] != mode) - { - [self removeRequestMode: [_requestModes objectAtIndex: 0]]; - } - while ([_requestModes count] > 1) - { - [self removeRequestMode: [_requestModes objectAtIndex: 1]]; - } - if (mode != nil && [_requestModes count] == 0) - { - [self addRequestMode: mode]; - } - M_UNLOCK(_refGate); -} - -- (void) setRequestTimeout: (NSTimeInterval)to -{ - _requestTimeout = to; -} - -- (void) setRootObject: (id)anObj -{ - setRootObjectForInPort(anObj, _receivePort); -} - -- (NSDictionary*) statistics -{ - NSMutableDictionary *d; - id o; - - d = [NSMutableDictionary dictionaryWithCapacity: 8]; - - M_LOCK(_refGate); - - /* - * These are in OPENSTEP 4.2 - */ - o = [NSNumber numberWithUnsignedInt: _repInCount]; - [d setObject: o forKey: NSConnectionRepliesReceived]; - o = [NSNumber numberWithUnsignedInt: _repOutCount]; - [d setObject: o forKey: NSConnectionRepliesSent]; - o = [NSNumber numberWithUnsignedInt: _reqInCount]; - [d setObject: o forKey: NSConnectionRequestsReceived]; - o = [NSNumber numberWithUnsignedInt: _reqOutCount]; - [d setObject: o forKey: NSConnectionRequestsSent]; - - /* - * These are GNUstep extras - */ - o = [NSNumber numberWithUnsignedInt: NSCountMapTable(_localTargets)]; - [d setObject: o forKey: NSConnectionLocalCount]; - o = [NSNumber numberWithUnsignedInt: NSCountMapTable(_remoteProxies)]; - [d setObject: o forKey: NSConnectionProxyCount]; - - M_UNLOCK(_refGate); - - return d; -} - -@end - - - -@implementation NSConnection (GNUstepExtensions) - -+ (NSConnection*) newRegisteringAtName: (NSString*)name - withRootObject: (id)anObject -{ - NSConnection *conn; - - conn = [[self alloc] initWithReceivePort: [NSPort port] - sendPort: nil]; - [conn setRootObject: anObject]; - if ([conn registerName: name] == NO) - { - DESTROY(conn); - } - return conn; -} - -- (void) gcFinalize -{ - CREATE_AUTORELEASE_POOL(arp); - - if (debug_connection) - NSLog(@"finalising 0x%x", (gsaddr)self); - - [self invalidate]; - M_LOCK(connection_table_gate); - NSHashRemove(connection_table, self); - [timer invalidate]; - timer = nil; - M_UNLOCK(connection_table_gate); - - /* Remove rootObject from root_object_map if this is last connection */ - if (_receivePort != nil && existingConnection(_receivePort, nil) == nil) - { - setRootObjectForInPort(nil, _receivePort); - } - - /* Remove receive port from run loop. */ - [self setRequestMode: nil]; - - DESTROY(_requestModes); - DESTROY(_runLoops); - - /* - * Finished with ports - releasing them may generate a notification - * If we are the receive port delagate, try to shift responsibility. - */ - if ([_receivePort delegate] == self) - { - NSConnection *root = existingConnection(_receivePort, _receivePort); - - if (root == nil) - { - root = existingConnection(_receivePort, nil); - } - [_receivePort setDelegate: root]; - } - DESTROY(_receivePort); - DESTROY(_sendPort); - - M_LOCK(_proxiesGate); - if (_remoteProxies != 0) - { - NSFreeMapTable(_remoteProxies); - _remoteProxies = 0; - } - if (_localObjects != 0) - { - NSFreeMapTable(_localObjects); - _localObjects = 0; - } - if (_localTargets != 0) - { - NSFreeMapTable(_localTargets); - _localTargets = 0; - } - M_UNLOCK(_proxiesGate); - - DESTROY(_requestQueue); - if (_replyMap != 0) - { - NSFreeMapTable(_replyMap); - _replyMap = 0; - } - - DESTROY(_cachedDecoders); - DESTROY(_cachedEncoders); - - DESTROY(_refGate); - RELEASE(arp); -} - -/* - * NSDistantObject's -forward: : method calls this to send the message - * over the wire. - */ -- (retval_t) forwardForProxy: (NSDistantObject*)object - selector: (SEL)sel - argFrame: (arglist_t)argframe -{ - NSPortCoder *op; - - /* The callback for encoding the args of the method call. */ - void encoder (int argnum, void *datum, const char *type, int flags) - { -#define ENCODED_ARGNAME @"argument value" - switch (*type) - { - case _C_ID: - if (flags & _F_BYCOPY) - [op encodeBycopyObject: *(id*)datum]; -#ifdef _F_BYREF - else if (flags & _F_BYREF) - [op encodeByrefObject: *(id*)datum]; -#endif - else - [op encodeObject: *(id*)datum]; - break; - default: - [op encodeValueOfObjCType: type at: datum]; - } - } - - /* Encode the method on an RMC, and send it. */ - { - BOOL out_parameters; - const char *type; - int seq_num; - retval_t retframe; - - NSParameterAssert (_isValid); - - /* get the method types from the selector */ -#if NeXT_runtime - [NSException - raise: NSGenericException - format: @"Sorry, distributed objects does not work with NeXT runtime"]; - /* type = [object selectorTypeForProxy: sel]; */ -#else - type = sel_get_type(sel); -#endif - if (type == 0 || *type == '\0') { - type = [[object methodSignatureForSelector: sel] methodType]; - if (type) { - sel_register_typed_name(sel_get_name(sel), type); - } - } - NSParameterAssert(type); - NSParameterAssert(*type); - - seq_num = [self _newMsgNumber]; - op = [self _makeOutRmc: seq_num]; - - if (debug_connection > 4) - NSLog(@"building packet seq %d", seq_num); - - /* Send the types that we're using, so that the performer knows - exactly what qualifiers we're using. - If all selectors included qualifiers, and if I could make - sel_types_match() work the way I wanted, we wouldn't need to do - this. */ - [op encodeValueOfObjCType: @encode(char*) at: &type]; - - /* xxx This doesn't work with proxies and the NeXT runtime because - type may be a method_type from a remote machine with a - different architecture, and its argframe layout specifiers - won't be right for this machine! */ - out_parameters = mframe_dissect_call (argframe, type, encoder); - - [self _sendOutRmc: op type: METHOD_REQUEST]; - - if (debug_connection > 1) - NSLog(@"Sent message to 0x%x", (gsaddr)self); - - /* Get the reply rmc, and decode it. */ - { - 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"]; - } - /* xxx Why do we get the reply packet in here, and not - just before calling dissect_method_return() below? */ - 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]; - /* -decodeValueOfObjCType: at: malloc's new memory - for char*'s. We need to make sure it gets freed eventually - so we don't have a memory leak. Request here that it be - autorelease'ed. Also autorelease created objects. */ - if ((*type == _C_CHARPTR || *type == _C_PTR) && *(void**)datum != 0) - [NSData dataWithBytesNoCopy: *(void**)datum length: 1]; - else if (*type == _C_ID) - AUTORELEASE(*(id*)datum); - } - - retframe = mframe_build_return (argframe, type, out_parameters, decoder); - /* 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); - return retframe; - } - } -} - -- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target -{ - id op, ip; - char *type = 0; - int seq_num; - - NSParameterAssert(_receivePort); - NSParameterAssert (_isValid); - seq_num = [self _newMsgNumber]; - op = [self _makeOutRmc: seq_num]; - [op encodeValueOfObjCType: ":" at: &sel]; - [op encodeValueOfObjCType: @encode(unsigned) at: &target]; - [self _sendOutRmc: op type: METHODTYPE_REQUEST]; - ip = [self _getReplyRmc: seq_num]; - [ip decodeValueOfObjCType: @encode(char*) at: &type]; - DESTROY(ip); - return type; -} - - -/* Class-wide stats and collections. */ - -+ (unsigned) connectionsCount -{ - unsigned result; - - M_LOCK(connection_table_gate); - result = NSCountHashTable(connection_table); - M_UNLOCK(connection_table_gate); - return result; -} - -+ (unsigned) connectionsCountWithInPort: (NSPort*)aPort -{ - unsigned count = 0; - NSHashEnumerator enumerator; - NSConnection *o; - - M_LOCK(connection_table_gate); - enumerator = NSEnumerateHashTable(connection_table); - while ((o = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) - { - if ([aPort isEqual: [o receivePort]]) - { - count++; - } - } - M_UNLOCK(connection_table_gate); - - return count; -} - -@end - - - - - -@implementation NSConnection (Private) - -- (void) handlePortMessage: (NSPortMessage*)msg -{ - NSPortCoder *rmc; - int type = [msg msgid]; - NSMutableArray *components = [msg _components]; - NSPort *rp = [msg receivePort]; - NSPort *sp = [msg sendPort]; - NSConnection *conn; - - if (debug_connection > 4) - { - NSLog(@"handling packet of type %d (%@)", type, stringFromMsgType(type)); - } - conn = [connectionClass connectionWithReceivePort: rp sendPort: sp]; - if (conn == nil) - { - NSLog(@"received port message for unknown connection - %@", msg); - return; - } - else if ([conn isValid] == NO) - { - if (debug_connection) - { - NSLog(@"received port message for invalid connection - %@", msg); - } - return; - } - - if (_authenticateIn == YES) - { - NSData *d; - unsigned count = [components count]; - - d = AUTORELEASE(RETAIN([components objectAtIndex: --count])); - [components removeObjectAtIndex: count]; - if ([[self delegate] authenticateComponents: components - withData: d] == NO) - { - [NSException raise: NSFailedAuthenticationException - format: @"message not authenticated by delegate"]; - } - } - - rmc = [conn _makeInRmc: components]; - - switch (type) - { - case ROOTPROXY_REQUEST: - /* It won't take much time to handle this, so go ahead and service - it, even if we are waiting for a reply. */ - [conn _service_rootObject: rmc]; - break; - - case METHODTYPE_REQUEST: - /* It won't take much time to handle this, so go ahead and service - it, even if we are waiting for a reply. */ - [conn _service_typeForSelector: rmc]; - break; - - case METHOD_REQUEST: - /* - * We just got a new request; we need to decide whether to queue - * it or service it now. - * If the REPLY_DEPTH is 0, then we aren't in the middle of waiting - * for a reply, we are waiting for requests---so service it now. - * If REPLY_DEPTH is non-zero, we may still want to service it now - * if independent_queuing is NO. - */ - M_LOCK(conn->_queueGate); - if (conn->_requestDepth == 0 || conn->_independentQueueing == NO) - { - conn->_requestDepth++; - M_UNLOCK(conn->_queueGate); - [conn _service_forwardForProxy: rmc]; - M_LOCK(conn->_queueGate); - conn->_requestDepth--; - } - else - { - [conn->_requestQueue addObject: rmc]; - } - /* - * Service any requests that were queued while we - * were waiting for replies. - */ - while (conn->_requestDepth == 0 && [conn->_requestQueue count] > 0) - { - rmc = [conn->_requestQueue objectAtIndex: 0]; - [conn->_requestQueue removeObjectAtIndex: 0]; - M_UNLOCK(conn->_queueGate); - [conn _service_forwardForProxy: rmc]; - M_LOCK(conn->_queueGate); - } - M_UNLOCK(conn->_queueGate); - break; - - /* - * For replies, we read the sequence number from the reply object and - * store it in a map using thee sequence number as the key. That way - * it's easy for the connection to find replies by their numbers. - */ - case ROOTPROXY_REPLY: - case METHOD_REPLY: - case METHODTYPE_REPLY: - case RETAIN_REPLY: - { - int sequence; - - [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; - M_LOCK(conn->_queueGate); - NSMapInsert(conn->_replyMap, (void*)sequence, rmc); - M_UNLOCK(conn->_queueGate); - } - break; - - case CONNECTION_SHUTDOWN: - { - [conn _service_shutdown: rmc]; - break; - } - case PROXY_RELEASE: - { - [conn _service_release: rmc]; - break; - } - case PROXY_RETAIN: - { - [conn _service_retain: rmc]; - break; - } - default: - [NSException raise: NSGenericException - format: @"unrecognized NSPortCoder identifier"]; - } -} - -- (void) _runInNewThread -{ - NSRunLoop *loop = [runLoopClass currentRunLoop]; - - [self addRunLoop: loop]; - [loop run]; -} - -+ (void) setDebug: (int)val -{ - debug_connection = val; -} - -/* NSConnection calls this to service the incoming method request. */ -- (void) _service_forwardForProxy: (NSPortCoder*)aRmc -{ - char *forward_type = 0; - id op = nil; - int reply_sequence_number; - - void decoder (int argnum, void *datum, const char *type) - { - /* We need this "dismiss" to happen here and not later so that Coder - "-awake..." methods will get sent before the __builtin_apply! */ - if (argnum == -1 && datum == 0 && type == 0) - { - [self _doneInRmc: aRmc]; - return; - } - - [aRmc decodeValueOfObjCType: type at: datum]; - /* -decodeValueOfObjCType: at: malloc's new memory - for char*'s. We need to make sure it gets freed eventually - so we don't have a memory leak. Request here that it be - autorelease'ed. Also autorelease created objects. */ - if ((*type == _C_CHARPTR || *type == _C_PTR) && *(void**)datum != 0) - [NSData dataWithBytesNoCopy: *(void**)datum length: 1]; - else if (*type == _C_ID) - AUTORELEASE(*(id*)datum); - } - - void encoder (int argnum, void *datum, const char *type, int flags) - { -#define ENCODED_RETNAME @"return value" - if (op == nil) - { - BOOL is_exception = NO; - /* It is possible that our connection died while the method was - being called - in this case we mustn't try to send the result - back to the remote application! */ - if (!_isValid) - return; - op = [self _makeOutRmc: reply_sequence_number]; - [op encodeValueOfObjCType: @encode(BOOL) at: &is_exception]; - } - switch (*type) - { - case _C_ID: - if (flags & _F_BYCOPY) - [op encodeBycopyObject: *(id*)datum]; -#ifdef _F_BYREF - else if (flags & _F_BYREF) - [op encodeByrefObject: *(id*)datum]; -#endif - else - [op encodeObject: *(id*)datum]; - break; - default: - [op encodeValueOfObjCType: type at: datum]; - } - } - - /* Make sure don't let exceptions caused by servicing the client's - request cause us to crash. */ - NS_DURING - { - NSParameterAssert (_isValid); - - /* Save this for later */ - [aRmc decodeValueOfObjCType: @encode(int) at: &reply_sequence_number]; - - /* Get the types that we're using, so that we know - exactly what qualifiers the forwarder used. - If all selectors included qualifiers and I could make - sel_types_match() work the way I wanted, we wouldn't need - to do this. */ - [aRmc decodeValueOfObjCType: @encode(char*) at: &forward_type]; - - if (debug_connection > 1) - NSLog(@"Handling message from 0x%x", (gsaddr)self); - _reqInCount++; /* Handling an incoming request. */ - mframe_do_call (forward_type, decoder, encoder); - if (op != nil) - { - [self _sendOutRmc: op type: METHOD_REPLY]; - } - } - - /* Make sure we pass all exceptions back to the requestor. */ - NS_HANDLER - { - BOOL is_exception = YES; - - /* Send the exception back to the client. */ - if (_isValid) - { - NS_DURING - { - op = [self _makeOutRmc: reply_sequence_number]; - [op encodeValueOfObjCType: @encode(BOOL) - at: &is_exception]; - [op encodeBycopyObject: localException]; - [self _sendOutRmc: op type: METHOD_REPLY]; - } - NS_HANDLER - { - NSLog(@"Exception when sending exception back to client - %@", - localException); - } - NS_ENDHANDLER; - } - } - NS_ENDHANDLER; - if (forward_type != 0) - { - NSZoneFree(NSDefaultMallocZone(), forward_type); - } -} - -- (void) _service_rootObject: (NSPortCoder*)rmc -{ - id rootObject = rootObjectForInPort(_receivePort); - int sequence; - NSPortCoder *op; - - NSParameterAssert(_receivePort); - NSParameterAssert(_isValid); - NSParameterAssert([rmc connection] == self); - - [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; - [self _doneInRmc: rmc]; - op = [self _makeOutRmc: sequence]; - [op encodeObject: rootObject]; - [self _sendOutRmc: op type: ROOTPROXY_REPLY]; -} - -- (void) _service_release: (NSPortCoder*)rmc -{ - unsigned int count; - unsigned int pos; - int sequence; - - NSParameterAssert (_isValid); - - [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; - [rmc decodeValueOfObjCType: @encode(typeof(count)) at: &count]; - - for (pos = 0; pos < count; pos++) - { - unsigned target; - NSDistantObject *prox; - - [rmc decodeValueOfObjCType: @encode(typeof(target)) at: &target]; - - prox = (NSDistantObject*)[self includesLocalTarget: target]; - if (prox != nil) - { - if (debug_connection > 3) - NSLog(@"releasing object with target (0x%x) on (0x%x)", - target, (gsaddr)self); - [self removeLocalObject: [prox localForProxy]]; - } - else if (debug_connection > 3) - NSLog(@"releasing object with target (0x%x) on (0x%x) - nothing to do", - target, (gsaddr)self); - } - [self _doneInRmc: rmc]; -} - -- (void) _service_retain: (NSPortCoder*)rmc -{ - unsigned target; - NSPortCoder *op; - int sequence; - - NSParameterAssert (_isValid); - - [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; - op = [self _makeOutRmc: sequence]; - - [rmc decodeValueOfObjCType: @encode(typeof(target)) at: &target]; - [self _doneInRmc: rmc]; - - if (debug_connection > 3) - NSLog(@"looking to retain local object with target (0x%x) on (0x%x)", - target, (gsaddr)self); - - if ([self includesLocalTarget: target] == nil) - { - GSLocalCounter *counter; - - M_LOCK(global_proxies_gate); - counter = NSMapGet (all_connections_local_targets, (void*)target); - if (counter == nil) - { - /* - * If the target doesn't exist for any connection, but still - * persists in the cache (ie it was recently released) then - * we move it back from the cache to the main maps so we can - * retain it on this connection. - */ - counter = NSMapGet (all_connections_local_cached, (void*)target); - if (counter) - { - unsigned t = counter->target; - id o = counter->object; - - NSMapInsert(all_connections_local_objects, (void*)o, counter); - NSMapInsert(all_connections_local_targets, (void*)t, counter); - NSMapRemove(all_connections_local_cached, (void*)t); - if (debug_connection > 3) - NSLog(@"target (0x%x) moved from cache", target); - } - } - M_UNLOCK(global_proxies_gate); - if (counter == nil) - { - [op encodeObject: @"target not found anywhere"]; - if (debug_connection > 3) - NSLog(@"target (0x%x) not found anywhere for retain", target); - } - else - { - [distantObjectClass proxyWithLocal: counter->object - connection: self]; - [op encodeObject: nil]; - if (debug_connection > 3) - NSLog(@"retained object (0x%x) target (0x%x) on connection(0x%x)", - counter->object, counter->target, self); - } - } - else - { - [op encodeObject: nil]; - if (debug_connection > 3) - NSLog(@"target (0x%x) already retained on connection (0x%x)", - target, self); - } - - [self _sendOutRmc: op type: RETAIN_REPLY]; -} - -- (void) shutdown -{ - NSPortCoder *op; - - NSParameterAssert(_receivePort); - NSParameterAssert (_isValid); - op = [self _makeOutRmc: [self _newMsgNumber]]; - [self _sendOutRmc: op type: CONNECTION_SHUTDOWN]; -} - -- (void) _service_shutdown: (NSPortCoder*)rmc -{ - NSParameterAssert (_isValid); - [self _doneInRmc: rmc]; - [self invalidate]; - [NSException raise: NSGenericException - format: @"connection waiting for request was shut down"]; -} - -- (void) _service_typeForSelector: (NSPortCoder*)rmc -{ - NSPortCoder *op; - unsigned target; - NSDistantObject *p; - int sequence; - id o; - SEL sel; - const char *type; - struct objc_method* m; - - NSParameterAssert(_receivePort); - NSParameterAssert (_isValid); - - [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; - op = [self _makeOutRmc: sequence]; - - [rmc decodeValueOfObjCType: ":" at: &sel]; - [rmc decodeValueOfObjCType: @encode(unsigned) at: &target]; - [self _doneInRmc: rmc]; - p = [self includesLocalTarget: target]; - o = [p localForProxy]; - - /* xxx We should make sure that TARGET is a valid object. */ - /* Not actually a Proxy, but we avoid the warnings "id" would have made. */ - m = class_get_instance_method(((NSDistantObject*)o)->isa, sel); - /* Perhaps I need to be more careful in the line above to get the - version of the method types that has the type qualifiers in it. - Search the protocols list. */ - if (m) - type = m->method_types; - else - type = ""; - [op encodeValueOfObjCType: @encode(char*) at: &type]; - [self _sendOutRmc: op type: METHODTYPE_REPLY]; -} - - - -/* - * Check the queue, then try to get it from the network by waiting - * while we run the NSRunLoop. Raise exception if we don't get anything - * before timing out. - */ -- _getReplyRmc: (int)sn -{ - NSPortCoder *rmc; - NSDate *timeout_date = nil; - - M_LOCK(_queueGate); - while ((rmc = (NSPortCoder*)NSMapGet(_replyMap, (void*)sn)) == nil) - { - if (timeout_date == nil) - { - timeout_date - = [dateClass dateWithTimeIntervalSinceNow: _replyTimeout]; - } - M_UNLOCK(_queueGate); - if ([runLoopClass runOnceBeforeDate: timeout_date - forMode: NSConnectionReplyMode] == NO) - { - [NSException raise: NSPortTimeoutException - format: @"timed out waiting for reply"]; - } - M_LOCK(_queueGate); - } - if (rmc != nil) - { - RETAIN(rmc); - NSMapRemove(_replyMap, (void*)sn); - } - M_UNLOCK(_queueGate); - return rmc; -} - -- (int) _newMsgNumber -{ - int n; - - NSParameterAssert (_isValid); - M_LOCK(sequenceNumberGate); - n = _messageCount++; - M_UNLOCK(sequenceNumberGate); - return n; -} - -- (void) _doneInRmc: (NSPortCoder*)c -{ - M_LOCK(_refGate); - [_cachedDecoders addObject: c]; - [c dispatch]; /* Tell NSPortCoder to release the connection. */ - RELEASE(c); - M_UNLOCK(_refGate); -} - -- (NSPortCoder*) _makeInRmc: (NSMutableArray*)components -{ - NSPortCoder *coder; - unsigned count; - - NSParameterAssert(_isValid); - - M_LOCK(_refGate); - count = [_cachedDecoders count]; - if (count > 0) - { - coder = [_cachedDecoders objectAtIndex: --count]; - RETAIN(coder); - [_cachedDecoders removeObjectAtIndex: count]; - } - else - { - coder = [portCoderClass allocWithZone: NSDefaultMallocZone()]; - } - M_UNLOCK(_refGate); - - coder = [coder initWithReceivePort: _receivePort - sendPort: _sendPort - components: components]; - return coder; -} - -- (NSPortCoder*) _makeOutRmc: (int)sequence -{ - NSPortCoder *coder; - unsigned count; - - NSParameterAssert(_isValid); - - M_LOCK(_refGate); - count = [_cachedEncoders count]; - if (count > 0) - { - coder = [_cachedEncoders objectAtIndex: --count]; - RETAIN(coder); - [_cachedEncoders removeObjectAtIndex: count]; - } - else - { - coder = [portCoderClass allocWithZone: NSDefaultMallocZone()]; - } - M_UNLOCK(_refGate); - - coder = [coder initWithReceivePort: _receivePort - sendPort: _sendPort - components: nil]; - [coder encodeValueOfObjCType: @encode(int) at: &sequence]; - return coder; -} - -- (void) _sendOutRmc: (NSPortCoder*)c type: (int)msgid -{ - NSDate *limit; - BOOL sent = NO; - BOOL raiseException = NO; - BOOL needsReply = NO; - NSMutableArray *components = [c _components]; - - if (_authenticateOut == YES) - { - NSData *d; - - d = [[self delegate] authenticationDataForComponents: components]; - if (d == nil) - { - RELEASE(c); - [NSException raise: NSGenericException - format: @"Bad authentication data provided by delegate"]; - } - [components addObject: d]; - } - - switch (msgid) - { - case PROXY_RETAIN: - needsReply = YES; - case CONNECTION_SHUTDOWN: - case METHOD_REPLY: - case ROOTPROXY_REPLY: - case METHODTYPE_REPLY: - case PROXY_RELEASE: - case RETAIN_REPLY: - raiseException = NO; - break; - - case METHOD_REQUEST: - case ROOTPROXY_REQUEST: - case METHODTYPE_REQUEST: - default: - raiseException = YES; - needsReply = YES; - break; - } - - limit = [dateClass dateWithTimeIntervalSinceNow: _requestTimeout]; - sent = [_sendPort sendBeforeDate: limit - msgid: msgid - components: components - from: _receivePort - reserved: [_sendPort reservedSpaceLength]]; - - M_LOCK(_refGate); - /* - * If we have sent out a request on a run loop that we don't already - * know about, it must be on a new thread - so if we have multipleThreads - * enabled, we must add the run loop of the new thread so that we can - * get the reply in this thread. - */ - if (_multipleThreads == YES && needsReply == YES) - { - NSRunLoop *loop = [runLoopClass currentRunLoop]; - - if ([_runLoops indexOfObjectIdenticalTo: loop] == NSNotFound) - { - [self addRunLoop: loop]; - } - } - - /* - * We replace the code we have just used in the cache, and tell it not to - * retain this connection any more. - */ - [_cachedEncoders addObject: c]; - [c dispatch]; /* Tell NSPortCoder to release the connection. */ - RELEASE(c); - M_UNLOCK(_refGate); - - if (sent == NO) - { - NSString *text = stringFromMsgType(msgid); - - if (raiseException == YES) - { - [NSException raise: NSPortTimeoutException format: text]; - } - else - { - NSLog(@"Port operation timed out - %@", text); - } - } - else - { - switch (msgid) - { - case METHOD_REQUEST: - _reqOutCount++; /* Sent a request. */ - break; - case METHOD_REPLY: - _repOutCount++; /* Sent back a reply. */ - break; - default: - break; - } - } -} - - - - -/* Managing objects and proxies. */ -- (void) addLocalObject: (id)anObj -{ - id object = [anObj localForProxy]; - unsigned target; - GSLocalCounter *counter; - - NSParameterAssert (_isValid); - M_LOCK(_proxiesGate); - M_LOCK(global_proxies_gate); - /* xxx Do we need to check to make sure it's not already there? */ - /* This retains object. */ - NSMapInsert(_localObjects, (void*)object, anObj); - - /* - * Keep track of local objects accross all connections. - */ - counter = NSMapGet(all_connections_local_objects, (void*)object); - if (counter) - { - counter->ref++; - target = counter->target; - } - else - { - counter = [GSLocalCounter newWithObject: object]; - target = counter->target; - NSMapInsert(all_connections_local_objects, (void*)object, counter); - NSMapInsert(all_connections_local_targets, (void*)target, counter); - RELEASE(counter); - } - [anObj setProxyTarget: target]; - NSMapInsert(_localTargets, (void*)target, anObj); - if (debug_connection > 2) - NSLog(@"add local object (0x%x) target (0x%x) " - @"to connection (0x%x) (ref %d)", - (gsaddr)object, target, (gsaddr) self, counter->ref); - M_UNLOCK(global_proxies_gate); - M_UNLOCK(_proxiesGate); -} - -- (NSDistantObject*) localForObject: (id)object -{ - NSDistantObject *p; - - /* Don't assert (_isValid); */ - M_LOCK(_proxiesGate); - p = NSMapGet (_localObjects, (void*)object); - M_UNLOCK(_proxiesGate); - NSParameterAssert(!p || [p connectionForProxy] == self); - return p; -} - -/* This should get called whenever an object free's itself */ -+ (void) removeLocalObject: (id)anObj -{ - NSHashEnumerator enumerator; - NSConnection *o; - - enumerator = NSEnumerateHashTable(connection_table); - while ((o = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) - { - [o removeLocalObject: anObj]; - } -} - -- (void) removeLocalObject: (id)anObj -{ - NSDistantObject *prox; - unsigned target; - GSLocalCounter *counter; - unsigned val = 0; - - M_LOCK(global_proxies_gate); - M_LOCK(_proxiesGate); - - prox = NSMapGet(_localObjects, (void*)anObj); - target = [prox targetForProxy]; - - /* - * If all references to a local proxy have gone - remove the - * global reference as well. - */ - counter = NSMapGet(all_connections_local_objects, (void*)anObj); - if (counter) - { - counter->ref--; - if ((val = counter->ref) == 0) - { - /* - * If this proxy has been vended onwards by another process, we - * need to keep a reference to the local object around for a - * while in case that other process needs it. - */ - if (0) - { - id item; - if (timer == nil) - { - timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 - target: connectionClass - selector: @selector(_timeout:) - userInfo: nil - repeats: YES]; - } - item = [CachedLocalObject newWithObject: counter time: 30]; - NSMapInsert(all_connections_local_cached, (void*)target, item); - RELEASE(item); - if (debug_connection > 3) - NSLog(@"placed local object (0x%x) target (0x%x) in cache", - (gsaddr)anObj, target); - } - NSMapRemove(all_connections_local_objects, (void*)anObj); - NSMapRemove(all_connections_local_targets, (void*)target); - } - } - - NSMapRemove(_localObjects, (void*)anObj); - NSMapRemove(_localTargets, (void*)target); - - if (debug_connection > 2) - NSLog(@"remove local object (0x%x) target (0x%x) " - @"from connection (0x%x) (ref %d)", - (gsaddr)anObj, target, (gsaddr)self, val); - - M_UNLOCK(_proxiesGate); - M_UNLOCK(global_proxies_gate); -} - -- (void) _release_targets: (unsigned*)list count: (unsigned)number -{ - NS_DURING - { - /* - * Tell the remote app that it can release its local objects - * for the targets in the specified list since we don't have - * proxies for them any more. - */ - if (_receivePort && _isValid && number > 0) - { - id op; - unsigned i; - int sequence = [self _newMsgNumber]; - - op = [self _makeOutRmc: sequence]; - - [op encodeValueOfObjCType: @encode(unsigned) at: &number]; - - for (i = 0; i < number; i++) - { - [op encodeValueOfObjCType: @encode(unsigned) at: &list[i]]; - if (debug_connection > 3) - NSLog(@"sending release for target (0x%x) on (0x%x)", - list[i], (gsaddr)self); - } - - [self _sendOutRmc: op type: PROXY_RELEASE]; - } - } - NS_HANDLER - { - if (debug_connection) - NSLog(@"failed to release targets - %@", localException); - } - NS_ENDHANDLER -} - -- (void) retainTarget: (unsigned)target -{ - NS_DURING - { - /* - * Tell the remote app that it must retain the local object - * for the target on this connection. - */ - if (_receivePort && _isValid) - { - NSPortCoder *op; - id ip; - id result; - int seq_num = [self _newMsgNumber]; - - op = [self _makeOutRmc: seq_num]; - [op encodeValueOfObjCType: @encode(typeof(target)) at: &target]; - [self _sendOutRmc: op type: PROXY_RETAIN]; - - ip = [self _getReplyRmc: seq_num]; - [ip decodeValueOfObjCType: @encode(id) at: &result]; - DESTROY(ip); - if (result != nil) - NSLog(@"failed to retain target - %@", result); - } - } - NS_HANDLER - { - NSLog(@"failed to retain target - %@", localException); - } - NS_ENDHANDLER -} - -- (void) removeProxy: (NSDistantObject*)aProxy -{ - unsigned target = [aProxy targetForProxy]; - - /* Don't assert (_isValid); */ - M_LOCK(_proxiesGate); - /* This also releases aProxy */ - NSMapRemove(_remoteProxies, (void*)target); - M_UNLOCK(_proxiesGate); - - /* - * Tell the remote application that we have removed our proxy and - * it can release it's local object. - */ - [self _release_targets: &target count: 1]; -} - -- (NSDistantObject*) proxyForTarget: (unsigned)target -{ - NSDistantObject *p; - - /* Don't assert (_isValid); */ - M_LOCK(_proxiesGate); - p = NSMapGet(_remoteProxies, (void*)target); - M_UNLOCK(_proxiesGate); - NSParameterAssert(!p || [p connectionForProxy] == self); - return p; -} - -- (void) addProxy: (NSDistantObject*) aProxy -{ - unsigned target = (unsigned int)[aProxy targetForProxy]; - - NSParameterAssert (_isValid); - NSParameterAssert(aProxy->isa == distantObjectClass); - NSParameterAssert([aProxy connectionForProxy] == self); - M_LOCK(_proxiesGate); - if (NSMapGet(_remoteProxies, (void*)target)) - { - M_UNLOCK(_proxiesGate); - [NSException raise: NSGenericException - format: @"Trying to add the same proxy twice"]; - } - NSMapInsert(_remoteProxies, (void*)target, aProxy); - M_UNLOCK(_proxiesGate); -} - -- (id) includesProxyForTarget: (unsigned)target -{ - NSDistantObject *ret; - - /* Don't assert (_isValid); */ - M_LOCK(_proxiesGate); - ret = NSMapGet (_remoteProxies, (void*)target); - M_UNLOCK(_proxiesGate); - return ret; -} - -- (id) includesLocalObject: (id)anObj -{ - NSDistantObject *ret; - - /* Don't assert (_isValid); */ - M_LOCK(_proxiesGate); - ret = NSMapGet(_localObjects, (void*)anObj); - M_UNLOCK(_proxiesGate); - return ret; -} - -- (id) includesLocalTarget: (unsigned)target -{ - NSDistantObject *ret; - - /* Don't assert (_isValid); */ - M_LOCK(_proxiesGate); - ret = NSMapGet(_localTargets, (void*)target); - M_UNLOCK(_proxiesGate); - return ret; -} - -/* Check all connections. - Proxy needs to use this when decoding a local object in order to - make sure the target address is a valid object. It is not enough - for the Proxy to check the Proxy's connection only (using - -includesLocalTarget), because the proxy may have come from a - triangle connection. */ -+ (id) includesLocalTarget: (unsigned)target -{ - id ret; - - /* Don't assert (_isValid); */ - M_LOCK(global_proxies_gate); - ret = NSMapGet(all_connections_local_targets, (void*)target); - M_UNLOCK(global_proxies_gate); - return ret; -} - - -/* Accessing ivars */ - - -/* Prevent trying to encode the connection itself */ - -- (void) encodeWithCoder: (NSCoder*)anEncoder -{ - [self shouldNotImplement: _cmd]; -} - -- (id) initWithCoder: (NSCoder*)aDecoder; -{ - [self shouldNotImplement: _cmd]; - return self; -} - - -/* Shutting down and deallocating. */ - -/* - * We register this method for a notification when a port dies. - * NB. It is possible that the death of a port could be notified - * to us after we are invalidated - in which case we must ignore it. - */ -- (void) portIsInvalid: (NSNotification*)notification -{ - if (_isValid) - { - id port = [notification object]; - - if (debug_connection) - { - NSLog(@"Received port invalidation notification for " - @"connection 0x%x\n\t%@", (gsaddr)self, port); - } - - /* We shouldn't be getting any port invalidation notifications, - except from our own ports; this is how we registered ourselves - with the NSNotificationCenter in - +newForInPort: outPort: ancestorConnection. */ - NSParameterAssert (port == _receivePort || port == _sendPort); - - [self invalidate]; - } -} - -@end - diff --git a/Source/GSPortCoder.m b/Source/GSPortCoder.m deleted file mode 100644 index 3c8ddb9df..000000000 --- a/Source/GSPortCoder.m +++ /dev/null @@ -1,1946 +0,0 @@ -/* Implementation of NSPortCoder object for remote messaging - Copyright (C) 1997,2000 Free Software Foundation, Inc. - - This implementation for OPENSTEP conformance written by - Richard Frith-Macdonald - Created: August 1997, rewritten June 2000 - - based on original code - - - Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. - - Written by: Andrew Kachites McCallum - Created: July 1994 - - 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 -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * Setup for inline operation of pointer map tables. - */ -#define GSI_MAP_RETAIN_KEY(X) -#define GSI_MAP_RELEASE_KEY(X) -#define GSI_MAP_RETAIN_VAL(X) -#define GSI_MAP_RELEASE_VAL(X) -#define GSI_MAP_HASH(X) ((X).uint) -#define GSI_MAP_EQUAL(X,Y) ((X).uint == (Y).uint) - -#include - -/* - * Setup for inline operation of arrays. - */ -#define GSI_ARRAY_RETAIN(X) -#define GSI_ARRAY_RELEASE(X) -#define GSI_ARRAY_TYPES GSUNION_OBJ|GSUNION_SEL|GSUNION_STR - -#include - - - -#define _IN_PORT_CODER_M -#include -#undef _IN_PORT_CODER_M - -#include - -static BOOL debug_port_coder = NO; - -typedef unsigned char uchar; - -#define PREFIX "GNUstep DO archive" - -static SEL eSerSel = @selector(serializeDataAt:ofObjCType:context:); -static SEL eTagSel = @selector(serializeTypeTag:); -static SEL xRefSel = @selector(serializeTypeTag:andCrossRef:); -static SEL eObjSel = @selector(encodeObject:); -static SEL eValSel = @selector(encodeValueOfObjCType:at:); -static SEL dDesSel = @selector(deserializeDataAt:ofObjCType:atCursor:context:); -static SEL dTagSel = @selector(deserializeTypeTag:andCrossRef:atCursor:); -static SEL dValSel = @selector(decodeValueOfObjCType:at:); - - - -static const char* -typeToName1(char type) -{ - switch (type) - { - case _C_CLASS: return "class"; - case _C_ID: return "object"; - case _C_SEL: return "selector"; - case _C_CHR: return "char"; - case _C_UCHR: return "unsigned char"; - case _C_SHT: return "short"; - case _C_USHT: return "unsigned short"; - case _C_INT: return "int"; - case _C_UINT: return "unsigned int"; - case _C_LNG: return "long"; - case _C_ULNG: return "unsigned long"; - case _C_LNG_LNG: return "long long"; - case _C_ULNG_LNG: return "unsigned long long"; - case _C_FLT: return "float"; - case _C_DBL: return "double"; - case _C_PTR: return "pointer"; - case _C_CHARPTR: return "cstring"; - case _C_ARY_B: return "array"; - case _C_STRUCT_B: return "struct"; - default: - { - static char buf1[32]; - static char buf2[32]; - static char *bufptr = buf1; - - if (bufptr == buf1) - { - bufptr = buf2; - } - else - { - bufptr = buf1; - } - sprintf(bufptr, "unknown type info - 0x%x", type); - return bufptr; - } - } -} - -static const char* -typeToName2(char type) -{ - switch (type & _GSC_MASK) - { - case _GSC_CLASS: return "class"; - case _GSC_ID: return "object"; - case _GSC_SEL: return "selector"; - case _GSC_CHR: return "char"; - case _GSC_UCHR: return "unsigned char"; - case _GSC_SHT: return "short"; - case _GSC_USHT: return "unsigned short"; - case _GSC_INT: return "int"; - case _GSC_UINT: return "unsigned int"; - case _GSC_LNG: return "long"; - case _GSC_ULNG: return "unsigned long"; - case _GSC_LNG_LNG: return "long long"; - case _GSC_ULNG_LNG: return "unsigned long long"; - case _GSC_FLT: return "float"; - case _GSC_DBL: return "double"; - case _GSC_PTR: return "pointer"; - case _GSC_CHARPTR: return "cstring"; - case _GSC_ARY_B: return "array"; - case _GSC_STRUCT_B: return "struct"; - default: - { - static char buf1[32]; - static char buf2[32]; - static char *bufptr = buf1; - - if (bufptr == buf1) - { - bufptr = buf2; - } - else - { - bufptr = buf1; - } - sprintf(bufptr, "unknown type info - 0x%x", type); - return bufptr; - } - } -} - -/* - * There are thirtyone possible basic types. We reserve a type of zero - * to mean that no information is specified. The slots in this array - * MUST correspond to the definitions in NSData.h - */ -static char type_map[32] = { - 0, - _C_CHR, - _C_UCHR, - _C_SHT, - _C_USHT, - _C_INT, - _C_UINT, - _C_LNG, - _C_ULNG, -#ifdef _C_LNG_LNG - _C_LNG_LNG, - _C_ULNG_LNG, -#else - 0, - 0, -#endif - _C_FLT, - _C_DBL, - 0, - 0, - 0, - _C_ID, - _C_CLASS, - _C_SEL, - _C_PTR, - _C_CHARPTR, - _C_ARY_B, - _C_STRUCT_B, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 -}; - -static inline void -typeCheck(char t1, char t2) -{ - if (type_map[(t2 & _GSC_MASK)] != t1) - { - [NSException raise: NSInternalInconsistencyException - format: @"expected %s and got %s", - typeToName1(t1), typeToName2(t2)]; - } -} - -@interface GSClassInfo : NSObject -{ -@public - Class class; - unsigned version; - NSString *name; -} -+ (id) newWithClass: (Class)c andVersion: (unsigned)v; -- (NSString*) className; -@end - -@implementation GSClassInfo -+ (id) newWithClass: (Class)c andVersion: (unsigned)v; -{ - GSClassInfo *info; - - info = (GSClassInfo*)NSAllocateObject(self, 0, NSDefaultMallocZone()); - if (info != nil) - { - info->class = c; - info->version = v; - } - return info; -} -- (NSString*) className -{ - if (name == nil) - { - name = RETAIN(NSStringFromClass(class)); - } - return name; -} -- (void) dealloc -{ - TEST_RELEASE(name); - NSDeallocateObject(self); -} -@end - - - - - -@interface NSPortCoder (Headers) -- (void) _deserializeHeaderAt: (unsigned*)pos - version: (unsigned*)v - classes: (unsigned*)c - objects: (unsigned*)o - pointers: (unsigned*)p; -- (void) _serializeHeaderAt: (unsigned)pos - version: (unsigned)v - classes: (unsigned)c - objects: (unsigned)o - pointers: (unsigned)p; -@end - - -@implementation NSPortCoder - -static Class connectionClass; -static Class mutableArrayClass; -static Class mutableDataClass; -static Class mutableDictionaryClass; - -+ (void) initialize -{ - if (self == [NSPortCoder class]) - { - connectionClass = [NSConnection class]; - mutableArrayClass = [NSMutableArray class]; - mutableDataClass = [NSMutableData class]; - mutableDictionaryClass = [NSMutableDictionary class]; - } -} - -+ (NSPortCoder*) portCoderWithReceivePort: (NSPort*)recv - sendPort: (NSPort*)send - components: (NSArray*)comp; -{ - id coder; - - coder = [self allocWithZone: NSDefaultMallocZone()]; - coder = [coder initWithReceivePort: recv sendPort: send components: comp]; - AUTORELEASE(coder); - return coder; -} - -- (NSConnection*) connection -{ - return _conn; -} - -- (void) dealloc -{ - RELEASE(_comp); - RELEASE(_conn); - RELEASE(_cInfo); - if (_clsMap != 0) - { - GSIMapEmptyMap(_clsMap); - GSIMapEmptyMap(_cIdMap); - GSIMapEmptyMap(_uIdMap); - GSIMapEmptyMap(_ptrMap); - NSZoneFree(_clsMap->zone, (void*)_clsMap); - } - if (_clsAry != 0) - { - unsigned count = GSIArrayCount(_clsAry); - - while (count-- > 0) - { - RELEASE(GSIArrayItemAtIndex(_clsAry, count).obj); - } - GSIArrayClear(_clsAry); - GSIArrayClear(_objAry); - GSIArrayClear(_ptrAry); - NSZoneFree(_clsAry->zone, (void*)_clsAry); - } - - [super dealloc]; -} - -- (void) decodeArrayOfObjCType: (const char*)type - count: (unsigned)expected - at: (void*)buf -{ - int i; - int offset = 0; - int size = objc_sizeof_type(type); - unsigned char info; - unsigned count; - - (*_dTagImp)(_src, dTagSel, &info, 0, &_cursor); - (*_dDesImp)(_src, dDesSel, &count, @encode(unsigned), &_cursor, nil); - if (info != _GSC_ARY_B) - { - [NSException raise: NSInternalInconsistencyException - format: @"expected array and got %s", typeToName2(info)]; - } - if (count != expected) - { - [NSException raise: NSInternalInconsistencyException - format: @"expected array count %u and got %u", - expected, count]; - } - - switch (*type) - { - case _C_ID: info = _GSC_NONE; break; - case _C_CHR: info = _GSC_CHR; break; - case _C_UCHR: info = _GSC_UCHR; break; - case _C_SHT: info = _GSC_SHT; break; - case _C_USHT: info = _GSC_USHT; break; - case _C_INT: info = _GSC_INT; break; - case _C_UINT: info = _GSC_UINT; break; - case _C_LNG: info = _GSC_LNG; break; - case _C_ULNG: info = _GSC_ULNG; break; -#ifdef _C_LNG_LNG - case _C_LNG_LNG: info = _GSC_LNG_LNG; break; - case _C_ULNG_LNG: info = _GSC_ULNG_LNG; break; -#endif - case _C_FLT: info = _GSC_FLT; break; - case _C_DBL: info = _GSC_DBL; break; - default: info = _GSC_NONE; break; - } - - if (info == _GSC_NONE) - { - for (i = 0; i < count; i++) - { - (*_dValImp)(self, dValSel, type, (char*)buf + offset); - offset += size; - } - } - else - { - unsigned char ainfo; - - (*_dTagImp)(_src, dTagSel, &ainfo, 0, &_cursor); - if (info != (ainfo & _GSC_MASK)) - { - [NSException raise: NSInternalInconsistencyException - format: @"expected %s and got %s", - typeToName2(info), typeToName2(ainfo)]; - } - - for (i = 0; i < count; i++) - { - (*_dDesImp)(_src, dDesSel, (char*)buf + offset, type, &_cursor, nil); - offset += size; - } - } -} - -- (NSData*) decodeDataObject -{ - int pos; - - [self decodeValueOfObjCType: @encode(int) at: &pos]; - if (pos >= 0) - { - return [_comp objectAtIndex: pos]; - } - else if (pos == -1) - { - return nil; - } - else if (pos == -2) - { - return [mutableDataClass data]; - } - else - { - [NSException raise: NSInternalInconsistencyException - format: @"Bad tag (%d) decoding data object", pos]; - return nil; - } -} - -- (NSPort*) decodePortObject -{ - unsigned pos; - - [self decodeValueOfObjCType: @encode(unsigned) at: &pos]; - return [_comp objectAtIndex: pos]; -} - -/* - * The [-decodeObject] method is implemented purely for performance - - * It duplicates the code for handling objects in the - * [-decodeValueOfObjCType:at:] method above, but differs in that the - * resulting object is autoreleased when it comes from this method. - */ -- (id) decodeObject -{ - unsigned char info; - unsigned xref; - id obj; - - (*_dTagImp)(_src, dTagSel, &info, &xref, &_cursor); - if ((info & _GSC_MASK) != _GSC_ID) - { - [NSException raise: NSInternalInconsistencyException - format: @"expected object and got %s", typeToName2(info)]; - } - - /* - * Special case - a zero crossref value is a nil pointer. - */ - if ((info & _GSC_SIZE) == 0) - { - return nil; - } - - if (info & _GSC_XREF) - { - if (xref >= GSIArrayCount(_objAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"object crossref missing - %d", - xref]; - } - obj = GSIArrayItemAtIndex(_objAry, xref).obj; - /* - * If it's a cross-reference, we don't need to autorelease it - * since we didn't create it. - */ - return obj; - } - else - { - Class c; - id rep; - - if (xref != GSIArrayCount(_objAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"extra object crossref - %d", - xref]; - } - (*_dValImp)(self, dValSel, @encode(Class), &c); - - obj = [c allocWithZone: _zone]; - GSIArrayAddItem(_objAry, (GSIArrayItem)obj); - - rep = [obj initWithCoder: self]; - if (rep != obj) - { - obj = rep; - GSIArraySetItemAtIndex(_objAry, (GSIArrayItem)obj, xref); - } - - rep = [obj awakeAfterUsingCoder: self]; - if (rep != obj) - { - obj = rep; - GSIArraySetItemAtIndex(_objAry, (GSIArrayItem)obj, xref); - } - /* - * A newly allocated object needs to be autoreleased. - */ - return AUTORELEASE(obj); - } -} - -- (void) decodeValueOfObjCType: (const char*)type - at: (void*)address -{ - unsigned xref; - unsigned char info; -#if GS_HAVE_I128 - gsu128 bigval; -#else -#if GS_HAVE_I64 - gsu64 bigval; -#else - gsu32 bigval; -#endif -#endif - - (*_dTagImp)(_src, dTagSel, &info, &xref, &_cursor); - - switch (info & _GSC_MASK) - { - case _GSC_ID: - { - id obj; - - typeCheck(*type, _GSC_ID); - /* - * Special case - a zero crossref value size is a nil pointer. - */ - if ((info & _GSC_SIZE) == 0) - { - obj = nil; - } - else - { - if (info & _GSC_XREF) - { - if (xref >= GSIArrayCount(_objAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"object crossref missing - %d", - xref]; - } - obj = GSIArrayItemAtIndex(_objAry, xref).obj; - /* - * If it's a cross-reference, we need to retain it in - * order to give the appearance that it's actually a - * new object. - */ - IF_NO_GC(RETAIN(obj)); - } - else - { - Class c; - id rep; - - if (xref != GSIArrayCount(_objAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"extra object crossref - %d", - xref]; - } - (*_dValImp)(self, dValSel, @encode(Class), &c); - - obj = [c allocWithZone: _zone]; - GSIArrayAddItem(_objAry, (GSIArrayItem)obj); - - rep = [obj initWithCoder: self]; - if (rep != obj) - { - obj = rep; - GSIArraySetItemAtIndex(_objAry, (GSIArrayItem)obj, xref); - } - - rep = [obj awakeAfterUsingCoder: self]; - if (rep != obj) - { - obj = rep; - GSIArraySetItemAtIndex(_objAry, (GSIArrayItem)obj, xref); - } - } - } - *(id*)address = obj; - return; - } - - case _GSC_CLASS: - { - Class c; - GSClassInfo *classInfo; - Class dummy; - - typeCheck(*type, _GSC_CLASS); - /* - * Special case - a zero crossref value size is a nil pointer. - */ - if ((info & _GSC_SIZE) == 0) - { - *(SEL*)address = 0; - return; - } - if (info & _GSC_XREF) - { - if (xref >= GSIArrayCount(_clsAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"class crossref missing - %d", xref]; - } - classInfo = (GSClassInfo*)GSIArrayItemAtIndex(_clsAry, xref).obj; - *(Class*)address = classInfo->class; - return; - } - while ((info & _GSC_MASK) == _GSC_CLASS) - { - unsigned cver; - - if (xref != GSIArrayCount(_clsAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"extra class crossref - %d", xref]; - } - (*_dDesImp)(_src, dDesSel, &c, @encode(Class), &_cursor, nil); - (*_dDesImp)(_src, dDesSel, &cver, @encode(unsigned), &_cursor, - nil); - if (c == 0) - { - [NSException raise: NSInternalInconsistencyException - format: @"decoded nil class"]; - } - classInfo = [GSClassInfo newWithClass: c andVersion: cver]; - GSIArrayAddItem(_clsAry, (GSIArrayItem)classInfo); - *(Class*)address = classInfo->class; - /* - * Point the address to a dummy location and read the - * next tag - if it is another class, loop to get it. - */ - address = &dummy; - (*_dTagImp)(_src, dTagSel, &info, &xref, &_cursor); - } - if (info != _GSC_NONE) - { - [NSException raise: NSInternalInconsistencyException - format: @"class list improperly terminated"]; - } - return; - } - - case _GSC_SEL: - { - SEL sel; - - typeCheck(*type, _GSC_SEL); - /* - * Special case - a zero crossref value size is a nil pointer. - */ - if ((info & _GSC_SIZE) == 0) - { - *(SEL*)address = 0; - return; - } - if (info & _GSC_XREF) - { - if (xref >= GSIArrayCount(_ptrAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"sel crossref missing - %d", xref]; - } - sel = GSIArrayItemAtIndex(_ptrAry, xref).sel; - } - else - { - if (xref != GSIArrayCount(_ptrAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"extra sel crossref - %d", xref]; - } - (*_dDesImp)(_src, dDesSel, &sel, @encode(SEL), &_cursor, nil); - GSIArrayAddItem(_ptrAry, (GSIArrayItem)sel); - } - *(SEL*)address = sel; - return; - } - - case _GSC_ARY_B: - { - int count; - - typeCheck(*type, _GSC_ARY_B); - count = atoi(++type); - while (isdigit(*type)) - { - type++; - } - [self decodeArrayOfObjCType: type count: count at: address]; - return; - } - - case _GSC_STRUCT_B: - { - int offset = 0; - - typeCheck(*type, _GSC_STRUCT_B); - while (*type != _C_STRUCT_E && *type++ != '='); /* skip "=" */ - for (;;) - { - (*_dValImp)(self, dValSel, type, (char*)address + offset); - offset += objc_sizeof_type(type); - type = objc_skip_typespec(type); - if (*type == _C_STRUCT_E) - { - break; - } - else - { - int align = objc_alignof_type(type); - int rem = offset % align; - - if (rem != 0) - { - offset += align - rem; - } - } - } - return; - } - - case _GSC_PTR: - { - typeCheck(*type, _GSC_PTR); - /* - * Special case - a zero crossref value size is a nil pointer. - */ - if ((info & _GSC_SIZE) == 0) - { - *(void**)address = 0; - return; - } - if (info & _GSC_XREF) - { - if (xref >= GSIArrayCount(_ptrAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"ptr crossref missing - %d", xref]; - } - *(void**)address = GSIArrayItemAtIndex(_ptrAry, xref).ptr; - } - else - { - unsigned size; - - if (GSIArrayCount(_ptrAry) != xref) - { - [NSException raise: NSInternalInconsistencyException - format: @"extra ptr crossref - %d", xref]; - } - - /* - * Allocate memory for object to be decoded into and - * add it to the crossref map. - */ - size = objc_sizeof_type(++type); - *(void**)address = _fastMallocBuffer(size); - GSIArrayAddItem(_ptrAry, (GSIArrayItem)*(void**)address); - - /* - * Decode value and add memory to map for crossrefs. - */ - (*_dValImp)(self, dValSel, type, *(void**)address); - } - return; - } - - case _GSC_CHARPTR: - { - typeCheck(*type, _GSC_CHARPTR); - /* - * Special case - a zero crossref value size is a nil pointer. - */ - if ((info & _GSC_SIZE) == 0) - { - *(char**)address = 0; - return; - } - if (info & _GSC_XREF) - { - if (xref >= GSIArrayCount(_ptrAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"string crossref missing - %d", xref]; - } - *(char**)address = GSIArrayItemAtIndex(_ptrAry, xref).str; - } - else - { - if (xref != GSIArrayCount(_ptrAry)) - { - [NSException raise: NSInternalInconsistencyException - format: @"extra string crossref - %d", xref]; - } - (*_dDesImp)(_src, dDesSel, address, @encode(char*), &_cursor, - nil); - GSIArrayAddItem(_ptrAry, (GSIArrayItem)*(void**)address); - } - return; - } - - case _GSC_CHR: - case _GSC_UCHR: - typeCheck(*type, info & _GSC_MASK); - (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); - return; - - case _GSC_SHT: - case _GSC_USHT: - typeCheck(*type, info & _GSC_MASK); - if ((info & _GSC_SIZE) == _GSC_S_SHT) - { - (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); - return; - } - break; - - case _GSC_INT: - case _GSC_UINT: - typeCheck(*type, info & _GSC_MASK); - if ((info & _GSC_SIZE) == _GSC_S_INT) - { - (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); - return; - } - break; - - case _GSC_LNG: - case _GSC_ULNG: - typeCheck(*type, info & _GSC_MASK); - if ((info & _GSC_SIZE) == _GSC_S_LNG) - { - (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); - return; - } - break; - -#ifdef _C_LNG_LNG - case _GSC_LNG_LNG: - case _GSC_ULNG_LNG: - typeCheck(*type, info & _GSC_MASK); - if ((info & _GSC_SIZE) == _GSC_S_LNG_LNG) - { - (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); - return; - } - break; - -#endif - case _GSC_FLT: - typeCheck(*type, _GSC_FLT); - (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); - return; - - case _GSC_DBL: - typeCheck(*type, _GSC_DBL); - (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); - return; - - default: - [NSException raise: NSInternalInconsistencyException - format: @"read unknown type info - %d", info]; - } - - /* - * We fall through to here only when we have to decode a value - * whose natural size on this system is not the same as on the - * machine on which the archive was created. - */ - - /* - * First, we read the data and convert it to the largest size - * this system can support. - */ - switch (info & _GSC_SIZE) - { - case _GSC_I16: /* Encoded as 16-bit */ - { - gsu16 val; - - (*_dDesImp)(_src, dDesSel, &val, @encode(gsu16), &_cursor, nil); - bigval = val; - break; - } - - case _GSC_I32: /* Encoded as 32-bit */ - { - gsu32 val; - - (*_dDesImp)(_src, dDesSel, &val, @encode(gsu32), &_cursor, nil); - bigval = val; - break; - } - - case _GSC_I64: /* Encoded as 64-bit */ - { - gsu64 val; - - (*_dDesImp)(_src, dDesSel, &val, @encode(gsu64), &_cursor, nil); -#if GS_HAVE_I64 - bigval = val; -#else - bigval = GSSwapBigI64ToHost(val); -#endif - break; - } - - default: /* A 128-bit value */ - { - gsu128 val; - - (*_dDesImp)(_src, dDesSel, &val, @encode(gsu128), &_cursor, nil); -#if GS_HAVE_I128 - bigval = val; -#else - val = GSSwapBigI128ToHost(val); -#if GS_HAVE_I64 - bigval = *(gsu64*)&val; -#else - bigval = *(gsu32*)&val; -#endif -#endif - break; - } - } - -/* - * Now we copy from the 'bigval' to the destination location. - */ - switch (info & _GSC_MASK) - { - case _GSC_SHT: - *(short*)address = (short)bigval; - return; - case _GSC_USHT: - *(unsigned short*)address = (unsigned short)bigval; - return; - case _GSC_INT: - *(int*)address = (int)bigval; - return; - case _GSC_UINT: - *(unsigned int*)address = (unsigned int)bigval; - return; - case _GSC_LNG: - *(long*)address = (long)bigval; - return; - case _GSC_ULNG: - *(unsigned long*)address = (unsigned long)bigval; - return; -#ifdef _C_LNG_LNG - case _GSC_LNG_LNG: - *(long long*)address = (long long)bigval; - return; - case _GSC_ULNG_LNG: - *(unsigned long long*)address = (unsigned long long)bigval; - return; -#endif - default: - [NSException raise: NSInternalInconsistencyException - format: @"type/size information error"]; - } -} - -- (void) dispatch -{ - /* - * Get ready for re-use - * Make sure that we don't retain the connection - or it might never be - * released if it is keeping this coder in a cache. - */ - DESTROY(_conn); -} - -- (void) encodeArrayOfObjCType: (const char*)type - count: (unsigned)count - at: (const void*)buf -{ - unsigned i; - unsigned offset = 0; - unsigned size = objc_sizeof_type(type); - uchar info; - - switch (*type) - { - case _C_ID: info = _GSC_NONE; break; - case _C_CHR: info = _GSC_CHR; break; - case _C_UCHR: info = _GSC_UCHR; break; - case _C_SHT: info = _GSC_SHT | _GSC_S_SHT; break; - case _C_USHT: info = _GSC_USHT | _GSC_S_SHT; break; - case _C_INT: info = _GSC_INT | _GSC_S_INT; break; - case _C_UINT: info = _GSC_UINT | _GSC_S_INT; break; - case _C_LNG: info = _GSC_LNG | _GSC_S_LNG; break; - case _C_ULNG: info = _GSC_ULNG | _GSC_S_LNG; break; - case _C_LNG_LNG: info = _GSC_LNG_LNG | _GSC_S_LNG_LNG; break; - case _C_ULNG_LNG: info = _GSC_ULNG_LNG | _GSC_S_LNG_LNG; break; - case _C_FLT: info = _GSC_FLT; break; - case _C_DBL: info = _GSC_DBL; break; - default: info = _GSC_NONE; break; - } - - /* - * Simple types can be serialized immediately, more complex ones - * are dealt with by our [encodeValueOfObjCType:at:] method. - */ - if (info == _GSC_NONE) - { - if (_initialPass == NO) - { - (*_eTagImp)(_dst, eTagSel, _GSC_ARY_B); - (*_eSerImp)(_dst, eSerSel, &count, @encode(unsigned), nil); - } - for (i = 0; i < count; i++) - { - (*_eValImp)(self, eValSel, type, (char*)buf + offset); - offset += size; - } - } - else if (_initialPass == NO) - { - (*_eTagImp)(_dst, eTagSel, _GSC_ARY_B); - (*_eSerImp)(_dst, eSerSel, &count, @encode(unsigned), nil); - - (*_eTagImp)(_dst, eTagSel, info); - for (i = 0; i < count; i++) - { - (*_eSerImp)(_dst, eSerSel, (char*)buf + offset, type, nil); - offset += size; - } - } -} - -- (void) encodeBycopyObject: (id)anObj -{ - BOOL oldBycopy = _is_by_copy; - BOOL oldByref = _is_by_ref; - - _is_by_copy = YES; - _is_by_ref = NO; - (*_eObjImp)(self, eObjSel, anObj); - _is_by_copy = oldBycopy; - _is_by_ref = oldByref; -} - -- (void) encodeByrefObject: (id)anObj -{ - BOOL oldBycopy = _is_by_copy; - BOOL oldByref = _is_by_ref; - - _is_by_copy = NO; - _is_by_ref = YES; - (*_eObjImp)(self, eObjSel, anObj); - _is_by_copy = oldBycopy; - _is_by_ref = oldByref; -} - -- (void) encodeConditionalObject: (id)anObject -{ - if (_encodingRoot == NO) - { - [NSException raise: NSInvalidArgumentException - format: @"conditionally encoding without root object"]; - return; - } - - if (_initialPass) - { - GSIMapNode node; - - /* - * Conditionally encoding 'nil' is a no-op. - */ - if (anObject == nil) - { - return; - } - - /* - * If we have already conditionally encoded this object, we can - * ignore it this time. - */ - node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject); - if (node != 0) - { - return; - } - - /* - * If we have unconditionally encoded this object, we can ignore - * it now. - */ - node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject); - if (node != 0) - { - return; - } - - GSIMapAddPair(_cIdMap, (GSIMapKey)anObject, (GSIMapVal)0); - } - else if (anObject == nil) - { - (*_eObjImp)(self, eObjSel, nil); - } - else - { - GSIMapNode node; - - node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject); - if (node != 0) - { - (*_eObjImp)(self, eObjSel, nil); - } - else - { - (*_eObjImp)(self, eObjSel, anObject); - } - } -} - -/* - * When asked to encode a data object, we add the object to the components - * array, and simply record the array index, so the corresponding decode - * method can tell which component to use. - */ -- (void) encodeDataObject: (NSData*)anObject -{ - int pos; - - if (anObject == nil) - { - pos = -1; - } - else if ([anObject length] == 0) - { - pos = -2; - } - else - { - pos = (int)[_comp count]; - [_comp addObject: anObject]; - } - [self encodeValueOfObjCType: @encode(int) at: &pos]; -} - -- (void) encodeObject: (id)anObject -{ - if (anObject == nil) - { - if (_initialPass == NO) - { - /* - * Special case - encode a nil pointer as a crossref of zero. - */ - (*_eTagImp)(_dst, eTagSel, _GSC_ID | _GSC_XREF, _GSC_X_0); - } - } - else if (fastIsInstance(anObject) == NO) - { - /* - * If the object we have been given is actually a class, - * we encode it as a class instead. - */ - (*_eValImp)(self, eValSel, @encode(Class), &anObject); - } - else - { - GSIMapNode node; - - /* - * See if the object has already been encoded. - */ - node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject); - - if (_initialPass) - { - if (node == 0) - { - /* - * Remove object from map of conditionally encoded objects - * and add it to the map of unconditionay encoded ones. - */ - GSIMapRemoveKey(_cIdMap, (GSIMapKey)anObject); - GSIMapAddPair(_uIdMap, (GSIMapKey)anObject, (GSIMapVal)0); - [anObject encodeWithCoder: self]; - } - return; - } - - if (node == 0 || node->value.uint == 0) - { - Class cls; - id obj; - - if (node == 0) - { - node = GSIMapAddPair(_uIdMap, - (GSIMapKey)anObject, (GSIMapVal)++_xRefO); - } - else - { - node->value.uint = ++_xRefO; - } - - obj = [anObject replacementObjectForPortCoder: self]; - cls = [obj classForPortCoder]; - - (*_xRefImp)(_dst, xRefSel, _GSC_ID, node->value.uint); - (*_eValImp)(self, eValSel, @encode(Class), &cls); - [obj encodeWithCoder: self]; - } - else - { - (*_xRefImp)(_dst, xRefSel, _GSC_ID | _GSC_XREF, node->value.uint); - } - } -} - -/* - * When asked to encode a port object, we add the object to the components - * array, and simply record the array index, so the corresponding decode - * method can tell which component to use. - */ -- (void) encodePortObject: (NSPort*)anObject -{ - unsigned pos = [_comp count]; - - [_comp addObject: anObject]; - [self encodeValueOfObjCType: @encode(unsigned) at: &pos]; -} - -- (void) encodeRootObject: (id)rootObject -{ - if (_encodingRoot) - { - [NSException raise: NSInvalidArgumentException - format: @"encoding root object more than once"]; - } - - _encodingRoot = YES; - - /* - * First pass - find conditional objects. - */ - _initialPass = YES; - (*_eObjImp)(self, eObjSel, rootObject); - - /* - * Second pass - write archive. - */ - _initialPass = NO; - (*_eObjImp)(self, eObjSel, rootObject); - - /* - * Write sizes of crossref arrays to head of archive. - */ - [self _serializeHeaderAt: _cursor - version: [self systemVersion] - classes: _clsMap->nodeCount - objects: _uIdMap->nodeCount - pointers: _ptrMap->nodeCount]; - - _encodingRoot = NO; -} - -- (void) encodeValueOfObjCType: (const char*)type - at: (const void*)buf -{ - switch (*type) - { - case _C_ID: - (*_eObjImp)(self, eObjSel, *(void**)buf); - return; - - case _C_ARY_B: - { - int count = atoi(++type); - - while (isdigit(*type)) - { - type++; - } - - if (_initialPass == NO) - { - (*_eTagImp)(_dst, eTagSel, _GSC_ARY_B); - } - - [self encodeArrayOfObjCType: type count: count at: buf]; - } - return; - - case _C_STRUCT_B: - { - int offset = 0; - - if (_initialPass == NO) - { - (*_eTagImp)(_dst, eTagSel, _GSC_STRUCT_B); - } - - while (*type != _C_STRUCT_E && *type++ != '='); /* skip "=" */ - - for (;;) - { - (*_eValImp)(self, eValSel, type, (char*)buf + offset); - offset += objc_sizeof_type(type); - type = objc_skip_typespec(type); - if (*type == _C_STRUCT_E) - { - break; - } - else - { - int align = objc_alignof_type(type); - int rem = offset % align; - - if (rem != 0) - { - offset += align - rem; - } - } - } - } - return; - - case _C_PTR: - if (*(void**)buf == 0) - { - if (_initialPass == NO) - { - /* - * Special case - a nul pointer gets an xref of zero - */ - (*_eTagImp)(_dst, eTagSel, _GSC_PTR | _GSC_XREF | _GSC_X_0); - } - } - else - { - GSIMapNode node; - - node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)*(void**)buf); - if (_initialPass == YES) - { - /* - * First pass - add pointer to map and encode item pointed - * to in case it is a conditionally encoded object. - */ - if (node == 0) - { - GSIMapAddPair(_ptrMap, - (GSIMapKey)*(void**)buf, (GSIMapVal)0); - type++; - buf = *(char**)buf; - (*_eValImp)(self, eValSel, type, buf); - } - } - else if (node == 0 || node->value.uint == 0) - { - /* - * Second pass, unwritten pointer - write it. - */ - if (node == 0) - { - node = GSIMapAddPair(_ptrMap, - (GSIMapKey)*(void**)buf, (GSIMapVal)++_xRefP); - } - else - { - node->value.uint = ++_xRefP; - } - (*_xRefImp)(_dst, xRefSel, _GSC_PTR, node->value.uint); - type++; - buf = *(char**)buf; - (*_eValImp)(self, eValSel, type, buf); - } - else - { - /* - * Second pass, write a cross-reference number. - */ - (*_xRefImp)(_dst, xRefSel, _GSC_PTR|_GSC_XREF, - node->value.uint); - } - } - return; - - default: /* Types that can be ignored in first pass. */ - if (_initialPass) - { - return; - } - break; - } - - switch (*type) - { - case _C_CLASS: - if (*(Class*)buf == 0) - { - /* - * Special case - a nul pointer gets an xref of zero - */ - (*_eTagImp)(_dst, eTagSel, _GSC_CLASS | _GSC_XREF | _GSC_X_0); - } - else - { - Class c = *(Class*)buf; - GSIMapNode node; - BOOL done = NO; - - node = GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)c); - - if (node != 0) - { - (*_xRefImp)(_dst, xRefSel, _GSC_CLASS | _GSC_XREF, - node->value.uint); - return; - } - while (done == NO) - { - int tmp = fastClassVersion(c); - unsigned version = tmp; - Class s = fastSuper(c); - - if (tmp < 0) - { - [NSException raise: NSInternalInconsistencyException - format: @"negative class version"]; - } - node = GSIMapAddPair(_clsMap, - (GSIMapKey)(void*)c, (GSIMapVal)++_xRefC); - /* - * Encode tag and crossref number. - */ - (*_xRefImp)(_dst, xRefSel, _GSC_CLASS, node->value.uint); - /* - * Encode class, and version. - */ - (*_eSerImp)(_dst, eSerSel, &c, @encode(Class), nil); - (*_eSerImp)(_dst, eSerSel, &version, @encode(unsigned), nil); - /* - * If we have a super class that has not been encoded, - * we must loop round to encode it here so that its - * version information will be available when objects - * of its subclasses are decoded and call - * [super initWithCoder:ccc] - */ - if (s == c || s == 0 - || GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)s) != 0) - { - done = YES; - } - else - { - c = s; - } - } - /* - * Encode an empty tag to terminate the list of classes. - */ - (*_eTagImp)(_dst, eTagSel, _GSC_NONE); - } - return; - - case _C_SEL: - if (*(SEL*)buf == 0) - { - /* - * Special case - a nul pointer gets an xref of zero - */ - (*_eTagImp)(_dst, eTagSel, _GSC_SEL | _GSC_XREF | _GSC_X_0); - } - else - { - SEL s = *(SEL*)buf; - GSIMapNode node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)(void*)s); - - if (node == 0) - { - node = GSIMapAddPair(_ptrMap, - (GSIMapKey)(void*)s, (GSIMapVal)++_xRefP); - (*_xRefImp)(_dst, xRefSel, _GSC_SEL, node->value.uint); - /* - * Encode selector. - */ - (*_eSerImp)(_dst, eSerSel, buf, @encode(SEL), nil); - } - else - { - (*_xRefImp)(_dst, xRefSel, _GSC_SEL|_GSC_XREF, - node->value.uint); - } - } - return; - - case _C_CHARPTR: - if (*(char**)buf == 0) - { - /* - * Special case - a nul pointer gets an xref of zero - */ - (*_eTagImp)(_dst, eTagSel, _GSC_CHARPTR | _GSC_XREF | _GSC_X_0); - } - else - { - GSIMapNode node; - - node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)*(char**)buf); - if (node == 0) - { - node = GSIMapAddPair(_ptrMap, - (GSIMapKey)*(char**)buf, (GSIMapVal)++_xRefP); - (*_xRefImp)(_dst, xRefSel, _GSC_CHARPTR, node->value.uint); - (*_eSerImp)(_dst, eSerSel, buf, type, nil); - } - else - { - (*_xRefImp)(_dst, xRefSel, _GSC_CHARPTR|_GSC_XREF, - node->value.uint); - } - } - return; - - case _C_CHR: - (*_eTagImp)(_dst, eTagSel, _GSC_CHR); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(char), nil); - return; - - case _C_UCHR: - (*_eTagImp)(_dst, eTagSel, _GSC_UCHR); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned char), nil); - return; - - case _C_SHT: - (*_eTagImp)(_dst, eTagSel, _GSC_SHT | _GSC_S_SHT); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(short), nil); - return; - - case _C_USHT: - (*_eTagImp)(_dst, eTagSel, _GSC_USHT | _GSC_S_SHT); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned short), nil); - return; - - case _C_INT: - (*_eTagImp)(_dst, eTagSel, _GSC_INT | _GSC_S_INT); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(int), nil); - return; - - case _C_UINT: - (*_eTagImp)(_dst, eTagSel, _GSC_UINT | _GSC_S_INT); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned int), nil); - return; - - case _C_LNG: - (*_eTagImp)(_dst, eTagSel, _GSC_LNG | _GSC_S_LNG); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(long), nil); - return; - - case _C_ULNG: - (*_eTagImp)(_dst, eTagSel, _GSC_ULNG | _GSC_S_LNG); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned long), nil); - return; - - case _C_LNG_LNG: - (*_eTagImp)(_dst, eTagSel, _GSC_LNG_LNG | _GSC_S_LNG_LNG); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(long long), nil); - return; - - case _C_ULNG_LNG: - (*_eTagImp)(_dst, eTagSel, _GSC_ULNG_LNG | _GSC_S_LNG_LNG); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned long long), - nil); - return; - - case _C_FLT: - (*_eTagImp)(_dst, eTagSel, _GSC_FLT); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(float), nil); - return; - - case _C_DBL: - (*_eTagImp)(_dst, eTagSel, _GSC_DBL); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(double), nil); - return; - - case _C_VOID: - [NSException raise: NSInvalidArgumentException - format: @"can't encode void item"]; - - default: - [NSException raise: NSInvalidArgumentException - format: @"item with unknown type - %s", type]; - } -} - -- (id) initWithReceivePort: (NSPort*)recv - sendPort: (NSPort*)send - components: (NSArray*)comp -{ - BOOL firstTime; - - _conn = RETAIN([connectionClass connectionWithReceivePort: recv - sendPort: send]); - if (_comp == nil) - { - firstTime = YES; - _version = [super systemVersion]; - _zone = NSDefaultMallocZone(); - } - else - { - NSAssert(recv == [_conn receivePort] && send == [_conn sendPort], - NSInvalidArgumentException); - /* - * Re-initialising - destroy old components. - */ - firstTime = NO; - } - - if (comp == nil) - { - NS_DURING - { - _encodingRoot = NO; - _initialPass = NO; - _xRefC = 0; - _xRefO = 0; - _xRefP = 0; - - _cursor = [send reservedSpaceLength]; - if (firstTime == YES) - { - /* - * Set up mutable data object to encode into - reserve space at - * the start for use by the port when the encoded data is sent. - * Make the data item the first component of the array. - */ - _comp = [mutableArrayClass new]; - _dst = [mutableDataClass allocWithZone: _zone]; - _dst = [_dst initWithLength: _cursor]; - [_comp addObject: _dst]; - RELEASE(_dst); - - /* - * Cache method implementations for writing into data object etc - */ - _eSerImp = [_dst methodForSelector: eSerSel]; - _eTagImp = [_dst methodForSelector: eTagSel]; - _xRefImp = [_dst methodForSelector: xRefSel]; - _eObjImp = [self methodForSelector: eObjSel]; - _eValImp = [self methodForSelector: eValSel]; - - /* - * Set up map tables. - */ - _clsMap - = (GSIMapTable)NSZoneMalloc(_zone, sizeof(GSIMapTable_t)*4); - _cIdMap = &_clsMap[1]; - _uIdMap = &_clsMap[2]; - _ptrMap = &_clsMap[3]; - GSIMapInitWithZoneAndCapacity(_clsMap, _zone, 100); - GSIMapInitWithZoneAndCapacity(_cIdMap, _zone, 10); - GSIMapInitWithZoneAndCapacity(_uIdMap, _zone, 200); - GSIMapInitWithZoneAndCapacity(_ptrMap, _zone, 100); - } - else - { - unsigned count; - - /* - * If re-initialising, we just need to empty the old stuff. - */ - count = [_comp count]; - while (count-- > 1) - { - [_comp removeObjectAtIndex: count]; - } - [_dst setLength: _cursor]; - GSIMapCleanMap(_clsMap); - GSIMapCleanMap(_cIdMap); - GSIMapCleanMap(_uIdMap); - GSIMapCleanMap(_ptrMap); - } - - /* - * Write dummy header - */ - [self _serializeHeaderAt: _cursor - version: 0 - classes: 0 - objects: 0 - pointers: 0]; - } - NS_HANDLER - { - NSLog(@"Exception setting up port coder for encoding - %@", - localException); - DESTROY(self); - } - NS_ENDHANDLER - } - else - { - RELEASE(_comp); - _comp = [comp mutableCopy]; - NS_DURING - { - unsigned sizeC; - unsigned sizeO; - unsigned sizeP; - - if (firstTime == YES) - { - _dValImp = [self methodForSelector: dValSel]; - } - _src = [_comp objectAtIndex: 0]; - _dDesImp = [_src methodForSelector: dDesSel]; - _dTagImp = (void (*)(id, SEL, unsigned char*, unsigned*, unsigned*)) - [_src methodForSelector: dTagSel]; - - /* - * _cInfo is a dictionary of objects for keeping track of the - * version numbers that the classes were encoded with. - */ - if (firstTime == NO) - { - [_cInfo removeAllObjects]; - } - - /* - * Read header including version and crossref table sizes. - */ - _cursor = 0; - [self _deserializeHeaderAt: &_cursor - version: &_version - classes: &sizeC - objects: &sizeO - pointers: &sizeP]; - - /* - * Allocate and initialise arrays to build crossref maps in. - */ - if (firstTime == YES) - { - _clsAry = NSZoneMalloc(_zone, sizeof(GSIArray_t)*3); - _objAry = &_clsAry[1]; - _ptrAry = &_clsAry[2]; - GSIArrayInitWithZoneAndCapacity(_clsAry, _zone, sizeC); - GSIArrayInitWithZoneAndCapacity(_objAry, _zone, sizeO); - GSIArrayInitWithZoneAndCapacity(_ptrAry, _zone, sizeP); - } - else - { - unsigned count = GSIArrayCount(_clsAry); - - while (count-- > 0) - { - RELEASE(GSIArrayItemAtIndex(_clsAry, count).obj); - } - GSIArrayRemoveAllItems(_clsAry); - GSIArrayRemoveAllItems(_objAry); - GSIArrayRemoveAllItems(_ptrAry); - } - GSIArrayAddItem(_clsAry, (GSIArrayItem)nil); - GSIArrayAddItem(_objAry, (GSIArrayItem)nil); - GSIArrayAddItem(_ptrAry, (GSIArrayItem)0); - } - NS_HANDLER - { - NSLog(@"Exception setting up port coder for decoding - %@", - localException); - DESTROY(self); - } - NS_ENDHANDLER - } - return self; -} - -- (BOOL) isBycopy -{ - return _is_by_copy; -} - -- (BOOL) isByref -{ - return _is_by_ref; -} - -- (NSZone*) objectZone -{ - return _zone; -} - -- (void) setObjectZone: (NSZone*)aZone -{ - _zone = aZone; -} - -- (unsigned) systemVersion -{ - return _version; -} - -- (unsigned) versionForClassName: (NSString*)className -{ - GSClassInfo *info = nil; - unsigned version = NSNotFound; - unsigned count = GSIArrayCount(_clsAry); - - /* - * Lazy ... we construct a dictionary of all the class information in - * the request the first time that class version info is asked for. - */ - if (_cInfo == nil) - { - _cInfo = [[mutableDictionaryClass alloc] initWithCapacity: count]; - } - if ([_cInfo count] == 0) - { - while (count-- > 0) - { - info = GSIArrayItemAtIndex(_clsAry, count).obj; - [_cInfo setObject: info forKey: NSStringFromClass(info->class)]; - } - } - info = [_cInfo objectForKey: className]; - if (info != nil) - { - version = info->version; - } - return version; -} - -@end - - - -@implementation NSPortCoder (Private) - -- (NSMutableArray*) _components -{ - return _comp; -} - -@end - -@implementation NSPortCoder (Headers) - -- (void) _deserializeHeaderAt: (unsigned*)pos - version: (unsigned*)v - classes: (unsigned*)c - objects: (unsigned*)o - pointers: (unsigned*)p -{ - unsigned plen = strlen(PREFIX); - unsigned size = plen+36; - char header[size+1]; - - [_src getBytes: header range: NSMakeRange(*pos, size)]; - *pos += size; - header[size] = '\0'; - if (strncmp(header, PREFIX, plen) != 0) - { - [NSException raise: NSInternalInconsistencyException - format: @"Archive has wrong prefix"]; - } - if (sscanf(&header[plen], "%x:%x:%x:%x:", v, c, o, p) != 4) - { - [NSException raise: NSInternalInconsistencyException - format: @"Archive has wrong prefix"]; - } -} - -- (void) _serializeHeaderAt: (unsigned)locationInData - version: (unsigned)v - classes: (unsigned)cc - objects: (unsigned)oc - pointers: (unsigned)pc -{ - unsigned headerLength = strlen(PREFIX)+36; - char header[headerLength+1]; - unsigned dataLength = [_dst length]; - - sprintf(header, "%s%08x:%08x:%08x:%08x:", PREFIX, v, cc, oc, pc); - - if (locationInData + headerLength <= dataLength) - { - [_dst replaceBytesInRange: NSMakeRange(locationInData, headerLength) - withBytes: header]; - } - else if (locationInData == dataLength) - { - [_dst appendBytes: header length: headerLength]; - } - else - { - [NSException raise: NSInternalInconsistencyException - format: @"serializeHeader:at: bad location"]; - } -} - -@end - diff --git a/Source/Makefile.preamble b/Source/Makefile.preamble index 3e1f29a85..1ed3d51f0 100644 --- a/Source/Makefile.preamble +++ b/Source/Makefile.preamble @@ -39,7 +39,7 @@ # # Additional flags to pass to the preprocessor -ADDITIONAL_CPPFLAGS = $(DEFS) -Wall +ADDITIONAL_CPPFLAGS = $(DEFS) -Wall -DGS_NEW_DO=0 # Additional flags to pass to the Objective-C compiler ADDITIONAL_OBJCFLAGS = diff --git a/Source/NSConnection.m b/Source/NSConnection.m index db03c17b6..c9e2f854b 100644 --- a/Source/NSConnection.m +++ b/Source/NSConnection.m @@ -1,3 +1,2448 @@ +#if GS_NEW_DO +/* Implementation of connection object for remote object messaging + Copyright (C) 1994, 1995, 1996, 1997, 2000 Free Software Foundation, Inc. + + Created by: Andrew Kachites McCallum + Date: July 1994 + Minor rewrite for OPENSTEP by: Richard Frith-Macdonald + Date: August 1997 + Major rewrite for MACOSX by: Richard Frith-Macdonald + Date: 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 +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define F_LOCK(X) {NSDebugFLLog(@"GSConnection",@"Lock %@",X);[X lock];} +#define F_UNLOCK(X) {NSDebugFLLog(@"GSConnection",@"Unlock %@",X);[X unlock];} +#define M_LOCK(X) {NSDebugMLLog(@"GSConnection",@"Lock %@",X);[X lock];} +#define M_UNLOCK(X) {NSDebugMLLog(@"GSConnection",@"Unlock %@",X);[X unlock];} + +static Class connectionClass; +static Class dateClass; +static Class distantObjectClass; +static Class portCoderClass; +static Class runLoopClass; + +static NSString* +stringFromMsgType(int type) +{ + switch (type) + { + case METHOD_REQUEST: + return @"method request"; + case METHOD_REPLY: + return @"method reply"; + case ROOTPROXY_REQUEST: + return @"root proxy request"; + case ROOTPROXY_REPLY: + return @"root proxy reply"; + case CONNECTION_SHUTDOWN: + return @"connection shutdown"; + case METHODTYPE_REQUEST: + return @"methodtype request"; + case METHODTYPE_REPLY: + return @"methodtype reply"; + case PROXY_RELEASE: + return @"proxy release"; + case PROXY_RETAIN: + return @"proxy retain"; + case RETAIN_REPLY: + return @"retain replay"; + default: + return @"unknown operation type!"; + } +} + +@interface NSDistantObject (NSConnection) +- (id) localForProxy; +- (void) setProxyTarget: (unsigned)target; +- (unsigned) targetForProxy; +@end + +@implementation NSDistantObject (NSConnection) +- (id) localForProxy +{ + return _object; +} +- (void) setProxyTarget: (unsigned)target +{ + _handle = target; +} +- (unsigned) targetForProxy +{ + return _handle; +} +@end + +/* + * GSLocalCounter is a trivial class to keep track of how + * many different connections a particular local object is vended + * over. This is required so that we know when to remove an object + * from the global list when it is removed from the list of objects + * vended on a particular connection. + */ +@interface GSLocalCounter : NSObject +{ +@public + unsigned ref; + unsigned target; + id object; +} ++ (id) newWithObject: (id)ob; +@end + +@implementation GSLocalCounter + +static unsigned local_object_counter = 0; + ++ (id) newWithObject: (id)obj +{ + GSLocalCounter *counter; + + counter = (GSLocalCounter*)NSAllocateObject(self, 0, NSDefaultMallocZone()); + counter->ref = 1; + counter->object = RETAIN(obj); + counter->target = ++local_object_counter; + return counter; +} +- (void) dealloc +{ + RELEASE(object); + NSDeallocateObject(self); +} +@end + + + +/* + * CachedLocalObject is a trivial class to keep track of how + * many different connections a particular local object is vended + * over. This is required so that we know when to remove an object + * from the global list when it is removed from the list of objects + * vended on a particular connection. + */ +@interface CachedLocalObject : NSObject +{ + id obj; + int time; +} +- (BOOL)countdown; +- (id) obj; ++ (id) newWithObject: (id)o time: (int)t; +@end + +@implementation CachedLocalObject + ++ (id) newWithObject: (id)o time: (int)t +{ + CachedLocalObject *item; + + item = (CachedLocalObject*)NSAllocateObject(self, 0, NSDefaultMallocZone()); + item->obj = RETAIN(o); + item->time = t; + return item; +} + +- (void) dealloc +{ + RELEASE(obj); + NSDeallocateObject(self); +} + +- (BOOL) countdown +{ + if (time-- > 0) + return YES; + return NO; +} + +- (id) obj +{ + return obj; +} + +@end + + + +@interface NSConnection (Private) +- (void) handlePortMessage: (NSPortMessage*)msg; +- (void) _runInNewThread; ++ (void) setDebug: (int)val; + +- (void) _doneInRmc: (NSPortCoder*)c; +- (NSPortCoder*) _getReplyRmc: (int)sn; +- (NSPortCoder*) _makeInRmc: (NSMutableArray*)components; +- (NSPortCoder*) _makeOutRmc: (int)sequence; +- (int) _newMsgNumber; +- (void) _sendOutRmc: (NSPortCoder*)c type: (int)msgid; + +- (void) _service_forwardForProxy: (NSPortCoder*)rmc; +- (void) _service_release: (NSPortCoder*)rmc; +- (void) _service_retain: (NSPortCoder*)rmc; +- (void) _service_rootObject: (NSPortCoder*)rmc; +- (void) _service_shutdown: (NSPortCoder*)rmc; +- (void) _service_typeForSelector: (NSPortCoder*)rmc; +@end + +#define _proxiesGate _refGate +#define _queueGate _refGate +#define sequenceNumberGate _refGate + + +/* class defaults */ +static NSTimer *timer; + +static int debug_connection = 0; + +static NSHashTable *connection_table; +static NSLock *connection_table_gate; + +/* + * Locate an existing connection with the specified send and receive ports. + * nil ports act as wildcards and return the first match. + */ +static NSConnection* +existingConnection(NSPort *receivePort, NSPort *sendPort) +{ + NSHashEnumerator enumerator; + NSConnection *c; + + F_LOCK(connection_table_gate); + enumerator = NSEnumerateHashTable(connection_table); + while ((c = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) + { + if ((sendPort == nil || [sendPort isEqual: [c sendPort]]) + && (receivePort == nil || [receivePort isEqual: [c receivePort]])) + { + /* + * We don't want this connection to be destroyed by another thread + * between now and when it's returned from this function and used! + */ + AUTORELEASE(RETAIN(c)); + break; + } + } + F_UNLOCK(connection_table_gate); + return c; +} + +static NSMapTable *root_object_map; +static NSLock *root_object_map_gate; + +static id +rootObjectForInPort(NSPort *aPort) +{ + id rootObject; + + F_LOCK(root_object_map_gate); + rootObject = (id)NSMapGet(root_object_map, (void*)(gsaddr)aPort); + F_UNLOCK(root_object_map_gate); + return rootObject; +} + +/* Pass nil to remove any reference keyed by aPort. */ +static void +setRootObjectForInPort(id anObj, NSPort *aPort) +{ + id oldRootObject; + + F_LOCK(root_object_map_gate); + oldRootObject = (id)NSMapGet(root_object_map, (void*)(gsaddr)aPort); + if (oldRootObject != anObj) + { + if (anObj != nil) + { + NSMapInsert(root_object_map, (void*)(gsaddr)aPort, + (void*)(gsaddr)anObj); + } + else /* anObj == nil && oldRootObject != nil */ + { + NSMapRemove(root_object_map, (void*)(gsaddr)aPort); + } + } + F_UNLOCK(root_object_map_gate); +} + +static NSMapTable *all_connections_local_objects = NULL; +static NSMapTable *all_connections_local_targets = NULL; +static NSMapTable *all_connections_local_cached = NULL; +static NSLock *global_proxies_gate; + + + + +@implementation NSConnection + ++ (NSArray*) allConnections +{ + return NSAllHashTableObjects(connection_table); +} + ++ (NSConnection*) connectionWithReceivePort: (NSPort*)r + sendPort: (NSPort*)s +{ + NSConnection *c = existingConnection(r, s); + + if (c == nil) + { + c = [self allocWithZone: NSDefaultMallocZone()]; + c = [c initWithReceivePort: r sendPort: s]; + AUTORELEASE(c); + } + return c; +} + ++ (NSConnection*) connectionWithRegisteredName: (NSString*)n + host: (NSString*)h +{ + NSPortNameServer *s; + + s = [NSPortNameServer systemDefaultPortNameServer]; + return [self connectionWithRegisteredName: n + host: h + usingNameServer: s]; +} + ++ (NSConnection*) connectionWithRegisteredName: (NSString*)n + host: (NSString*)h + usingNameServer: (NSPortNameServer*)s +{ + NSConnection *con = nil; + + if (s != nil) + { + NSPort *sendPort = [s portForName: n onHost: h]; + + if (sendPort != nil) + { + con = existingConnection(nil, sendPort); + if (con == nil) + { + NSPort *recvPort; + + recvPort = [[self defaultConnection] receivePort]; + con = [self connectionWithReceivePort: recvPort + sendPort: sendPort]; + } + } + } + return con; +} + ++ (id) currentConversation +{ + [self notImplemented: _cmd]; + return self; +} + +/* + * Get the default connection for a thread. + * Possible problem - if the connection is invalidated, it won't be + * cleaned up until this thread calls this method again. The connection + * and it's ports could hang around for a very long time. + */ ++ (NSConnection*) defaultConnection +{ + static NSString *tkey = @"NSConnectionThreadKey"; + NSConnection *c; + NSMutableDictionary *d; + + d = GSCurrentThreadDictionary(); + c = (NSConnection*)[d objectForKey: tkey]; + if (c != nil && [c isValid] == NO) + { + /* + * If the default connection for this thread has been invalidated - + * release it and create a new one. + */ + [d removeObjectForKey: tkey]; + c = nil; + } + if (c == nil) + { + NSPort *port; + + c = [self alloc]; + port = [NSPort port]; + c = [c initWithReceivePort: port sendPort: nil]; + [d setObject: c forKey: tkey]; + RELEASE(c); + } + return c; +} + ++ (void) initialize +{ + if (self == [NSConnection class]) + { + connectionClass = self; + dateClass = [NSDate class]; + distantObjectClass = [NSDistantObject class]; + portCoderClass = [NSPortCoder class]; + runLoopClass = [NSRunLoop class]; + + connection_table = + NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); + connection_table_gate = [NSLock new]; + /* xxx When NSHashTable's are working, change this. */ + all_connections_local_objects = + NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + all_connections_local_targets = + NSCreateMapTable(NSIntMapKeyCallBacks, + NSNonOwnedPointerMapValueCallBacks, 0); + all_connections_local_cached = + NSCreateMapTable(NSIntMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + global_proxies_gate = [NSLock new]; + root_object_map = + NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + root_object_map_gate = [NSLock new]; + } +} + ++ (id) new +{ + /* + * Undocumented feature of OPENSTEP/MacOS-X + * +new returns the default connection. + */ + return RETAIN([self defaultConnection]); +} + ++ (NSDistantObject*) rootProxyForConnectionWithRegisteredName: (NSString*)n + host: (NSString*)h +{ + NSConnection *connection; + NSDistantObject *proxy = nil; + + connection = [self connectionWithRegisteredName: n host: h]; + if (connection != nil) + { + proxy = [connection rootProxy]; + } + + return proxy; +} + ++ (NSDistantObject*) rootProxyForConnectionWithRegisteredName: (NSString*)n + host: (NSString*)h usingNameServer: (NSPortNameServer*)s +{ + NSConnection *connection; + NSDistantObject *proxy = nil; + + connection = [self connectionWithRegisteredName: n + host: h + usingNameServer: s]; + if (connection != nil) + { + proxy = [connection rootProxy]; + } + + return proxy; +} + ++ (void) _timeout: (NSTimer*)t +{ + NSArray *cached_locals; + int i; + + cached_locals = NSAllMapTableValues(all_connections_local_cached); + for (i = [cached_locals count]; i > 0; i--) + { + CachedLocalObject *item = [cached_locals objectAtIndex: i-1]; + + if ([item countdown] == NO) + { + GSLocalCounter *counter = [item obj]; + NSMapRemove(all_connections_local_cached, (void*)counter->target); + } + } + if ([cached_locals count] == 0) + { + [t invalidate]; + timer = nil; + } +} + +- (void) addRequestMode: (NSString*)mode +{ + M_LOCK(_refGate); + if ([self isValid] == YES) + { + if ([_requestModes containsObject: mode] == NO) + { + unsigned c = [_runLoops count]; + + while (c-- > 0) + { + NSRunLoop *loop = [_runLoops objectAtIndex: c]; + + [loop addPort: _receivePort forMode: mode]; + } + [_requestModes addObject: mode]; + } + } + M_UNLOCK(_refGate); +} + +- (void) addRunLoop: (NSRunLoop*)loop +{ + M_LOCK(_refGate); + if ([self isValid] == YES) + { + if ([_runLoops indexOfObjectIdenticalTo: loop] == NSNotFound) + { + unsigned c = [_requestModes count]; + + while (c-- > 0) + { + NSString *mode = [_requestModes objectAtIndex: c]; + + [loop addPort: _receivePort forMode: mode]; + } + [_runLoops addObject: loop]; + } + } + M_UNLOCK(_refGate); +} + +- (void) dealloc +{ + if (debug_connection) + NSLog(@"deallocating 0x%x", (gsaddr)self); + [super dealloc]; +} + +- (id) delegate +{ + return GS_GC_UNHIDE(_delegate); +} + +- (void) enableMultipleThreads +{ + _multipleThreads = YES; +} + +- (BOOL) independentConversationQueueing +{ + return _independentQueueing; +} + +- (id) init +{ + /* + * Undocumented feature of OPENSTEP/MacOS-X + * -init returns the default connection. + */ + RELEASE(self); + return RETAIN([connectionClass defaultConnection]); +} + +/* This is the designated initializer for NSConnection */ +- (id) initWithReceivePort: (NSPort*)r + sendPort: (NSPort*)s +{ + NSNotificationCenter *nCenter; + NSConnection *parent; + NSConnection *conn; + NSRunLoop *loop; + id del; + + /* + * If the receive port is nil, deallocate connection and return nil. + */ + if (r == nil) + { + if (debug_connection > 2) + { + NSLog(@"Asked to create connection with nil receive port"); + } + DESTROY(self); + return self; + } + + /* + * If the send port is nil, set it to the same as the receive port + * This connection will then only be useful to act as a server. + */ + if (s == nil) + { + s = r; + } + + conn = existingConnection(r, s); + + /* + * If the send and receive ports match an existing connection + * deallocate the new one and retain and return the old one. + */ + if (conn != nil) + { + RELEASE(self); + self = RETAIN(conn); + if (debug_connection > 2) + { + NSLog(@"Found existing connection (0x%x) for \n\t%@\n\t%@", + (gsaddr)conn, r, s); + } + return self; + } + + /* + * The parent connection is the one whose send and receive ports are + * both the same as our receive port. + */ + parent = existingConnection(r, r); + + if (debug_connection) + { + NSLog(@"Initialising new connection with parent 0x%x, 0x%x\n\t%@\n\t%@", + (gsaddr)parent, (gsaddr)self, r, s); + } + + M_LOCK(connection_table_gate); + + _isValid = YES; + _receivePort = RETAIN(r); + _sendPort = RETAIN(s); + _messageCount = 0; + _repOutCount = 0; + _reqOutCount = 0; + _repInCount = 0; + _reqInCount = 0; + + /* + * These arrays cache NSPortCoder objects + */ + _cachedDecoders = [NSMutableArray new]; + _cachedEncoders = [NSMutableArray new]; + + /* + * This is used to queue up incoming NSPortMessages representing requests + * that can't immediately be dealt with. + */ + _requestQueue = [NSMutableArray new]; + + /* + * This maps request sequence numbers to the NSPortCoder objects representing + * replies arriving from the remote connection. + */ + _replyMap = + NSCreateMapTable(NSIntMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + + /* + * This maps (void*)obj to (id)obj. The obj's are retained. + * We use this instead of an NSHashTable because we only care about + * the object's address, and don't want to send the -hash message to it. + */ + _localObjects = + NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + + /* + * This maps handles for local objects to their local proxies. + */ + _localTargets = + NSCreateMapTable(NSIntMapKeyCallBacks, + NSNonOwnedPointerMapValueCallBacks, 0); + + /* + * This maps [proxy targetForProxy] to proxy. The proxy's are retained. + */ + _remoteProxies = + NSCreateMapTable(NSIntMapKeyCallBacks, + NSNonOwnedPointerMapValueCallBacks, 0); + + /* + * Some attributes are inherited from the parent if possible. + */ + if (parent != nil) + { + _independentQueueing = parent->_independentQueueing; + _replyTimeout = parent->_replyTimeout; + _requestTimeout = parent->_requestTimeout; + } + else + { + _independentQueueing = NO; + _replyTimeout = CONNECTION_DEFAULT_TIMEOUT; + _requestTimeout = CONNECTION_DEFAULT_TIMEOUT; + } + + _requestDepth = 0; + _delegate = nil; + _refGate = [NSRecursiveLock new]; + + /* + * Set up request modes array and make sure the receiving port is + * added to the run loop to get data. + */ + loop = [runLoopClass currentRunLoop]; + _runLoops = [[NSMutableArray alloc] initWithObjects: &loop count: 1]; + _requestModes = [[NSMutableArray alloc] initWithCapacity: 2]; + [self addRequestMode: NSDefaultRunLoopMode]; + [self addRequestMode: NSConnectionReplyMode]; + + /* Ask the delegate for permission, (OpenStep-style and GNUstep-style). */ + + /* Preferred MacOS-X version, which just allows the returning of BOOL */ + del = [parent delegate]; + if ([del respondsTo: @selector(connection:shouldMakeNewConnection:)]) + { + if ([del connection: parent shouldMakeNewConnection: self] == NO) + { + M_UNLOCK(connection_table_gate); + RELEASE(self); + return nil; + } + } + /* Deprecated OpenStep version, which just allows the returning of BOOL */ + if ([del respondsTo: @selector(makeNewConnection:sender:)]) + { + if (![del makeNewConnection: self sender: parent]) + { + M_UNLOCK(connection_table_gate); + RELEASE(self); + return nil; + } + } + /* Here is the GNUstep version, which allows the delegate to specify + a substitute. Note: The delegate is responsible for freeing + newConn if it returns something different. */ + if ([del respondsTo: @selector(connection:didConnect:)]) + self = [del connection: parent didConnect: self]; + + /* + * If we have no parent, we must handle incoming packets on our + * receive port ourself - so we set ourself up as the port delegate. + */ + if (parent == nil) + { + [_receivePort setDelegate: self]; + } + + /* Register ourselves for invalidation notification when the + ports become invalid. */ + nCenter = [NSNotificationCenter defaultCenter]; + [nCenter addObserver: self + selector: @selector(portIsInvalid:) + name: NSPortDidBecomeInvalidNotification + object: r]; + if (s != nil) + [nCenter addObserver: self + selector: @selector(portIsInvalid:) + name: NSPortDidBecomeInvalidNotification + object: s]; + + /* In order that connections may be deallocated - there is an + implementation of [-release] to automatically remove the connection + from this array when it is the only thing retaining it. */ + NSHashInsert(connection_table, (void*)self); + M_UNLOCK(connection_table_gate); + + [[NSNotificationCenter defaultCenter] + postNotificationName: NSConnectionDidInitializeNotification + object: self]; + + return self; +} + +- (void) invalidate +{ + BOOL wasValid; + + M_LOCK(_refGate); + wasValid = _isValid; + _isValid = NO; + M_UNLOCK(_refGate); + + if (wasValid == NO) + { + return; + } + + /* + * Don't need notifications any more - so remove self as observer. + */ + [[NSNotificationCenter defaultCenter] removeObserver: self]; + + /* + * Withdraw from run loops. + */ + [self setRequestMode: nil]; + + RETAIN(self); + + if (debug_connection) + { + NSLog(@"Invalidating connection 0x%x\n\t%@\n\t%@", + (gsaddr)self, _receivePort, _sendPort); + } + /* + * We need to notify any watchers of our death - but if we are already + * in the deallocation process, we can't have a notification retaining + * and autoreleasing us later once we are deallocated - so we do the + * notification with a local autorelease pool to ensure that any release + * is done before the deallocation completes. + */ + { + CREATE_AUTORELEASE_POOL(arp); + + [[NSNotificationCenter defaultCenter] + postNotificationName: NSConnectionDidDieNotification + object: self]; + RELEASE(arp); + } + + /* + * If we have been invalidated, we don't need to retain proxies + * for local objects any more. In fact, we want to get rid of + * these proxies in case they are keeping us retained when we + * might otherwise de deallocated. + */ + { + NSArray *targets; + unsigned i; + + M_LOCK(_proxiesGate); + targets = NSAllMapTableValues(_localTargets); + IF_NO_GC(RETAIN(targets)); + for (i = 0; i < [targets count]; i++) + { + id t = [[targets objectAtIndex: i] localForProxy]; + + [self removeLocalObject: t]; + } + [targets release]; + M_UNLOCK(_proxiesGate); + } + + RELEASE(self); +} + +- (BOOL) isValid +{ + return _isValid; +} + +- (NSArray*) localObjects +{ + NSArray *c; + + /* Don't assert (_isValid); */ + M_LOCK(_proxiesGate); + c = NSAllMapTableValues(_localObjects); + M_UNLOCK(_proxiesGate); + return c; +} + +- (BOOL) multipleThreadsEnabled +{ + return _multipleThreads; +} + +- (NSPort*) receivePort +{ + return _receivePort; +} + +- (BOOL) registerName: (NSString*)name +{ + NSPortNameServer *svr = [NSPortNameServer systemDefaultPortNameServer]; + + return [self registerName: name withNameServer: svr]; +} + +- (BOOL) registerName: (NSString*)name withNameServer: (NSPortNameServer*)svr +{ + NSArray *names = [svr namesForPort: _receivePort]; + BOOL result = YES; + unsigned c; + + if (name != nil) + { + result = [svr registerPort: _receivePort forName: name]; + } + if (result == YES && (c = [names count]) > 0) + { + unsigned i; + + for (i = 0; i < c; i++) + { + NSString *tmp = [names objectAtIndex: i]; + + if ([tmp isEqualToString: name] == NO) + { + [svr removePort: _receivePort forName: name]; + } + } + } + return result; +} + +- (void) release +{ + /* + * If this would cause the connection to be deallocated then we + * must perform all necessary work (done in [-gcFinalize]). + * We bracket the code with a retain and release so that any + * retain/release pairs in the code won't cause recursion. + */ + if ([self retainCount] == 1) + { + [super retain]; + [self gcFinalize]; + [super release]; + } + [super release]; +} + +- (NSArray *) remoteObjects +{ + NSArray *c; + + /* Don't assert (_isValid); */ + M_LOCK(_proxiesGate); + c = NSAllMapTableValues(_remoteProxies); + M_UNLOCK(_proxiesGate); + return c; +} + +- (void) removeRequestMode: (NSString*)mode +{ + M_LOCK(_refGate); + if ([_requestModes containsObject: mode]) + { + unsigned c = [_runLoops count]; + + while (c-- > 0) + { + NSRunLoop *loop = [_runLoops objectAtIndex: c]; + + [loop removePort: _receivePort forMode: mode]; + } + [_requestModes removeObject: mode]; + } + M_UNLOCK(_refGate); +} + +- (void) removeRunLoop: (NSRunLoop*)loop +{ + unsigned pos; + + M_LOCK(_refGate); + pos = [_runLoops indexOfObjectIdenticalTo: loop]; + if (pos != NSNotFound) + { + unsigned c = [_requestModes count]; + + while (c-- > 0) + { + NSString *mode = [_requestModes objectAtIndex: c]; + + [loop removePort: _receivePort forMode: mode]; + } + [_runLoops removeObjectAtIndex: pos]; + } + M_UNLOCK(_refGate); +} + +- (NSTimeInterval) replyTimeout +{ + return _replyTimeout; +} + +- (NSArray*) requestModes +{ + return AUTORELEASE([_requestModes copy]); +} + +- (NSTimeInterval) requestTimeout +{ + return _requestTimeout; +} + +- (id) rootObject +{ + return rootObjectForInPort(_receivePort); +} + +- (NSDistantObject*) rootProxy +{ + NSPortCoder *op; + NSPortCoder *ip; + NSDistantObject *newProxy = nil; + int seq_num; + + NSParameterAssert(_receivePort); + NSParameterAssert(_isValid); + + seq_num = [self _newMsgNumber]; + op = [self _makeOutRmc: seq_num]; + [self _sendOutRmc: op type: ROOTPROXY_REQUEST]; + + ip = [self _getReplyRmc: seq_num]; + [ip decodeValueOfObjCType: @encode(id) at: &newProxy]; + DESTROY(ip); + return AUTORELEASE(newProxy); +} + +- (void) runInNewThread +{ + [self removeRunLoop: [runLoopClass currentRunLoop]]; + [NSThread detachNewThreadSelector: @selector(_runInNewThread) + toTarget: self + withObject: nil]; +} + +- (NSPort*) sendPort +{ + return _sendPort; +} + +- (void) setDelegate: (id)anObj +{ + _delegate = GS_GC_HIDE(anObj); + _authenticateIn = + [anObj respondsToSelector: @selector(authenticateComponents:withData:)]; + _authenticateOut = + [anObj respondsToSelector: @selector(authenticationDataForComponents:)]; +} + +- (void) setIndependentConversationQueueing: (BOOL)flag +{ + _independentQueueing = flag; +} + +- (void) setReplyTimeout: (NSTimeInterval)to +{ + _replyTimeout = to; +} + +- (void) setRequestMode: (NSString*)mode +{ + M_LOCK(_refGate); + while ([_requestModes count] > 0 && [_requestModes objectAtIndex: 0] != mode) + { + [self removeRequestMode: [_requestModes objectAtIndex: 0]]; + } + while ([_requestModes count] > 1) + { + [self removeRequestMode: [_requestModes objectAtIndex: 1]]; + } + if (mode != nil && [_requestModes count] == 0) + { + [self addRequestMode: mode]; + } + M_UNLOCK(_refGate); +} + +- (void) setRequestTimeout: (NSTimeInterval)to +{ + _requestTimeout = to; +} + +- (void) setRootObject: (id)anObj +{ + setRootObjectForInPort(anObj, _receivePort); +} + +- (NSDictionary*) statistics +{ + NSMutableDictionary *d; + id o; + + d = [NSMutableDictionary dictionaryWithCapacity: 8]; + + M_LOCK(_refGate); + + /* + * These are in OPENSTEP 4.2 + */ + o = [NSNumber numberWithUnsignedInt: _repInCount]; + [d setObject: o forKey: NSConnectionRepliesReceived]; + o = [NSNumber numberWithUnsignedInt: _repOutCount]; + [d setObject: o forKey: NSConnectionRepliesSent]; + o = [NSNumber numberWithUnsignedInt: _reqInCount]; + [d setObject: o forKey: NSConnectionRequestsReceived]; + o = [NSNumber numberWithUnsignedInt: _reqOutCount]; + [d setObject: o forKey: NSConnectionRequestsSent]; + + /* + * These are GNUstep extras + */ + o = [NSNumber numberWithUnsignedInt: NSCountMapTable(_localTargets)]; + [d setObject: o forKey: NSConnectionLocalCount]; + o = [NSNumber numberWithUnsignedInt: NSCountMapTable(_remoteProxies)]; + [d setObject: o forKey: NSConnectionProxyCount]; + + M_UNLOCK(_refGate); + + return d; +} + +@end + + + +@implementation NSConnection (GNUstepExtensions) + ++ (NSConnection*) newRegisteringAtName: (NSString*)name + withRootObject: (id)anObject +{ + NSConnection *conn; + + conn = [[self alloc] initWithReceivePort: [NSPort port] + sendPort: nil]; + [conn setRootObject: anObject]; + if ([conn registerName: name] == NO) + { + DESTROY(conn); + } + return conn; +} + +- (void) gcFinalize +{ + CREATE_AUTORELEASE_POOL(arp); + + if (debug_connection) + NSLog(@"finalising 0x%x", (gsaddr)self); + + [self invalidate]; + M_LOCK(connection_table_gate); + NSHashRemove(connection_table, self); + [timer invalidate]; + timer = nil; + M_UNLOCK(connection_table_gate); + + /* Remove rootObject from root_object_map if this is last connection */ + if (_receivePort != nil && existingConnection(_receivePort, nil) == nil) + { + setRootObjectForInPort(nil, _receivePort); + } + + /* Remove receive port from run loop. */ + [self setRequestMode: nil]; + + DESTROY(_requestModes); + DESTROY(_runLoops); + + /* + * Finished with ports - releasing them may generate a notification + * If we are the receive port delagate, try to shift responsibility. + */ + if ([_receivePort delegate] == self) + { + NSConnection *root = existingConnection(_receivePort, _receivePort); + + if (root == nil) + { + root = existingConnection(_receivePort, nil); + } + [_receivePort setDelegate: root]; + } + DESTROY(_receivePort); + DESTROY(_sendPort); + + M_LOCK(_proxiesGate); + if (_remoteProxies != 0) + { + NSFreeMapTable(_remoteProxies); + _remoteProxies = 0; + } + if (_localObjects != 0) + { + NSFreeMapTable(_localObjects); + _localObjects = 0; + } + if (_localTargets != 0) + { + NSFreeMapTable(_localTargets); + _localTargets = 0; + } + M_UNLOCK(_proxiesGate); + + DESTROY(_requestQueue); + if (_replyMap != 0) + { + NSFreeMapTable(_replyMap); + _replyMap = 0; + } + + DESTROY(_cachedDecoders); + DESTROY(_cachedEncoders); + + DESTROY(_refGate); + RELEASE(arp); +} + +/* + * NSDistantObject's -forward: : method calls this to send the message + * over the wire. + */ +- (retval_t) forwardForProxy: (NSDistantObject*)object + selector: (SEL)sel + argFrame: (arglist_t)argframe +{ + NSPortCoder *op; + + /* The callback for encoding the args of the method call. */ + void encoder (int argnum, void *datum, const char *type, int flags) + { +#define ENCODED_ARGNAME @"argument value" + switch (*type) + { + case _C_ID: + if (flags & _F_BYCOPY) + [op encodeBycopyObject: *(id*)datum]; +#ifdef _F_BYREF + else if (flags & _F_BYREF) + [op encodeByrefObject: *(id*)datum]; +#endif + else + [op encodeObject: *(id*)datum]; + break; + default: + [op encodeValueOfObjCType: type at: datum]; + } + } + + /* Encode the method on an RMC, and send it. */ + { + BOOL out_parameters; + const char *type; + int seq_num; + retval_t retframe; + + NSParameterAssert (_isValid); + + /* get the method types from the selector */ +#if NeXT_runtime + [NSException + raise: NSGenericException + format: @"Sorry, distributed objects does not work with NeXT runtime"]; + /* type = [object selectorTypeForProxy: sel]; */ +#else + type = sel_get_type(sel); +#endif + if (type == 0 || *type == '\0') { + type = [[object methodSignatureForSelector: sel] methodType]; + if (type) { + sel_register_typed_name(sel_get_name(sel), type); + } + } + NSParameterAssert(type); + NSParameterAssert(*type); + + seq_num = [self _newMsgNumber]; + op = [self _makeOutRmc: seq_num]; + + if (debug_connection > 4) + NSLog(@"building packet seq %d", seq_num); + + /* Send the types that we're using, so that the performer knows + exactly what qualifiers we're using. + If all selectors included qualifiers, and if I could make + sel_types_match() work the way I wanted, we wouldn't need to do + this. */ + [op encodeValueOfObjCType: @encode(char*) at: &type]; + + /* xxx This doesn't work with proxies and the NeXT runtime because + type may be a method_type from a remote machine with a + different architecture, and its argframe layout specifiers + won't be right for this machine! */ + out_parameters = mframe_dissect_call (argframe, type, encoder); + + [self _sendOutRmc: op type: METHOD_REQUEST]; + + if (debug_connection > 1) + NSLog(@"Sent message to 0x%x", (gsaddr)self); + + /* Get the reply rmc, and decode it. */ + { + 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"]; + } + /* xxx Why do we get the reply packet in here, and not + just before calling dissect_method_return() below? */ + 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]; + /* -decodeValueOfObjCType: at: malloc's new memory + for char*'s. We need to make sure it gets freed eventually + so we don't have a memory leak. Request here that it be + autorelease'ed. Also autorelease created objects. */ + if ((*type == _C_CHARPTR || *type == _C_PTR) && *(void**)datum != 0) + [NSData dataWithBytesNoCopy: *(void**)datum length: 1]; + else if (*type == _C_ID) + AUTORELEASE(*(id*)datum); + } + + retframe = mframe_build_return (argframe, type, out_parameters, decoder); + /* 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); + return retframe; + } + } +} + +- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target +{ + id op, ip; + char *type = 0; + int seq_num; + + NSParameterAssert(_receivePort); + NSParameterAssert (_isValid); + seq_num = [self _newMsgNumber]; + op = [self _makeOutRmc: seq_num]; + [op encodeValueOfObjCType: ":" at: &sel]; + [op encodeValueOfObjCType: @encode(unsigned) at: &target]; + [self _sendOutRmc: op type: METHODTYPE_REQUEST]; + ip = [self _getReplyRmc: seq_num]; + [ip decodeValueOfObjCType: @encode(char*) at: &type]; + DESTROY(ip); + return type; +} + + +/* Class-wide stats and collections. */ + ++ (unsigned) connectionsCount +{ + unsigned result; + + M_LOCK(connection_table_gate); + result = NSCountHashTable(connection_table); + M_UNLOCK(connection_table_gate); + return result; +} + ++ (unsigned) connectionsCountWithInPort: (NSPort*)aPort +{ + unsigned count = 0; + NSHashEnumerator enumerator; + NSConnection *o; + + M_LOCK(connection_table_gate); + enumerator = NSEnumerateHashTable(connection_table); + while ((o = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) + { + if ([aPort isEqual: [o receivePort]]) + { + count++; + } + } + M_UNLOCK(connection_table_gate); + + return count; +} + +@end + + + + + +@implementation NSConnection (Private) + +- (void) handlePortMessage: (NSPortMessage*)msg +{ + NSPortCoder *rmc; + int type = [msg msgid]; + NSMutableArray *components = [msg _components]; + NSPort *rp = [msg receivePort]; + NSPort *sp = [msg sendPort]; + NSConnection *conn; + + if (debug_connection > 4) + { + NSLog(@"handling packet of type %d (%@)", type, stringFromMsgType(type)); + } + conn = [connectionClass connectionWithReceivePort: rp sendPort: sp]; + if (conn == nil) + { + NSLog(@"received port message for unknown connection - %@", msg); + return; + } + else if ([conn isValid] == NO) + { + if (debug_connection) + { + NSLog(@"received port message for invalid connection - %@", msg); + } + return; + } + + if (_authenticateIn == YES) + { + NSData *d; + unsigned count = [components count]; + + d = AUTORELEASE(RETAIN([components objectAtIndex: --count])); + [components removeObjectAtIndex: count]; + if ([[self delegate] authenticateComponents: components + withData: d] == NO) + { + [NSException raise: NSFailedAuthenticationException + format: @"message not authenticated by delegate"]; + } + } + + rmc = [conn _makeInRmc: components]; + + switch (type) + { + case ROOTPROXY_REQUEST: + /* It won't take much time to handle this, so go ahead and service + it, even if we are waiting for a reply. */ + [conn _service_rootObject: rmc]; + break; + + case METHODTYPE_REQUEST: + /* It won't take much time to handle this, so go ahead and service + it, even if we are waiting for a reply. */ + [conn _service_typeForSelector: rmc]; + break; + + case METHOD_REQUEST: + /* + * We just got a new request; we need to decide whether to queue + * it or service it now. + * If the REPLY_DEPTH is 0, then we aren't in the middle of waiting + * for a reply, we are waiting for requests---so service it now. + * If REPLY_DEPTH is non-zero, we may still want to service it now + * if independent_queuing is NO. + */ + M_LOCK(conn->_queueGate); + if (conn->_requestDepth == 0 || conn->_independentQueueing == NO) + { + conn->_requestDepth++; + M_UNLOCK(conn->_queueGate); + [conn _service_forwardForProxy: rmc]; + M_LOCK(conn->_queueGate); + conn->_requestDepth--; + } + else + { + [conn->_requestQueue addObject: rmc]; + } + /* + * Service any requests that were queued while we + * were waiting for replies. + */ + while (conn->_requestDepth == 0 && [conn->_requestQueue count] > 0) + { + rmc = [conn->_requestQueue objectAtIndex: 0]; + [conn->_requestQueue removeObjectAtIndex: 0]; + M_UNLOCK(conn->_queueGate); + [conn _service_forwardForProxy: rmc]; + M_LOCK(conn->_queueGate); + } + M_UNLOCK(conn->_queueGate); + break; + + /* + * For replies, we read the sequence number from the reply object and + * store it in a map using thee sequence number as the key. That way + * it's easy for the connection to find replies by their numbers. + */ + case ROOTPROXY_REPLY: + case METHOD_REPLY: + case METHODTYPE_REPLY: + case RETAIN_REPLY: + { + int sequence; + + [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; + M_LOCK(conn->_queueGate); + NSMapInsert(conn->_replyMap, (void*)sequence, rmc); + M_UNLOCK(conn->_queueGate); + } + break; + + case CONNECTION_SHUTDOWN: + { + [conn _service_shutdown: rmc]; + break; + } + case PROXY_RELEASE: + { + [conn _service_release: rmc]; + break; + } + case PROXY_RETAIN: + { + [conn _service_retain: rmc]; + break; + } + default: + [NSException raise: NSGenericException + format: @"unrecognized NSPortCoder identifier"]; + } +} + +- (void) _runInNewThread +{ + NSRunLoop *loop = [runLoopClass currentRunLoop]; + + [self addRunLoop: loop]; + [loop run]; +} + ++ (void) setDebug: (int)val +{ + debug_connection = val; +} + +/* NSConnection calls this to service the incoming method request. */ +- (void) _service_forwardForProxy: (NSPortCoder*)aRmc +{ + char *forward_type = 0; + id op = nil; + int reply_sequence_number; + + void decoder (int argnum, void *datum, const char *type) + { + /* We need this "dismiss" to happen here and not later so that Coder + "-awake..." methods will get sent before the __builtin_apply! */ + if (argnum == -1 && datum == 0 && type == 0) + { + [self _doneInRmc: aRmc]; + return; + } + + [aRmc decodeValueOfObjCType: type at: datum]; + /* -decodeValueOfObjCType: at: malloc's new memory + for char*'s. We need to make sure it gets freed eventually + so we don't have a memory leak. Request here that it be + autorelease'ed. Also autorelease created objects. */ + if ((*type == _C_CHARPTR || *type == _C_PTR) && *(void**)datum != 0) + [NSData dataWithBytesNoCopy: *(void**)datum length: 1]; + else if (*type == _C_ID) + AUTORELEASE(*(id*)datum); + } + + void encoder (int argnum, void *datum, const char *type, int flags) + { +#define ENCODED_RETNAME @"return value" + if (op == nil) + { + BOOL is_exception = NO; + /* It is possible that our connection died while the method was + being called - in this case we mustn't try to send the result + back to the remote application! */ + if (!_isValid) + return; + op = [self _makeOutRmc: reply_sequence_number]; + [op encodeValueOfObjCType: @encode(BOOL) at: &is_exception]; + } + switch (*type) + { + case _C_ID: + if (flags & _F_BYCOPY) + [op encodeBycopyObject: *(id*)datum]; +#ifdef _F_BYREF + else if (flags & _F_BYREF) + [op encodeByrefObject: *(id*)datum]; +#endif + else + [op encodeObject: *(id*)datum]; + break; + default: + [op encodeValueOfObjCType: type at: datum]; + } + } + + /* Make sure don't let exceptions caused by servicing the client's + request cause us to crash. */ + NS_DURING + { + NSParameterAssert (_isValid); + + /* Save this for later */ + [aRmc decodeValueOfObjCType: @encode(int) at: &reply_sequence_number]; + + /* Get the types that we're using, so that we know + exactly what qualifiers the forwarder used. + If all selectors included qualifiers and I could make + sel_types_match() work the way I wanted, we wouldn't need + to do this. */ + [aRmc decodeValueOfObjCType: @encode(char*) at: &forward_type]; + + if (debug_connection > 1) + NSLog(@"Handling message from 0x%x", (gsaddr)self); + _reqInCount++; /* Handling an incoming request. */ + mframe_do_call (forward_type, decoder, encoder); + if (op != nil) + { + [self _sendOutRmc: op type: METHOD_REPLY]; + } + } + + /* Make sure we pass all exceptions back to the requestor. */ + NS_HANDLER + { + BOOL is_exception = YES; + + /* Send the exception back to the client. */ + if (_isValid) + { + NS_DURING + { + op = [self _makeOutRmc: reply_sequence_number]; + [op encodeValueOfObjCType: @encode(BOOL) + at: &is_exception]; + [op encodeBycopyObject: localException]; + [self _sendOutRmc: op type: METHOD_REPLY]; + } + NS_HANDLER + { + NSLog(@"Exception when sending exception back to client - %@", + localException); + } + NS_ENDHANDLER; + } + } + NS_ENDHANDLER; + if (forward_type != 0) + { + NSZoneFree(NSDefaultMallocZone(), forward_type); + } +} + +- (void) _service_rootObject: (NSPortCoder*)rmc +{ + id rootObject = rootObjectForInPort(_receivePort); + int sequence; + NSPortCoder *op; + + NSParameterAssert(_receivePort); + NSParameterAssert(_isValid); + NSParameterAssert([rmc connection] == self); + + [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; + [self _doneInRmc: rmc]; + op = [self _makeOutRmc: sequence]; + [op encodeObject: rootObject]; + [self _sendOutRmc: op type: ROOTPROXY_REPLY]; +} + +- (void) _service_release: (NSPortCoder*)rmc +{ + unsigned int count; + unsigned int pos; + int sequence; + + NSParameterAssert (_isValid); + + [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; + [rmc decodeValueOfObjCType: @encode(typeof(count)) at: &count]; + + for (pos = 0; pos < count; pos++) + { + unsigned target; + NSDistantObject *prox; + + [rmc decodeValueOfObjCType: @encode(typeof(target)) at: &target]; + + prox = (NSDistantObject*)[self includesLocalTarget: target]; + if (prox != nil) + { + if (debug_connection > 3) + NSLog(@"releasing object with target (0x%x) on (0x%x)", + target, (gsaddr)self); + [self removeLocalObject: [prox localForProxy]]; + } + else if (debug_connection > 3) + NSLog(@"releasing object with target (0x%x) on (0x%x) - nothing to do", + target, (gsaddr)self); + } + [self _doneInRmc: rmc]; +} + +- (void) _service_retain: (NSPortCoder*)rmc +{ + unsigned target; + NSPortCoder *op; + int sequence; + + NSParameterAssert (_isValid); + + [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; + op = [self _makeOutRmc: sequence]; + + [rmc decodeValueOfObjCType: @encode(typeof(target)) at: &target]; + [self _doneInRmc: rmc]; + + if (debug_connection > 3) + NSLog(@"looking to retain local object with target (0x%x) on (0x%x)", + target, (gsaddr)self); + + if ([self includesLocalTarget: target] == nil) + { + GSLocalCounter *counter; + + M_LOCK(global_proxies_gate); + counter = NSMapGet (all_connections_local_targets, (void*)target); + if (counter == nil) + { + /* + * If the target doesn't exist for any connection, but still + * persists in the cache (ie it was recently released) then + * we move it back from the cache to the main maps so we can + * retain it on this connection. + */ + counter = NSMapGet (all_connections_local_cached, (void*)target); + if (counter) + { + unsigned t = counter->target; + id o = counter->object; + + NSMapInsert(all_connections_local_objects, (void*)o, counter); + NSMapInsert(all_connections_local_targets, (void*)t, counter); + NSMapRemove(all_connections_local_cached, (void*)t); + if (debug_connection > 3) + NSLog(@"target (0x%x) moved from cache", target); + } + } + M_UNLOCK(global_proxies_gate); + if (counter == nil) + { + [op encodeObject: @"target not found anywhere"]; + if (debug_connection > 3) + NSLog(@"target (0x%x) not found anywhere for retain", target); + } + else + { + [distantObjectClass proxyWithLocal: counter->object + connection: self]; + [op encodeObject: nil]; + if (debug_connection > 3) + NSLog(@"retained object (0x%x) target (0x%x) on connection(0x%x)", + counter->object, counter->target, self); + } + } + else + { + [op encodeObject: nil]; + if (debug_connection > 3) + NSLog(@"target (0x%x) already retained on connection (0x%x)", + target, self); + } + + [self _sendOutRmc: op type: RETAIN_REPLY]; +} + +- (void) shutdown +{ + NSPortCoder *op; + + NSParameterAssert(_receivePort); + NSParameterAssert (_isValid); + op = [self _makeOutRmc: [self _newMsgNumber]]; + [self _sendOutRmc: op type: CONNECTION_SHUTDOWN]; +} + +- (void) _service_shutdown: (NSPortCoder*)rmc +{ + NSParameterAssert (_isValid); + [self _doneInRmc: rmc]; + [self invalidate]; + [NSException raise: NSGenericException + format: @"connection waiting for request was shut down"]; +} + +- (void) _service_typeForSelector: (NSPortCoder*)rmc +{ + NSPortCoder *op; + unsigned target; + NSDistantObject *p; + int sequence; + id o; + SEL sel; + const char *type; + struct objc_method* m; + + NSParameterAssert(_receivePort); + NSParameterAssert (_isValid); + + [rmc decodeValueOfObjCType: @encode(int) at: &sequence]; + op = [self _makeOutRmc: sequence]; + + [rmc decodeValueOfObjCType: ":" at: &sel]; + [rmc decodeValueOfObjCType: @encode(unsigned) at: &target]; + [self _doneInRmc: rmc]; + p = [self includesLocalTarget: target]; + o = [p localForProxy]; + + /* xxx We should make sure that TARGET is a valid object. */ + /* Not actually a Proxy, but we avoid the warnings "id" would have made. */ + m = class_get_instance_method(((NSDistantObject*)o)->isa, sel); + /* Perhaps I need to be more careful in the line above to get the + version of the method types that has the type qualifiers in it. + Search the protocols list. */ + if (m) + type = m->method_types; + else + type = ""; + [op encodeValueOfObjCType: @encode(char*) at: &type]; + [self _sendOutRmc: op type: METHODTYPE_REPLY]; +} + + + +/* + * Check the queue, then try to get it from the network by waiting + * while we run the NSRunLoop. Raise exception if we don't get anything + * before timing out. + */ +- _getReplyRmc: (int)sn +{ + NSPortCoder *rmc; + NSDate *timeout_date = nil; + + M_LOCK(_queueGate); + while ((rmc = (NSPortCoder*)NSMapGet(_replyMap, (void*)sn)) == nil) + { + if (timeout_date == nil) + { + timeout_date + = [dateClass dateWithTimeIntervalSinceNow: _replyTimeout]; + } + M_UNLOCK(_queueGate); + if ([runLoopClass runOnceBeforeDate: timeout_date + forMode: NSConnectionReplyMode] == NO) + { + [NSException raise: NSPortTimeoutException + format: @"timed out waiting for reply"]; + } + M_LOCK(_queueGate); + } + if (rmc != nil) + { + RETAIN(rmc); + NSMapRemove(_replyMap, (void*)sn); + } + M_UNLOCK(_queueGate); + return rmc; +} + +- (int) _newMsgNumber +{ + int n; + + NSParameterAssert (_isValid); + M_LOCK(sequenceNumberGate); + n = _messageCount++; + M_UNLOCK(sequenceNumberGate); + return n; +} + +- (void) _doneInRmc: (NSPortCoder*)c +{ + M_LOCK(_refGate); + [_cachedDecoders addObject: c]; + [c dispatch]; /* Tell NSPortCoder to release the connection. */ + RELEASE(c); + M_UNLOCK(_refGate); +} + +- (NSPortCoder*) _makeInRmc: (NSMutableArray*)components +{ + NSPortCoder *coder; + unsigned count; + + NSParameterAssert(_isValid); + + M_LOCK(_refGate); + count = [_cachedDecoders count]; + if (count > 0) + { + coder = [_cachedDecoders objectAtIndex: --count]; + RETAIN(coder); + [_cachedDecoders removeObjectAtIndex: count]; + } + else + { + coder = [portCoderClass allocWithZone: NSDefaultMallocZone()]; + } + M_UNLOCK(_refGate); + + coder = [coder initWithReceivePort: _receivePort + sendPort: _sendPort + components: components]; + return coder; +} + +- (NSPortCoder*) _makeOutRmc: (int)sequence +{ + NSPortCoder *coder; + unsigned count; + + NSParameterAssert(_isValid); + + M_LOCK(_refGate); + count = [_cachedEncoders count]; + if (count > 0) + { + coder = [_cachedEncoders objectAtIndex: --count]; + RETAIN(coder); + [_cachedEncoders removeObjectAtIndex: count]; + } + else + { + coder = [portCoderClass allocWithZone: NSDefaultMallocZone()]; + } + M_UNLOCK(_refGate); + + coder = [coder initWithReceivePort: _receivePort + sendPort: _sendPort + components: nil]; + [coder encodeValueOfObjCType: @encode(int) at: &sequence]; + return coder; +} + +- (void) _sendOutRmc: (NSPortCoder*)c type: (int)msgid +{ + NSDate *limit; + BOOL sent = NO; + BOOL raiseException = NO; + BOOL needsReply = NO; + NSMutableArray *components = [c _components]; + + if (_authenticateOut == YES) + { + NSData *d; + + d = [[self delegate] authenticationDataForComponents: components]; + if (d == nil) + { + RELEASE(c); + [NSException raise: NSGenericException + format: @"Bad authentication data provided by delegate"]; + } + [components addObject: d]; + } + + switch (msgid) + { + case PROXY_RETAIN: + needsReply = YES; + case CONNECTION_SHUTDOWN: + case METHOD_REPLY: + case ROOTPROXY_REPLY: + case METHODTYPE_REPLY: + case PROXY_RELEASE: + case RETAIN_REPLY: + raiseException = NO; + break; + + case METHOD_REQUEST: + case ROOTPROXY_REQUEST: + case METHODTYPE_REQUEST: + default: + raiseException = YES; + needsReply = YES; + break; + } + + limit = [dateClass dateWithTimeIntervalSinceNow: _requestTimeout]; + sent = [_sendPort sendBeforeDate: limit + msgid: msgid + components: components + from: _receivePort + reserved: [_sendPort reservedSpaceLength]]; + + M_LOCK(_refGate); + /* + * If we have sent out a request on a run loop that we don't already + * know about, it must be on a new thread - so if we have multipleThreads + * enabled, we must add the run loop of the new thread so that we can + * get the reply in this thread. + */ + if (_multipleThreads == YES && needsReply == YES) + { + NSRunLoop *loop = [runLoopClass currentRunLoop]; + + if ([_runLoops indexOfObjectIdenticalTo: loop] == NSNotFound) + { + [self addRunLoop: loop]; + } + } + + /* + * We replace the code we have just used in the cache, and tell it not to + * retain this connection any more. + */ + [_cachedEncoders addObject: c]; + [c dispatch]; /* Tell NSPortCoder to release the connection. */ + RELEASE(c); + M_UNLOCK(_refGate); + + if (sent == NO) + { + NSString *text = stringFromMsgType(msgid); + + if (raiseException == YES) + { + [NSException raise: NSPortTimeoutException format: text]; + } + else + { + NSLog(@"Port operation timed out - %@", text); + } + } + else + { + switch (msgid) + { + case METHOD_REQUEST: + _reqOutCount++; /* Sent a request. */ + break; + case METHOD_REPLY: + _repOutCount++; /* Sent back a reply. */ + break; + default: + break; + } + } +} + + + + +/* Managing objects and proxies. */ +- (void) addLocalObject: (id)anObj +{ + id object = [anObj localForProxy]; + unsigned target; + GSLocalCounter *counter; + + NSParameterAssert (_isValid); + M_LOCK(_proxiesGate); + M_LOCK(global_proxies_gate); + /* xxx Do we need to check to make sure it's not already there? */ + /* This retains object. */ + NSMapInsert(_localObjects, (void*)object, anObj); + + /* + * Keep track of local objects accross all connections. + */ + counter = NSMapGet(all_connections_local_objects, (void*)object); + if (counter) + { + counter->ref++; + target = counter->target; + } + else + { + counter = [GSLocalCounter newWithObject: object]; + target = counter->target; + NSMapInsert(all_connections_local_objects, (void*)object, counter); + NSMapInsert(all_connections_local_targets, (void*)target, counter); + RELEASE(counter); + } + [anObj setProxyTarget: target]; + NSMapInsert(_localTargets, (void*)target, anObj); + if (debug_connection > 2) + NSLog(@"add local object (0x%x) target (0x%x) " + @"to connection (0x%x) (ref %d)", + (gsaddr)object, target, (gsaddr) self, counter->ref); + M_UNLOCK(global_proxies_gate); + M_UNLOCK(_proxiesGate); +} + +- (NSDistantObject*) localForObject: (id)object +{ + NSDistantObject *p; + + /* Don't assert (_isValid); */ + M_LOCK(_proxiesGate); + p = NSMapGet (_localObjects, (void*)object); + M_UNLOCK(_proxiesGate); + NSParameterAssert(!p || [p connectionForProxy] == self); + return p; +} + +/* This should get called whenever an object free's itself */ ++ (void) removeLocalObject: (id)anObj +{ + NSHashEnumerator enumerator; + NSConnection *o; + + enumerator = NSEnumerateHashTable(connection_table); + while ((o = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) + { + [o removeLocalObject: anObj]; + } +} + +- (void) removeLocalObject: (id)anObj +{ + NSDistantObject *prox; + unsigned target; + GSLocalCounter *counter; + unsigned val = 0; + + M_LOCK(global_proxies_gate); + M_LOCK(_proxiesGate); + + prox = NSMapGet(_localObjects, (void*)anObj); + target = [prox targetForProxy]; + + /* + * If all references to a local proxy have gone - remove the + * global reference as well. + */ + counter = NSMapGet(all_connections_local_objects, (void*)anObj); + if (counter) + { + counter->ref--; + if ((val = counter->ref) == 0) + { + /* + * If this proxy has been vended onwards by another process, we + * need to keep a reference to the local object around for a + * while in case that other process needs it. + */ + if (0) + { + id item; + if (timer == nil) + { + timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 + target: connectionClass + selector: @selector(_timeout:) + userInfo: nil + repeats: YES]; + } + item = [CachedLocalObject newWithObject: counter time: 30]; + NSMapInsert(all_connections_local_cached, (void*)target, item); + RELEASE(item); + if (debug_connection > 3) + NSLog(@"placed local object (0x%x) target (0x%x) in cache", + (gsaddr)anObj, target); + } + NSMapRemove(all_connections_local_objects, (void*)anObj); + NSMapRemove(all_connections_local_targets, (void*)target); + } + } + + NSMapRemove(_localObjects, (void*)anObj); + NSMapRemove(_localTargets, (void*)target); + + if (debug_connection > 2) + NSLog(@"remove local object (0x%x) target (0x%x) " + @"from connection (0x%x) (ref %d)", + (gsaddr)anObj, target, (gsaddr)self, val); + + M_UNLOCK(_proxiesGate); + M_UNLOCK(global_proxies_gate); +} + +- (void) _release_targets: (unsigned*)list count: (unsigned)number +{ + NS_DURING + { + /* + * Tell the remote app that it can release its local objects + * for the targets in the specified list since we don't have + * proxies for them any more. + */ + if (_receivePort && _isValid && number > 0) + { + id op; + unsigned i; + int sequence = [self _newMsgNumber]; + + op = [self _makeOutRmc: sequence]; + + [op encodeValueOfObjCType: @encode(unsigned) at: &number]; + + for (i = 0; i < number; i++) + { + [op encodeValueOfObjCType: @encode(unsigned) at: &list[i]]; + if (debug_connection > 3) + NSLog(@"sending release for target (0x%x) on (0x%x)", + list[i], (gsaddr)self); + } + + [self _sendOutRmc: op type: PROXY_RELEASE]; + } + } + NS_HANDLER + { + if (debug_connection) + NSLog(@"failed to release targets - %@", localException); + } + NS_ENDHANDLER +} + +- (void) retainTarget: (unsigned)target +{ + NS_DURING + { + /* + * Tell the remote app that it must retain the local object + * for the target on this connection. + */ + if (_receivePort && _isValid) + { + NSPortCoder *op; + id ip; + id result; + int seq_num = [self _newMsgNumber]; + + op = [self _makeOutRmc: seq_num]; + [op encodeValueOfObjCType: @encode(typeof(target)) at: &target]; + [self _sendOutRmc: op type: PROXY_RETAIN]; + + ip = [self _getReplyRmc: seq_num]; + [ip decodeValueOfObjCType: @encode(id) at: &result]; + DESTROY(ip); + if (result != nil) + NSLog(@"failed to retain target - %@", result); + } + } + NS_HANDLER + { + NSLog(@"failed to retain target - %@", localException); + } + NS_ENDHANDLER +} + +- (void) removeProxy: (NSDistantObject*)aProxy +{ + unsigned target = [aProxy targetForProxy]; + + /* Don't assert (_isValid); */ + M_LOCK(_proxiesGate); + /* This also releases aProxy */ + NSMapRemove(_remoteProxies, (void*)target); + M_UNLOCK(_proxiesGate); + + /* + * Tell the remote application that we have removed our proxy and + * it can release it's local object. + */ + [self _release_targets: &target count: 1]; +} + +- (NSDistantObject*) proxyForTarget: (unsigned)target +{ + NSDistantObject *p; + + /* Don't assert (_isValid); */ + M_LOCK(_proxiesGate); + p = NSMapGet(_remoteProxies, (void*)target); + M_UNLOCK(_proxiesGate); + NSParameterAssert(!p || [p connectionForProxy] == self); + return p; +} + +- (void) addProxy: (NSDistantObject*) aProxy +{ + unsigned target = (unsigned int)[aProxy targetForProxy]; + + NSParameterAssert (_isValid); + NSParameterAssert(aProxy->isa == distantObjectClass); + NSParameterAssert([aProxy connectionForProxy] == self); + M_LOCK(_proxiesGate); + if (NSMapGet(_remoteProxies, (void*)target)) + { + M_UNLOCK(_proxiesGate); + [NSException raise: NSGenericException + format: @"Trying to add the same proxy twice"]; + } + NSMapInsert(_remoteProxies, (void*)target, aProxy); + M_UNLOCK(_proxiesGate); +} + +- (id) includesProxyForTarget: (unsigned)target +{ + NSDistantObject *ret; + + /* Don't assert (_isValid); */ + M_LOCK(_proxiesGate); + ret = NSMapGet (_remoteProxies, (void*)target); + M_UNLOCK(_proxiesGate); + return ret; +} + +- (id) includesLocalObject: (id)anObj +{ + NSDistantObject *ret; + + /* Don't assert (_isValid); */ + M_LOCK(_proxiesGate); + ret = NSMapGet(_localObjects, (void*)anObj); + M_UNLOCK(_proxiesGate); + return ret; +} + +- (id) includesLocalTarget: (unsigned)target +{ + NSDistantObject *ret; + + /* Don't assert (_isValid); */ + M_LOCK(_proxiesGate); + ret = NSMapGet(_localTargets, (void*)target); + M_UNLOCK(_proxiesGate); + return ret; +} + +/* Check all connections. + Proxy needs to use this when decoding a local object in order to + make sure the target address is a valid object. It is not enough + for the Proxy to check the Proxy's connection only (using + -includesLocalTarget), because the proxy may have come from a + triangle connection. */ ++ (id) includesLocalTarget: (unsigned)target +{ + id ret; + + /* Don't assert (_isValid); */ + M_LOCK(global_proxies_gate); + ret = NSMapGet(all_connections_local_targets, (void*)target); + M_UNLOCK(global_proxies_gate); + return ret; +} + + +/* Accessing ivars */ + + +/* Prevent trying to encode the connection itself */ + +- (void) encodeWithCoder: (NSCoder*)anEncoder +{ + [self shouldNotImplement: _cmd]; +} + +- (id) initWithCoder: (NSCoder*)aDecoder; +{ + [self shouldNotImplement: _cmd]; + return self; +} + + +/* Shutting down and deallocating. */ + +/* + * We register this method for a notification when a port dies. + * NB. It is possible that the death of a port could be notified + * to us after we are invalidated - in which case we must ignore it. + */ +- (void) portIsInvalid: (NSNotification*)notification +{ + if (_isValid) + { + id port = [notification object]; + + if (debug_connection) + { + NSLog(@"Received port invalidation notification for " + @"connection 0x%x\n\t%@", (gsaddr)self, port); + } + + /* We shouldn't be getting any port invalidation notifications, + except from our own ports; this is how we registered ourselves + with the NSNotificationCenter in + +newForInPort: outPort: ancestorConnection. */ + NSParameterAssert (port == _receivePort || port == _sendPort); + + [self invalidate]; + } +} + +@end + +#else /* Implementation of connection object for remote object messaging Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc. @@ -2379,4 +4824,4 @@ static int messages_received_count; } @end - +#endif diff --git a/Source/NSPortCoder.m b/Source/NSPortCoder.m index ef7d4ee6c..56681cb93 100644 --- a/Source/NSPortCoder.m +++ b/Source/NSPortCoder.m @@ -1,3 +1,1951 @@ +#if GS_NEW_DO +/* Implementation of NSPortCoder object for remote messaging + Copyright (C) 1997,2000 Free Software Foundation, Inc. + + This implementation for OPENSTEP conformance written by + Richard Frith-Macdonald + Created: August 1997, rewritten June 2000 + + based on original code - + + Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + + Written by: Andrew Kachites McCallum + Created: July 1994 + + 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Setup for inline operation of pointer map tables. + */ +#define GSI_MAP_RETAIN_KEY(X) +#define GSI_MAP_RELEASE_KEY(X) +#define GSI_MAP_RETAIN_VAL(X) +#define GSI_MAP_RELEASE_VAL(X) +#define GSI_MAP_HASH(X) ((X).uint) +#define GSI_MAP_EQUAL(X,Y) ((X).uint == (Y).uint) + +#include + +/* + * Setup for inline operation of arrays. + */ +#define GSI_ARRAY_RETAIN(X) +#define GSI_ARRAY_RELEASE(X) +#define GSI_ARRAY_TYPES GSUNION_OBJ|GSUNION_SEL|GSUNION_STR + +#include + + + +#define _IN_PORT_CODER_M +#include +#undef _IN_PORT_CODER_M + +#include + +static BOOL debug_port_coder = NO; + +typedef unsigned char uchar; + +#define PREFIX "GNUstep DO archive" + +static SEL eSerSel = @selector(serializeDataAt:ofObjCType:context:); +static SEL eTagSel = @selector(serializeTypeTag:); +static SEL xRefSel = @selector(serializeTypeTag:andCrossRef:); +static SEL eObjSel = @selector(encodeObject:); +static SEL eValSel = @selector(encodeValueOfObjCType:at:); +static SEL dDesSel = @selector(deserializeDataAt:ofObjCType:atCursor:context:); +static SEL dTagSel = @selector(deserializeTypeTag:andCrossRef:atCursor:); +static SEL dValSel = @selector(decodeValueOfObjCType:at:); + + + +static const char* +typeToName1(char type) +{ + switch (type) + { + case _C_CLASS: return "class"; + case _C_ID: return "object"; + case _C_SEL: return "selector"; + case _C_CHR: return "char"; + case _C_UCHR: return "unsigned char"; + case _C_SHT: return "short"; + case _C_USHT: return "unsigned short"; + case _C_INT: return "int"; + case _C_UINT: return "unsigned int"; + case _C_LNG: return "long"; + case _C_ULNG: return "unsigned long"; + case _C_LNG_LNG: return "long long"; + case _C_ULNG_LNG: return "unsigned long long"; + case _C_FLT: return "float"; + case _C_DBL: return "double"; + case _C_PTR: return "pointer"; + case _C_CHARPTR: return "cstring"; + case _C_ARY_B: return "array"; + case _C_STRUCT_B: return "struct"; + default: + { + static char buf1[32]; + static char buf2[32]; + static char *bufptr = buf1; + + if (bufptr == buf1) + { + bufptr = buf2; + } + else + { + bufptr = buf1; + } + sprintf(bufptr, "unknown type info - 0x%x", type); + return bufptr; + } + } +} + +static const char* +typeToName2(char type) +{ + switch (type & _GSC_MASK) + { + case _GSC_CLASS: return "class"; + case _GSC_ID: return "object"; + case _GSC_SEL: return "selector"; + case _GSC_CHR: return "char"; + case _GSC_UCHR: return "unsigned char"; + case _GSC_SHT: return "short"; + case _GSC_USHT: return "unsigned short"; + case _GSC_INT: return "int"; + case _GSC_UINT: return "unsigned int"; + case _GSC_LNG: return "long"; + case _GSC_ULNG: return "unsigned long"; + case _GSC_LNG_LNG: return "long long"; + case _GSC_ULNG_LNG: return "unsigned long long"; + case _GSC_FLT: return "float"; + case _GSC_DBL: return "double"; + case _GSC_PTR: return "pointer"; + case _GSC_CHARPTR: return "cstring"; + case _GSC_ARY_B: return "array"; + case _GSC_STRUCT_B: return "struct"; + default: + { + static char buf1[32]; + static char buf2[32]; + static char *bufptr = buf1; + + if (bufptr == buf1) + { + bufptr = buf2; + } + else + { + bufptr = buf1; + } + sprintf(bufptr, "unknown type info - 0x%x", type); + return bufptr; + } + } +} + +/* + * There are thirtyone possible basic types. We reserve a type of zero + * to mean that no information is specified. The slots in this array + * MUST correspond to the definitions in NSData.h + */ +static char type_map[32] = { + 0, + _C_CHR, + _C_UCHR, + _C_SHT, + _C_USHT, + _C_INT, + _C_UINT, + _C_LNG, + _C_ULNG, +#ifdef _C_LNG_LNG + _C_LNG_LNG, + _C_ULNG_LNG, +#else + 0, + 0, +#endif + _C_FLT, + _C_DBL, + 0, + 0, + 0, + _C_ID, + _C_CLASS, + _C_SEL, + _C_PTR, + _C_CHARPTR, + _C_ARY_B, + _C_STRUCT_B, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +static inline void +typeCheck(char t1, char t2) +{ + if (type_map[(t2 & _GSC_MASK)] != t1) + { + [NSException raise: NSInternalInconsistencyException + format: @"expected %s and got %s", + typeToName1(t1), typeToName2(t2)]; + } +} + +@interface GSClassInfo : NSObject +{ +@public + Class class; + unsigned version; + NSString *name; +} ++ (id) newWithClass: (Class)c andVersion: (unsigned)v; +- (NSString*) className; +@end + +@implementation GSClassInfo ++ (id) newWithClass: (Class)c andVersion: (unsigned)v; +{ + GSClassInfo *info; + + info = (GSClassInfo*)NSAllocateObject(self, 0, NSDefaultMallocZone()); + if (info != nil) + { + info->class = c; + info->version = v; + } + return info; +} +- (NSString*) className +{ + if (name == nil) + { + name = RETAIN(NSStringFromClass(class)); + } + return name; +} +- (void) dealloc +{ + TEST_RELEASE(name); + NSDeallocateObject(self); +} +@end + + + + + +@interface NSPortCoder (Headers) +- (void) _deserializeHeaderAt: (unsigned*)pos + version: (unsigned*)v + classes: (unsigned*)c + objects: (unsigned*)o + pointers: (unsigned*)p; +- (void) _serializeHeaderAt: (unsigned)pos + version: (unsigned)v + classes: (unsigned)c + objects: (unsigned)o + pointers: (unsigned)p; +@end + + +@implementation NSPortCoder + +static Class connectionClass; +static Class mutableArrayClass; +static Class mutableDataClass; +static Class mutableDictionaryClass; + ++ (void) initialize +{ + if (self == [NSPortCoder class]) + { + connectionClass = [NSConnection class]; + mutableArrayClass = [NSMutableArray class]; + mutableDataClass = [NSMutableData class]; + mutableDictionaryClass = [NSMutableDictionary class]; + } +} + ++ (NSPortCoder*) portCoderWithReceivePort: (NSPort*)recv + sendPort: (NSPort*)send + components: (NSArray*)comp; +{ + id coder; + + coder = [self allocWithZone: NSDefaultMallocZone()]; + coder = [coder initWithReceivePort: recv sendPort: send components: comp]; + AUTORELEASE(coder); + return coder; +} + +- (NSConnection*) connection +{ + return _conn; +} + +- (void) dealloc +{ + RELEASE(_comp); + RELEASE(_conn); + RELEASE(_cInfo); + if (_clsMap != 0) + { + GSIMapEmptyMap(_clsMap); + GSIMapEmptyMap(_cIdMap); + GSIMapEmptyMap(_uIdMap); + GSIMapEmptyMap(_ptrMap); + NSZoneFree(_clsMap->zone, (void*)_clsMap); + } + if (_clsAry != 0) + { + unsigned count = GSIArrayCount(_clsAry); + + while (count-- > 0) + { + RELEASE(GSIArrayItemAtIndex(_clsAry, count).obj); + } + GSIArrayClear(_clsAry); + GSIArrayClear(_objAry); + GSIArrayClear(_ptrAry); + NSZoneFree(_clsAry->zone, (void*)_clsAry); + } + + [super dealloc]; +} + +- (void) decodeArrayOfObjCType: (const char*)type + count: (unsigned)expected + at: (void*)buf +{ + int i; + int offset = 0; + int size = objc_sizeof_type(type); + unsigned char info; + unsigned count; + + (*_dTagImp)(_src, dTagSel, &info, 0, &_cursor); + (*_dDesImp)(_src, dDesSel, &count, @encode(unsigned), &_cursor, nil); + if (info != _GSC_ARY_B) + { + [NSException raise: NSInternalInconsistencyException + format: @"expected array and got %s", typeToName2(info)]; + } + if (count != expected) + { + [NSException raise: NSInternalInconsistencyException + format: @"expected array count %u and got %u", + expected, count]; + } + + switch (*type) + { + case _C_ID: info = _GSC_NONE; break; + case _C_CHR: info = _GSC_CHR; break; + case _C_UCHR: info = _GSC_UCHR; break; + case _C_SHT: info = _GSC_SHT; break; + case _C_USHT: info = _GSC_USHT; break; + case _C_INT: info = _GSC_INT; break; + case _C_UINT: info = _GSC_UINT; break; + case _C_LNG: info = _GSC_LNG; break; + case _C_ULNG: info = _GSC_ULNG; break; +#ifdef _C_LNG_LNG + case _C_LNG_LNG: info = _GSC_LNG_LNG; break; + case _C_ULNG_LNG: info = _GSC_ULNG_LNG; break; +#endif + case _C_FLT: info = _GSC_FLT; break; + case _C_DBL: info = _GSC_DBL; break; + default: info = _GSC_NONE; break; + } + + if (info == _GSC_NONE) + { + for (i = 0; i < count; i++) + { + (*_dValImp)(self, dValSel, type, (char*)buf + offset); + offset += size; + } + } + else + { + unsigned char ainfo; + + (*_dTagImp)(_src, dTagSel, &ainfo, 0, &_cursor); + if (info != (ainfo & _GSC_MASK)) + { + [NSException raise: NSInternalInconsistencyException + format: @"expected %s and got %s", + typeToName2(info), typeToName2(ainfo)]; + } + + for (i = 0; i < count; i++) + { + (*_dDesImp)(_src, dDesSel, (char*)buf + offset, type, &_cursor, nil); + offset += size; + } + } +} + +- (NSData*) decodeDataObject +{ + int pos; + + [self decodeValueOfObjCType: @encode(int) at: &pos]; + if (pos >= 0) + { + return [_comp objectAtIndex: pos]; + } + else if (pos == -1) + { + return nil; + } + else if (pos == -2) + { + return [mutableDataClass data]; + } + else + { + [NSException raise: NSInternalInconsistencyException + format: @"Bad tag (%d) decoding data object", pos]; + return nil; + } +} + +- (NSPort*) decodePortObject +{ + unsigned pos; + + [self decodeValueOfObjCType: @encode(unsigned) at: &pos]; + return [_comp objectAtIndex: pos]; +} + +/* + * The [-decodeObject] method is implemented purely for performance - + * It duplicates the code for handling objects in the + * [-decodeValueOfObjCType:at:] method above, but differs in that the + * resulting object is autoreleased when it comes from this method. + */ +- (id) decodeObject +{ + unsigned char info; + unsigned xref; + id obj; + + (*_dTagImp)(_src, dTagSel, &info, &xref, &_cursor); + if ((info & _GSC_MASK) != _GSC_ID) + { + [NSException raise: NSInternalInconsistencyException + format: @"expected object and got %s", typeToName2(info)]; + } + + /* + * Special case - a zero crossref value is a nil pointer. + */ + if ((info & _GSC_SIZE) == 0) + { + return nil; + } + + if (info & _GSC_XREF) + { + if (xref >= GSIArrayCount(_objAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"object crossref missing - %d", + xref]; + } + obj = GSIArrayItemAtIndex(_objAry, xref).obj; + /* + * If it's a cross-reference, we don't need to autorelease it + * since we didn't create it. + */ + return obj; + } + else + { + Class c; + id rep; + + if (xref != GSIArrayCount(_objAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"extra object crossref - %d", + xref]; + } + (*_dValImp)(self, dValSel, @encode(Class), &c); + + obj = [c allocWithZone: _zone]; + GSIArrayAddItem(_objAry, (GSIArrayItem)obj); + + rep = [obj initWithCoder: self]; + if (rep != obj) + { + obj = rep; + GSIArraySetItemAtIndex(_objAry, (GSIArrayItem)obj, xref); + } + + rep = [obj awakeAfterUsingCoder: self]; + if (rep != obj) + { + obj = rep; + GSIArraySetItemAtIndex(_objAry, (GSIArrayItem)obj, xref); + } + /* + * A newly allocated object needs to be autoreleased. + */ + return AUTORELEASE(obj); + } +} + +- (void) decodeValueOfObjCType: (const char*)type + at: (void*)address +{ + unsigned xref; + unsigned char info; +#if GS_HAVE_I128 + gsu128 bigval; +#else +#if GS_HAVE_I64 + gsu64 bigval; +#else + gsu32 bigval; +#endif +#endif + + (*_dTagImp)(_src, dTagSel, &info, &xref, &_cursor); + + switch (info & _GSC_MASK) + { + case _GSC_ID: + { + id obj; + + typeCheck(*type, _GSC_ID); + /* + * Special case - a zero crossref value size is a nil pointer. + */ + if ((info & _GSC_SIZE) == 0) + { + obj = nil; + } + else + { + if (info & _GSC_XREF) + { + if (xref >= GSIArrayCount(_objAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"object crossref missing - %d", + xref]; + } + obj = GSIArrayItemAtIndex(_objAry, xref).obj; + /* + * If it's a cross-reference, we need to retain it in + * order to give the appearance that it's actually a + * new object. + */ + IF_NO_GC(RETAIN(obj)); + } + else + { + Class c; + id rep; + + if (xref != GSIArrayCount(_objAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"extra object crossref - %d", + xref]; + } + (*_dValImp)(self, dValSel, @encode(Class), &c); + + obj = [c allocWithZone: _zone]; + GSIArrayAddItem(_objAry, (GSIArrayItem)obj); + + rep = [obj initWithCoder: self]; + if (rep != obj) + { + obj = rep; + GSIArraySetItemAtIndex(_objAry, (GSIArrayItem)obj, xref); + } + + rep = [obj awakeAfterUsingCoder: self]; + if (rep != obj) + { + obj = rep; + GSIArraySetItemAtIndex(_objAry, (GSIArrayItem)obj, xref); + } + } + } + *(id*)address = obj; + return; + } + + case _GSC_CLASS: + { + Class c; + GSClassInfo *classInfo; + Class dummy; + + typeCheck(*type, _GSC_CLASS); + /* + * Special case - a zero crossref value size is a nil pointer. + */ + if ((info & _GSC_SIZE) == 0) + { + *(SEL*)address = 0; + return; + } + if (info & _GSC_XREF) + { + if (xref >= GSIArrayCount(_clsAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"class crossref missing - %d", xref]; + } + classInfo = (GSClassInfo*)GSIArrayItemAtIndex(_clsAry, xref).obj; + *(Class*)address = classInfo->class; + return; + } + while ((info & _GSC_MASK) == _GSC_CLASS) + { + unsigned cver; + + if (xref != GSIArrayCount(_clsAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"extra class crossref - %d", xref]; + } + (*_dDesImp)(_src, dDesSel, &c, @encode(Class), &_cursor, nil); + (*_dDesImp)(_src, dDesSel, &cver, @encode(unsigned), &_cursor, + nil); + if (c == 0) + { + [NSException raise: NSInternalInconsistencyException + format: @"decoded nil class"]; + } + classInfo = [GSClassInfo newWithClass: c andVersion: cver]; + GSIArrayAddItem(_clsAry, (GSIArrayItem)classInfo); + *(Class*)address = classInfo->class; + /* + * Point the address to a dummy location and read the + * next tag - if it is another class, loop to get it. + */ + address = &dummy; + (*_dTagImp)(_src, dTagSel, &info, &xref, &_cursor); + } + if (info != _GSC_NONE) + { + [NSException raise: NSInternalInconsistencyException + format: @"class list improperly terminated"]; + } + return; + } + + case _GSC_SEL: + { + SEL sel; + + typeCheck(*type, _GSC_SEL); + /* + * Special case - a zero crossref value size is a nil pointer. + */ + if ((info & _GSC_SIZE) == 0) + { + *(SEL*)address = 0; + return; + } + if (info & _GSC_XREF) + { + if (xref >= GSIArrayCount(_ptrAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"sel crossref missing - %d", xref]; + } + sel = GSIArrayItemAtIndex(_ptrAry, xref).sel; + } + else + { + if (xref != GSIArrayCount(_ptrAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"extra sel crossref - %d", xref]; + } + (*_dDesImp)(_src, dDesSel, &sel, @encode(SEL), &_cursor, nil); + GSIArrayAddItem(_ptrAry, (GSIArrayItem)sel); + } + *(SEL*)address = sel; + return; + } + + case _GSC_ARY_B: + { + int count; + + typeCheck(*type, _GSC_ARY_B); + count = atoi(++type); + while (isdigit(*type)) + { + type++; + } + [self decodeArrayOfObjCType: type count: count at: address]; + return; + } + + case _GSC_STRUCT_B: + { + int offset = 0; + + typeCheck(*type, _GSC_STRUCT_B); + while (*type != _C_STRUCT_E && *type++ != '='); /* skip "=" */ + for (;;) + { + (*_dValImp)(self, dValSel, type, (char*)address + offset); + offset += objc_sizeof_type(type); + type = objc_skip_typespec(type); + if (*type == _C_STRUCT_E) + { + break; + } + else + { + int align = objc_alignof_type(type); + int rem = offset % align; + + if (rem != 0) + { + offset += align - rem; + } + } + } + return; + } + + case _GSC_PTR: + { + typeCheck(*type, _GSC_PTR); + /* + * Special case - a zero crossref value size is a nil pointer. + */ + if ((info & _GSC_SIZE) == 0) + { + *(void**)address = 0; + return; + } + if (info & _GSC_XREF) + { + if (xref >= GSIArrayCount(_ptrAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"ptr crossref missing - %d", xref]; + } + *(void**)address = GSIArrayItemAtIndex(_ptrAry, xref).ptr; + } + else + { + unsigned size; + + if (GSIArrayCount(_ptrAry) != xref) + { + [NSException raise: NSInternalInconsistencyException + format: @"extra ptr crossref - %d", xref]; + } + + /* + * Allocate memory for object to be decoded into and + * add it to the crossref map. + */ + size = objc_sizeof_type(++type); + *(void**)address = _fastMallocBuffer(size); + GSIArrayAddItem(_ptrAry, (GSIArrayItem)*(void**)address); + + /* + * Decode value and add memory to map for crossrefs. + */ + (*_dValImp)(self, dValSel, type, *(void**)address); + } + return; + } + + case _GSC_CHARPTR: + { + typeCheck(*type, _GSC_CHARPTR); + /* + * Special case - a zero crossref value size is a nil pointer. + */ + if ((info & _GSC_SIZE) == 0) + { + *(char**)address = 0; + return; + } + if (info & _GSC_XREF) + { + if (xref >= GSIArrayCount(_ptrAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"string crossref missing - %d", xref]; + } + *(char**)address = GSIArrayItemAtIndex(_ptrAry, xref).str; + } + else + { + if (xref != GSIArrayCount(_ptrAry)) + { + [NSException raise: NSInternalInconsistencyException + format: @"extra string crossref - %d", xref]; + } + (*_dDesImp)(_src, dDesSel, address, @encode(char*), &_cursor, + nil); + GSIArrayAddItem(_ptrAry, (GSIArrayItem)*(void**)address); + } + return; + } + + case _GSC_CHR: + case _GSC_UCHR: + typeCheck(*type, info & _GSC_MASK); + (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); + return; + + case _GSC_SHT: + case _GSC_USHT: + typeCheck(*type, info & _GSC_MASK); + if ((info & _GSC_SIZE) == _GSC_S_SHT) + { + (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); + return; + } + break; + + case _GSC_INT: + case _GSC_UINT: + typeCheck(*type, info & _GSC_MASK); + if ((info & _GSC_SIZE) == _GSC_S_INT) + { + (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); + return; + } + break; + + case _GSC_LNG: + case _GSC_ULNG: + typeCheck(*type, info & _GSC_MASK); + if ((info & _GSC_SIZE) == _GSC_S_LNG) + { + (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); + return; + } + break; + +#ifdef _C_LNG_LNG + case _GSC_LNG_LNG: + case _GSC_ULNG_LNG: + typeCheck(*type, info & _GSC_MASK); + if ((info & _GSC_SIZE) == _GSC_S_LNG_LNG) + { + (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); + return; + } + break; + +#endif + case _GSC_FLT: + typeCheck(*type, _GSC_FLT); + (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); + return; + + case _GSC_DBL: + typeCheck(*type, _GSC_DBL); + (*_dDesImp)(_src, dDesSel, address, type, &_cursor, nil); + return; + + default: + [NSException raise: NSInternalInconsistencyException + format: @"read unknown type info - %d", info]; + } + + /* + * We fall through to here only when we have to decode a value + * whose natural size on this system is not the same as on the + * machine on which the archive was created. + */ + + /* + * First, we read the data and convert it to the largest size + * this system can support. + */ + switch (info & _GSC_SIZE) + { + case _GSC_I16: /* Encoded as 16-bit */ + { + gsu16 val; + + (*_dDesImp)(_src, dDesSel, &val, @encode(gsu16), &_cursor, nil); + bigval = val; + break; + } + + case _GSC_I32: /* Encoded as 32-bit */ + { + gsu32 val; + + (*_dDesImp)(_src, dDesSel, &val, @encode(gsu32), &_cursor, nil); + bigval = val; + break; + } + + case _GSC_I64: /* Encoded as 64-bit */ + { + gsu64 val; + + (*_dDesImp)(_src, dDesSel, &val, @encode(gsu64), &_cursor, nil); +#if GS_HAVE_I64 + bigval = val; +#else + bigval = GSSwapBigI64ToHost(val); +#endif + break; + } + + default: /* A 128-bit value */ + { + gsu128 val; + + (*_dDesImp)(_src, dDesSel, &val, @encode(gsu128), &_cursor, nil); +#if GS_HAVE_I128 + bigval = val; +#else + val = GSSwapBigI128ToHost(val); +#if GS_HAVE_I64 + bigval = *(gsu64*)&val; +#else + bigval = *(gsu32*)&val; +#endif +#endif + break; + } + } + +/* + * Now we copy from the 'bigval' to the destination location. + */ + switch (info & _GSC_MASK) + { + case _GSC_SHT: + *(short*)address = (short)bigval; + return; + case _GSC_USHT: + *(unsigned short*)address = (unsigned short)bigval; + return; + case _GSC_INT: + *(int*)address = (int)bigval; + return; + case _GSC_UINT: + *(unsigned int*)address = (unsigned int)bigval; + return; + case _GSC_LNG: + *(long*)address = (long)bigval; + return; + case _GSC_ULNG: + *(unsigned long*)address = (unsigned long)bigval; + return; +#ifdef _C_LNG_LNG + case _GSC_LNG_LNG: + *(long long*)address = (long long)bigval; + return; + case _GSC_ULNG_LNG: + *(unsigned long long*)address = (unsigned long long)bigval; + return; +#endif + default: + [NSException raise: NSInternalInconsistencyException + format: @"type/size information error"]; + } +} + +- (void) dispatch +{ + /* + * Get ready for re-use + * Make sure that we don't retain the connection - or it might never be + * released if it is keeping this coder in a cache. + */ + DESTROY(_conn); +} + +- (void) encodeArrayOfObjCType: (const char*)type + count: (unsigned)count + at: (const void*)buf +{ + unsigned i; + unsigned offset = 0; + unsigned size = objc_sizeof_type(type); + uchar info; + + switch (*type) + { + case _C_ID: info = _GSC_NONE; break; + case _C_CHR: info = _GSC_CHR; break; + case _C_UCHR: info = _GSC_UCHR; break; + case _C_SHT: info = _GSC_SHT | _GSC_S_SHT; break; + case _C_USHT: info = _GSC_USHT | _GSC_S_SHT; break; + case _C_INT: info = _GSC_INT | _GSC_S_INT; break; + case _C_UINT: info = _GSC_UINT | _GSC_S_INT; break; + case _C_LNG: info = _GSC_LNG | _GSC_S_LNG; break; + case _C_ULNG: info = _GSC_ULNG | _GSC_S_LNG; break; + case _C_LNG_LNG: info = _GSC_LNG_LNG | _GSC_S_LNG_LNG; break; + case _C_ULNG_LNG: info = _GSC_ULNG_LNG | _GSC_S_LNG_LNG; break; + case _C_FLT: info = _GSC_FLT; break; + case _C_DBL: info = _GSC_DBL; break; + default: info = _GSC_NONE; break; + } + + /* + * Simple types can be serialized immediately, more complex ones + * are dealt with by our [encodeValueOfObjCType:at:] method. + */ + if (info == _GSC_NONE) + { + if (_initialPass == NO) + { + (*_eTagImp)(_dst, eTagSel, _GSC_ARY_B); + (*_eSerImp)(_dst, eSerSel, &count, @encode(unsigned), nil); + } + for (i = 0; i < count; i++) + { + (*_eValImp)(self, eValSel, type, (char*)buf + offset); + offset += size; + } + } + else if (_initialPass == NO) + { + (*_eTagImp)(_dst, eTagSel, _GSC_ARY_B); + (*_eSerImp)(_dst, eSerSel, &count, @encode(unsigned), nil); + + (*_eTagImp)(_dst, eTagSel, info); + for (i = 0; i < count; i++) + { + (*_eSerImp)(_dst, eSerSel, (char*)buf + offset, type, nil); + offset += size; + } + } +} + +- (void) encodeBycopyObject: (id)anObj +{ + BOOL oldBycopy = _is_by_copy; + BOOL oldByref = _is_by_ref; + + _is_by_copy = YES; + _is_by_ref = NO; + (*_eObjImp)(self, eObjSel, anObj); + _is_by_copy = oldBycopy; + _is_by_ref = oldByref; +} + +- (void) encodeByrefObject: (id)anObj +{ + BOOL oldBycopy = _is_by_copy; + BOOL oldByref = _is_by_ref; + + _is_by_copy = NO; + _is_by_ref = YES; + (*_eObjImp)(self, eObjSel, anObj); + _is_by_copy = oldBycopy; + _is_by_ref = oldByref; +} + +- (void) encodeConditionalObject: (id)anObject +{ + if (_encodingRoot == NO) + { + [NSException raise: NSInvalidArgumentException + format: @"conditionally encoding without root object"]; + return; + } + + if (_initialPass) + { + GSIMapNode node; + + /* + * Conditionally encoding 'nil' is a no-op. + */ + if (anObject == nil) + { + return; + } + + /* + * If we have already conditionally encoded this object, we can + * ignore it this time. + */ + node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject); + if (node != 0) + { + return; + } + + /* + * If we have unconditionally encoded this object, we can ignore + * it now. + */ + node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject); + if (node != 0) + { + return; + } + + GSIMapAddPair(_cIdMap, (GSIMapKey)anObject, (GSIMapVal)0); + } + else if (anObject == nil) + { + (*_eObjImp)(self, eObjSel, nil); + } + else + { + GSIMapNode node; + + node = GSIMapNodeForKey(_cIdMap, (GSIMapKey)anObject); + if (node != 0) + { + (*_eObjImp)(self, eObjSel, nil); + } + else + { + (*_eObjImp)(self, eObjSel, anObject); + } + } +} + +/* + * When asked to encode a data object, we add the object to the components + * array, and simply record the array index, so the corresponding decode + * method can tell which component to use. + */ +- (void) encodeDataObject: (NSData*)anObject +{ + int pos; + + if (anObject == nil) + { + pos = -1; + } + else if ([anObject length] == 0) + { + pos = -2; + } + else + { + pos = (int)[_comp count]; + [_comp addObject: anObject]; + } + [self encodeValueOfObjCType: @encode(int) at: &pos]; +} + +- (void) encodeObject: (id)anObject +{ + if (anObject == nil) + { + if (_initialPass == NO) + { + /* + * Special case - encode a nil pointer as a crossref of zero. + */ + (*_eTagImp)(_dst, eTagSel, _GSC_ID | _GSC_XREF, _GSC_X_0); + } + } + else if (fastIsInstance(anObject) == NO) + { + /* + * If the object we have been given is actually a class, + * we encode it as a class instead. + */ + (*_eValImp)(self, eValSel, @encode(Class), &anObject); + } + else + { + GSIMapNode node; + + /* + * See if the object has already been encoded. + */ + node = GSIMapNodeForKey(_uIdMap, (GSIMapKey)anObject); + + if (_initialPass) + { + if (node == 0) + { + /* + * Remove object from map of conditionally encoded objects + * and add it to the map of unconditionay encoded ones. + */ + GSIMapRemoveKey(_cIdMap, (GSIMapKey)anObject); + GSIMapAddPair(_uIdMap, (GSIMapKey)anObject, (GSIMapVal)0); + [anObject encodeWithCoder: self]; + } + return; + } + + if (node == 0 || node->value.uint == 0) + { + Class cls; + id obj; + + if (node == 0) + { + node = GSIMapAddPair(_uIdMap, + (GSIMapKey)anObject, (GSIMapVal)++_xRefO); + } + else + { + node->value.uint = ++_xRefO; + } + + obj = [anObject replacementObjectForPortCoder: self]; + cls = [obj classForPortCoder]; + + (*_xRefImp)(_dst, xRefSel, _GSC_ID, node->value.uint); + (*_eValImp)(self, eValSel, @encode(Class), &cls); + [obj encodeWithCoder: self]; + } + else + { + (*_xRefImp)(_dst, xRefSel, _GSC_ID | _GSC_XREF, node->value.uint); + } + } +} + +/* + * When asked to encode a port object, we add the object to the components + * array, and simply record the array index, so the corresponding decode + * method can tell which component to use. + */ +- (void) encodePortObject: (NSPort*)anObject +{ + unsigned pos = [_comp count]; + + [_comp addObject: anObject]; + [self encodeValueOfObjCType: @encode(unsigned) at: &pos]; +} + +- (void) encodeRootObject: (id)rootObject +{ + if (_encodingRoot) + { + [NSException raise: NSInvalidArgumentException + format: @"encoding root object more than once"]; + } + + _encodingRoot = YES; + + /* + * First pass - find conditional objects. + */ + _initialPass = YES; + (*_eObjImp)(self, eObjSel, rootObject); + + /* + * Second pass - write archive. + */ + _initialPass = NO; + (*_eObjImp)(self, eObjSel, rootObject); + + /* + * Write sizes of crossref arrays to head of archive. + */ + [self _serializeHeaderAt: _cursor + version: [self systemVersion] + classes: _clsMap->nodeCount + objects: _uIdMap->nodeCount + pointers: _ptrMap->nodeCount]; + + _encodingRoot = NO; +} + +- (void) encodeValueOfObjCType: (const char*)type + at: (const void*)buf +{ + switch (*type) + { + case _C_ID: + (*_eObjImp)(self, eObjSel, *(void**)buf); + return; + + case _C_ARY_B: + { + int count = atoi(++type); + + while (isdigit(*type)) + { + type++; + } + + if (_initialPass == NO) + { + (*_eTagImp)(_dst, eTagSel, _GSC_ARY_B); + } + + [self encodeArrayOfObjCType: type count: count at: buf]; + } + return; + + case _C_STRUCT_B: + { + int offset = 0; + + if (_initialPass == NO) + { + (*_eTagImp)(_dst, eTagSel, _GSC_STRUCT_B); + } + + while (*type != _C_STRUCT_E && *type++ != '='); /* skip "=" */ + + for (;;) + { + (*_eValImp)(self, eValSel, type, (char*)buf + offset); + offset += objc_sizeof_type(type); + type = objc_skip_typespec(type); + if (*type == _C_STRUCT_E) + { + break; + } + else + { + int align = objc_alignof_type(type); + int rem = offset % align; + + if (rem != 0) + { + offset += align - rem; + } + } + } + } + return; + + case _C_PTR: + if (*(void**)buf == 0) + { + if (_initialPass == NO) + { + /* + * Special case - a nul pointer gets an xref of zero + */ + (*_eTagImp)(_dst, eTagSel, _GSC_PTR | _GSC_XREF | _GSC_X_0); + } + } + else + { + GSIMapNode node; + + node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)*(void**)buf); + if (_initialPass == YES) + { + /* + * First pass - add pointer to map and encode item pointed + * to in case it is a conditionally encoded object. + */ + if (node == 0) + { + GSIMapAddPair(_ptrMap, + (GSIMapKey)*(void**)buf, (GSIMapVal)0); + type++; + buf = *(char**)buf; + (*_eValImp)(self, eValSel, type, buf); + } + } + else if (node == 0 || node->value.uint == 0) + { + /* + * Second pass, unwritten pointer - write it. + */ + if (node == 0) + { + node = GSIMapAddPair(_ptrMap, + (GSIMapKey)*(void**)buf, (GSIMapVal)++_xRefP); + } + else + { + node->value.uint = ++_xRefP; + } + (*_xRefImp)(_dst, xRefSel, _GSC_PTR, node->value.uint); + type++; + buf = *(char**)buf; + (*_eValImp)(self, eValSel, type, buf); + } + else + { + /* + * Second pass, write a cross-reference number. + */ + (*_xRefImp)(_dst, xRefSel, _GSC_PTR|_GSC_XREF, + node->value.uint); + } + } + return; + + default: /* Types that can be ignored in first pass. */ + if (_initialPass) + { + return; + } + break; + } + + switch (*type) + { + case _C_CLASS: + if (*(Class*)buf == 0) + { + /* + * Special case - a nul pointer gets an xref of zero + */ + (*_eTagImp)(_dst, eTagSel, _GSC_CLASS | _GSC_XREF | _GSC_X_0); + } + else + { + Class c = *(Class*)buf; + GSIMapNode node; + BOOL done = NO; + + node = GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)c); + + if (node != 0) + { + (*_xRefImp)(_dst, xRefSel, _GSC_CLASS | _GSC_XREF, + node->value.uint); + return; + } + while (done == NO) + { + int tmp = fastClassVersion(c); + unsigned version = tmp; + Class s = fastSuper(c); + + if (tmp < 0) + { + [NSException raise: NSInternalInconsistencyException + format: @"negative class version"]; + } + node = GSIMapAddPair(_clsMap, + (GSIMapKey)(void*)c, (GSIMapVal)++_xRefC); + /* + * Encode tag and crossref number. + */ + (*_xRefImp)(_dst, xRefSel, _GSC_CLASS, node->value.uint); + /* + * Encode class, and version. + */ + (*_eSerImp)(_dst, eSerSel, &c, @encode(Class), nil); + (*_eSerImp)(_dst, eSerSel, &version, @encode(unsigned), nil); + /* + * If we have a super class that has not been encoded, + * we must loop round to encode it here so that its + * version information will be available when objects + * of its subclasses are decoded and call + * [super initWithCoder:ccc] + */ + if (s == c || s == 0 + || GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)s) != 0) + { + done = YES; + } + else + { + c = s; + } + } + /* + * Encode an empty tag to terminate the list of classes. + */ + (*_eTagImp)(_dst, eTagSel, _GSC_NONE); + } + return; + + case _C_SEL: + if (*(SEL*)buf == 0) + { + /* + * Special case - a nul pointer gets an xref of zero + */ + (*_eTagImp)(_dst, eTagSel, _GSC_SEL | _GSC_XREF | _GSC_X_0); + } + else + { + SEL s = *(SEL*)buf; + GSIMapNode node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)(void*)s); + + if (node == 0) + { + node = GSIMapAddPair(_ptrMap, + (GSIMapKey)(void*)s, (GSIMapVal)++_xRefP); + (*_xRefImp)(_dst, xRefSel, _GSC_SEL, node->value.uint); + /* + * Encode selector. + */ + (*_eSerImp)(_dst, eSerSel, buf, @encode(SEL), nil); + } + else + { + (*_xRefImp)(_dst, xRefSel, _GSC_SEL|_GSC_XREF, + node->value.uint); + } + } + return; + + case _C_CHARPTR: + if (*(char**)buf == 0) + { + /* + * Special case - a nul pointer gets an xref of zero + */ + (*_eTagImp)(_dst, eTagSel, _GSC_CHARPTR | _GSC_XREF | _GSC_X_0); + } + else + { + GSIMapNode node; + + node = GSIMapNodeForKey(_ptrMap, (GSIMapKey)*(char**)buf); + if (node == 0) + { + node = GSIMapAddPair(_ptrMap, + (GSIMapKey)*(char**)buf, (GSIMapVal)++_xRefP); + (*_xRefImp)(_dst, xRefSel, _GSC_CHARPTR, node->value.uint); + (*_eSerImp)(_dst, eSerSel, buf, type, nil); + } + else + { + (*_xRefImp)(_dst, xRefSel, _GSC_CHARPTR|_GSC_XREF, + node->value.uint); + } + } + return; + + case _C_CHR: + (*_eTagImp)(_dst, eTagSel, _GSC_CHR); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(char), nil); + return; + + case _C_UCHR: + (*_eTagImp)(_dst, eTagSel, _GSC_UCHR); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned char), nil); + return; + + case _C_SHT: + (*_eTagImp)(_dst, eTagSel, _GSC_SHT | _GSC_S_SHT); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(short), nil); + return; + + case _C_USHT: + (*_eTagImp)(_dst, eTagSel, _GSC_USHT | _GSC_S_SHT); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned short), nil); + return; + + case _C_INT: + (*_eTagImp)(_dst, eTagSel, _GSC_INT | _GSC_S_INT); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(int), nil); + return; + + case _C_UINT: + (*_eTagImp)(_dst, eTagSel, _GSC_UINT | _GSC_S_INT); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned int), nil); + return; + + case _C_LNG: + (*_eTagImp)(_dst, eTagSel, _GSC_LNG | _GSC_S_LNG); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(long), nil); + return; + + case _C_ULNG: + (*_eTagImp)(_dst, eTagSel, _GSC_ULNG | _GSC_S_LNG); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned long), nil); + return; + + case _C_LNG_LNG: + (*_eTagImp)(_dst, eTagSel, _GSC_LNG_LNG | _GSC_S_LNG_LNG); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(long long), nil); + return; + + case _C_ULNG_LNG: + (*_eTagImp)(_dst, eTagSel, _GSC_ULNG_LNG | _GSC_S_LNG_LNG); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned long long), + nil); + return; + + case _C_FLT: + (*_eTagImp)(_dst, eTagSel, _GSC_FLT); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(float), nil); + return; + + case _C_DBL: + (*_eTagImp)(_dst, eTagSel, _GSC_DBL); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(double), nil); + return; + + case _C_VOID: + [NSException raise: NSInvalidArgumentException + format: @"can't encode void item"]; + + default: + [NSException raise: NSInvalidArgumentException + format: @"item with unknown type - %s", type]; + } +} + +- (id) initWithReceivePort: (NSPort*)recv + sendPort: (NSPort*)send + components: (NSArray*)comp +{ + BOOL firstTime; + + _conn = RETAIN([connectionClass connectionWithReceivePort: recv + sendPort: send]); + if (_comp == nil) + { + firstTime = YES; + _version = [super systemVersion]; + _zone = NSDefaultMallocZone(); + } + else + { + NSAssert(recv == [_conn receivePort] && send == [_conn sendPort], + NSInvalidArgumentException); + /* + * Re-initialising - destroy old components. + */ + firstTime = NO; + } + + if (comp == nil) + { + NS_DURING + { + _encodingRoot = NO; + _initialPass = NO; + _xRefC = 0; + _xRefO = 0; + _xRefP = 0; + + _cursor = [send reservedSpaceLength]; + if (firstTime == YES) + { + /* + * Set up mutable data object to encode into - reserve space at + * the start for use by the port when the encoded data is sent. + * Make the data item the first component of the array. + */ + _comp = [mutableArrayClass new]; + _dst = [mutableDataClass allocWithZone: _zone]; + _dst = [_dst initWithLength: _cursor]; + [_comp addObject: _dst]; + RELEASE(_dst); + + /* + * Cache method implementations for writing into data object etc + */ + _eSerImp = [_dst methodForSelector: eSerSel]; + _eTagImp = [_dst methodForSelector: eTagSel]; + _xRefImp = [_dst methodForSelector: xRefSel]; + _eObjImp = [self methodForSelector: eObjSel]; + _eValImp = [self methodForSelector: eValSel]; + + /* + * Set up map tables. + */ + _clsMap + = (GSIMapTable)NSZoneMalloc(_zone, sizeof(GSIMapTable_t)*4); + _cIdMap = &_clsMap[1]; + _uIdMap = &_clsMap[2]; + _ptrMap = &_clsMap[3]; + GSIMapInitWithZoneAndCapacity(_clsMap, _zone, 100); + GSIMapInitWithZoneAndCapacity(_cIdMap, _zone, 10); + GSIMapInitWithZoneAndCapacity(_uIdMap, _zone, 200); + GSIMapInitWithZoneAndCapacity(_ptrMap, _zone, 100); + } + else + { + unsigned count; + + /* + * If re-initialising, we just need to empty the old stuff. + */ + count = [_comp count]; + while (count-- > 1) + { + [_comp removeObjectAtIndex: count]; + } + [_dst setLength: _cursor]; + GSIMapCleanMap(_clsMap); + GSIMapCleanMap(_cIdMap); + GSIMapCleanMap(_uIdMap); + GSIMapCleanMap(_ptrMap); + } + + /* + * Write dummy header + */ + [self _serializeHeaderAt: _cursor + version: 0 + classes: 0 + objects: 0 + pointers: 0]; + } + NS_HANDLER + { + NSLog(@"Exception setting up port coder for encoding - %@", + localException); + DESTROY(self); + } + NS_ENDHANDLER + } + else + { + RELEASE(_comp); + _comp = [comp mutableCopy]; + NS_DURING + { + unsigned sizeC; + unsigned sizeO; + unsigned sizeP; + + if (firstTime == YES) + { + _dValImp = [self methodForSelector: dValSel]; + } + _src = [_comp objectAtIndex: 0]; + _dDesImp = [_src methodForSelector: dDesSel]; + _dTagImp = (void (*)(id, SEL, unsigned char*, unsigned*, unsigned*)) + [_src methodForSelector: dTagSel]; + + /* + * _cInfo is a dictionary of objects for keeping track of the + * version numbers that the classes were encoded with. + */ + if (firstTime == NO) + { + [_cInfo removeAllObjects]; + } + + /* + * Read header including version and crossref table sizes. + */ + _cursor = 0; + [self _deserializeHeaderAt: &_cursor + version: &_version + classes: &sizeC + objects: &sizeO + pointers: &sizeP]; + + /* + * Allocate and initialise arrays to build crossref maps in. + */ + if (firstTime == YES) + { + _clsAry = NSZoneMalloc(_zone, sizeof(GSIArray_t)*3); + _objAry = &_clsAry[1]; + _ptrAry = &_clsAry[2]; + GSIArrayInitWithZoneAndCapacity(_clsAry, _zone, sizeC); + GSIArrayInitWithZoneAndCapacity(_objAry, _zone, sizeO); + GSIArrayInitWithZoneAndCapacity(_ptrAry, _zone, sizeP); + } + else + { + unsigned count = GSIArrayCount(_clsAry); + + while (count-- > 0) + { + RELEASE(GSIArrayItemAtIndex(_clsAry, count).obj); + } + GSIArrayRemoveAllItems(_clsAry); + GSIArrayRemoveAllItems(_objAry); + GSIArrayRemoveAllItems(_ptrAry); + } + GSIArrayAddItem(_clsAry, (GSIArrayItem)nil); + GSIArrayAddItem(_objAry, (GSIArrayItem)nil); + GSIArrayAddItem(_ptrAry, (GSIArrayItem)0); + } + NS_HANDLER + { + NSLog(@"Exception setting up port coder for decoding - %@", + localException); + DESTROY(self); + } + NS_ENDHANDLER + } + return self; +} + +- (BOOL) isBycopy +{ + return _is_by_copy; +} + +- (BOOL) isByref +{ + return _is_by_ref; +} + +- (NSZone*) objectZone +{ + return _zone; +} + +- (void) setObjectZone: (NSZone*)aZone +{ + _zone = aZone; +} + +- (unsigned) systemVersion +{ + return _version; +} + +- (unsigned) versionForClassName: (NSString*)className +{ + GSClassInfo *info = nil; + unsigned version = NSNotFound; + unsigned count = GSIArrayCount(_clsAry); + + /* + * Lazy ... we construct a dictionary of all the class information in + * the request the first time that class version info is asked for. + */ + if (_cInfo == nil) + { + _cInfo = [[mutableDictionaryClass alloc] initWithCapacity: count]; + } + if ([_cInfo count] == 0) + { + while (count-- > 0) + { + info = GSIArrayItemAtIndex(_clsAry, count).obj; + [_cInfo setObject: info forKey: NSStringFromClass(info->class)]; + } + } + info = [_cInfo objectForKey: className]; + if (info != nil) + { + version = info->version; + } + return version; +} + +@end + + + +@implementation NSPortCoder (Private) + +- (NSMutableArray*) _components +{ + return _comp; +} + +@end + +@implementation NSPortCoder (Headers) + +- (void) _deserializeHeaderAt: (unsigned*)pos + version: (unsigned*)v + classes: (unsigned*)c + objects: (unsigned*)o + pointers: (unsigned*)p +{ + unsigned plen = strlen(PREFIX); + unsigned size = plen+36; + char header[size+1]; + + [_src getBytes: header range: NSMakeRange(*pos, size)]; + *pos += size; + header[size] = '\0'; + if (strncmp(header, PREFIX, plen) != 0) + { + [NSException raise: NSInternalInconsistencyException + format: @"Archive has wrong prefix"]; + } + if (sscanf(&header[plen], "%x:%x:%x:%x:", v, c, o, p) != 4) + { + [NSException raise: NSInternalInconsistencyException + format: @"Archive has wrong prefix"]; + } +} + +- (void) _serializeHeaderAt: (unsigned)locationInData + version: (unsigned)v + classes: (unsigned)cc + objects: (unsigned)oc + pointers: (unsigned)pc +{ + unsigned headerLength = strlen(PREFIX)+36; + char header[headerLength+1]; + unsigned dataLength = [_dst length]; + + sprintf(header, "%s%08x:%08x:%08x:%08x:", PREFIX, v, cc, oc, pc); + + if (locationInData + headerLength <= dataLength) + { + [_dst replaceBytesInRange: NSMakeRange(locationInData, headerLength) + withBytes: header]; + } + else if (locationInData == dataLength) + { + [_dst appendBytes: header length: headerLength]; + } + else + { + [NSException raise: NSInternalInconsistencyException + format: @"serializeHeader:at: bad location"]; + } +} + +@end + +#else /* Implementation of NSPortCoder object for remote messaging Copyright (C) 1997 Free Software Foundation, Inc. @@ -484,4 +2432,4 @@ static BOOL debug_connected_coder = NO; } @end - +#endif diff --git a/Source/NSPortNameServer.m b/Source/NSPortNameServer.m index ed604ec6a..8f3022054 100644 --- a/Source/NSPortNameServer.m +++ b/Source/NSPortNameServer.m @@ -517,8 +517,11 @@ typedef enum { #endif launchCmd = [NSString stringWithCString: make_gdomap_cmd(GNUSTEP_INSTALL_PREFIX)]; +#if GS_NEW_DO + portClass = [GSTcpPort class]; +#else portClass = [TcpOutPort class]; -// portClass = [GSTcpPort class]; +#endif } } diff --git a/Source/NSTimer.m b/Source/NSTimer.m index a7971b6c1..4facad861 100644 --- a/Source/NSTimer.m +++ b/Source/NSTimer.m @@ -141,7 +141,7 @@ static Class NSDate_class; NSLog(@"Missed %d timeouts at %f second intervals", inc, _interval); #endif RELEASE(_date); - _date = [[NSDate allocWithZone: [self zone]] + _date = [[NSDate_class allocWithZone: [self zone]] initWithTimeIntervalSinceReferenceDate: nxt]; } }