1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
|
/** Implementation for GNU Objective-C version of NSDistantObject
Copyright (C) 1997 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Based on code by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
Created: August 1997
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 Lesser 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110 USA.
<title>NSDistantObject class reference</title>
$Date$ $Revision$
*/
#import "common.h"
#define EXPOSE_NSDistantObject_IVARS 1
#import "GNUstepBase/DistributedObjects.h"
#import "GNUstepBase/GSObjCRuntime.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSLock.h"
#import "Foundation/NSPort.h"
#import "Foundation/NSMethodSignature.h"
#import "Foundation/NSException.h"
#import "Foundation/NSHashTable.h"
#import "Foundation/NSInvocation.h"
#include <objc/Protocol.h>
#import "GSInvocation.h"
@interface NSDistantObject(GNUstepExtensions)
- (Class) classForPortCoder;
- (void) finalize;
@end
#define DO_FORWARD_INVOCATION(_SELX, _ARG1) ({ \
sig = [self methodSignatureForSelector: @selector(_SELX)]; \
if (sig != nil) \
{ \
inv = [NSInvocation invocationWithMethodSignature: sig]; \
[inv setSelector: @selector(_SELX)]; \
[inv setTarget: self]; \
[inv setArgument: (void*)&_ARG1 atIndex: 2]; \
[self forwardInvocation: inv]; \
[inv getReturnValue: &m]; \
} \
else \
{ \
NSWarnLog(@"DO_FORWARD_INVOCATION failed, got nil signature for '%@'.", \
NSStringFromSelector(@selector(_SELX))); \
}})
static int debug_proxy = 0;
static Class placeHolder = 0;
static Class distantObjectClass = 0;
#ifndef __GNUSTEP_RUNTIME__
@interface Object (NSConformsToProtocolNamed)
- (BOOL) _conformsToProtocolNamed: (const char*)aName;
@end
#endif
@interface NSObject (NSConformsToProtocolNamed)
- (BOOL) _conformsToProtocolNamed: (const char*)aName;
@end
/*
* Evil hack ... if a remote system wants to know if we conform
* to a protocol we use a local protocol with the same name.
*/
#ifndef __GNUSTEP_RUNTIME__
@interface Object (conformsTo)
- (BOOL) conformsTo: (Protocol*)p;
@end
@implementation Object (NSConformsToProtocolNamed)
- (BOOL) _conformsToProtocolNamed: (const char*)aName
{
Protocol *p;
p = objc_getProtocol(aName);
return [(id)self conformsTo: p];
}
@end
#endif
@implementation NSObject (NSConformsToProtocolNamed)
- (BOOL) _conformsToProtocolNamed: (const char*)aName
{
Protocol *p;
p = objc_getProtocol(aName);
return [self conformsToProtocol: p];
}
@end
@interface NSConnection (DistantObjectHacks)
- (void) acquireProxyForTarget: (unsigned)target;
- (NSDistantObject*) retainOrAddLocal: (NSDistantObject*)aProxy
forObject: (id)anObject;
- (NSDistantObject*) retainOrAddProxy: (NSDistantObject*)aProxy
forTarget: (unsigned)aTarget;
- (void) removeProxy: (id)obj;
- (void) vendLocal: (NSDistantObject*)aProxy;
@end
/* This is the proxy tag; it indicates where the local object is,
and determines whether the reply port to the Connection-where-the-
proxy-is-local needs to encoded/decoded or not. */
enum proxyLocation
{
PROXY_LOCAL_FOR_RECEIVER = 0,
PROXY_LOCAL_FOR_SENDER,
PROXY_REMOTE_FOR_BOTH
};
/*
* The GSDistantObjectPlaceHolder class is simply used as a placeholder
* for an NSDistantObject so we can manage efficient allocation and
* initialisation - in most cases when we ask for an NSDistantObject
* instance, we will get a pre-existing one, so we don't want to go
* allocating the memory for a new instance unless absolutely necessary.
*/
GS_ROOT_CLASS @interface GSDistantObjectPlaceHolder
+ (id) initWithCoder: (NSCoder*)aCoder;
+ (id) initWithLocal: (id)anObject connection: (NSConnection*)aConnection;
+ (id) initWithTarget: (unsigned)target connection: (NSConnection*)aConnection;
+ (id) autorelease;
+ (void) release;
+ (id) retain;
+ (BOOL) respondsToSelector: (SEL)sel;
@end
@implementation GSDistantObjectPlaceHolder
+ (id) autorelease
{
return self;
}
+ (void) release
{
}
+ (id) retain
{
return self;
}
+ (void) initialize
{
if (self == objc_lookUpClass("GSDistantObjectPlaceHolder"))
{
distantObjectClass = [NSDistantObject class];
}
}
+ (BOOL) respondsToSelector: (SEL)sel
{
return class_getClassMethod(self, sel) != NULL;
}
+ (id) initWithCoder: (NSCoder*)aCoder
{
uint8_t proxy_tag;
unsigned target;
id decoder_connection;
NSDistantObject *o;
/*
if ([aCoder isKindOfClass: [NSPortCoder class]] == NO)
{
[NSException raise: NSGenericException
format: @"NSDistantObject objects only decode with "
@"NSPortCoder class"];
}
*/
decoder_connection = [(NSPortCoder*)aCoder connection];
NSAssert(decoder_connection, NSInternalInconsistencyException);
/* First get the tag, so we know what values need to be decoded. */
[aCoder decodeValueOfObjCType: @encode(__typeof__(proxy_tag))
at: &proxy_tag];
switch (proxy_tag)
{
case PROXY_LOCAL_FOR_RECEIVER:
/*
* This was a proxy on the other side of the connection, but
* here it's local.
* Lookup the target handle to ensure that it exists here.
* Return a retained copy of the local target object.
*/
[aCoder decodeValueOfObjCType: @encode(__typeof__(target))
at: &target];
if (debug_proxy)
NSLog(@"Receiving a proxy for local object 0x%x "
@"connection %p\n", target, decoder_connection);
o = [decoder_connection locateLocalTarget: target];
if (o == nil)
{
[NSException raise: @"ProxyDecodedBadTarget"
format: @"No local object with given target (0x%x)",
target];
}
else
{
if (debug_proxy)
{
NSLog(@"Local object is %p (%p)\n",
(void*)(uintptr_t)o, o->_object);
}
return RETAIN(o->_object);
}
case PROXY_LOCAL_FOR_SENDER:
/*
* This was a local object on the other side of the connection,
* but here it's a proxy object. Get the target address, and
* send [NSDistantObject +proxyWithTarget:connection:]; this will
* return the proxy object we already created for this target, or
* create a new proxy object if necessary.
*/
[aCoder decodeValueOfObjCType: @encode(__typeof__(target))
at: &target];
if (debug_proxy)
NSLog(@"Receiving a proxy, was local 0x%x connection %p\n",
target, decoder_connection);
o = [self initWithTarget: target
connection: decoder_connection];
return o;
case PROXY_REMOTE_FOR_BOTH:
/*
* This was a proxy on the other side of the connection, and it
* will be a proxy on this side too; that is, the local version
* of this object is not on this host, not on the host the
* NSPortCoder is connected to, but on a *third* host.
* This is why I call this a "triangle connection". In addition
* to decoding the target, we decode the NSPort object that we
* will use to talk directly to this third host. We send
* [NSConnection +connectionWithReceivePort:sendPort:]; this
* will either return the connection already created for this
* inPort/outPort pair, or create a new connection if necessary.
*/
{
NSConnection *proxy_connection;
NSPort *proxy_connection_out_port = nil;
unsigned intermediary;
/*
* There is an object on the intermediary host that is keeping
* that hosts proxy for the original object retained, thus
* ensuring that the original is not released. We create a
* proxy for that intermediate proxy. When we release this
* proxy, the intermediary will be free to release it's proxy
* and the original can then be released. Of course, by that
* time we will have obtained our own proxy for the original
* object ...
*/
[aCoder decodeValueOfObjCType: @encode(__typeof__(intermediary))
at: &intermediary];
AUTORELEASE([self initWithTarget: intermediary
connection: decoder_connection]);
/*
* Now we get the target number and port for the orignal object
* and (if necessary) get the originating process to retain the
* object for us.
*/
[aCoder decodeValueOfObjCType: @encode(__typeof__(target))
at: &target];
[aCoder decodeValueOfObjCType: @encode(id)
at: &proxy_connection_out_port];
NSAssert(proxy_connection_out_port, NSInternalInconsistencyException);
/*
# If there already exists a connection for talking to the
* out port, we use that one rather than creating a new one from
* our listening port.
*
* First we try for a connection from our receive port,
* Then we try any connection to the send port
* Finally we resort to creating a new connection - we don't
* release the newly created connection - it will get released
* automatically when no proxies are left on it.
*/
proxy_connection = [[decoder_connection class]
connectionWithReceivePort: [decoder_connection receivePort]
sendPort: proxy_connection_out_port];
if (debug_proxy)
NSLog(@"Receiving a triangle-connection proxy 0x%x "
@"connection %p\n", target, proxy_connection);
NSAssert(proxy_connection != decoder_connection,
NSInternalInconsistencyException);
NSAssert([proxy_connection isValid],
NSInternalInconsistencyException);
/*
* We may not already have a proxy for the object on the
* remote system, we must tell the connection to make sure
* the other end knows we are creating one.
*/
[proxy_connection acquireProxyForTarget: target];
/*
* Finally - we get a proxy via a direct connection to the
* originating server. We have also created a proxy to an
* intermediate object - but this proxy has not been retained
* and will therefore go away when the current autorelease
* pool is destroyed.
*/
o = [self initWithTarget: target
connection: proxy_connection];
return o;
}
default:
/* xxx This should be something different than NSGenericException. */
[NSException raise: NSGenericException
format: @"Bad proxy tag"];
}
/* Not reached. */
return nil;
}
+ (id) initWithLocal: (id)anObject connection: (NSConnection*)aConnection
{
NSDistantObject *proxy;
NSAssert([aConnection isValid], NSInternalInconsistencyException);
/*
* If there already is a local proxy for this target/connection
* combination, don't create a new one, just return the old one.
*/
proxy = [aConnection retainOrAddLocal: nil forObject: anObject];
if (proxy == nil)
{
proxy = (NSDistantObject*)NSAllocateObject(distantObjectClass,
0, NSDefaultMallocZone());
proxy = [proxy initWithLocal: anObject connection: aConnection];
}
return proxy;
}
+ (id) initWithTarget: (unsigned)target connection: (NSConnection*)aConnection
{
NSDistantObject *proxy;
NSAssert([aConnection isValid], NSInternalInconsistencyException);
/*
* If there already is a local proxy for this target/connection
* combination, don't create a new one, just return the old one.
*/
proxy = [aConnection retainOrAddProxy: nil forTarget: target];
if (proxy == nil)
{
proxy = (NSDistantObject*)NSAllocateObject(distantObjectClass,
0, NSDefaultMallocZone());
proxy = [proxy initWithTarget: target connection: aConnection];
}
return proxy;
}
@end
@interface NSDistantObject (Debug)
+ (int) setDebug: (int)val;
@end
@implementation NSDistantObject (Debug)
+ (int) setDebug: (int)val
{
int old = debug_proxy;
debug_proxy = val;
return old;
}
@end
/**
* Instances of this class act as proxies to remote objects using
* the Distributed Objects system. They also hold references to
* local objects which are vended to remote processes.
*/
@implementation NSDistantObject
+ (void) initialize
{
if (self == [NSDistantObject class])
{
placeHolder = objc_lookUpClass("GSDistantObjectPlaceHolder");
}
}
+ (id) allocWithZone: (NSZone*)z
{
return (NSDistantObject*)placeHolder;
}
/**
* Creates and returns a proxy associated with aConnection
* which will hold a reference to the local object anObject.
*/
+ (NSDistantObject*) proxyWithLocal: (id)anObject
connection: (NSConnection*)aConnection
{
return AUTORELEASE([placeHolder initWithLocal: anObject
connection: aConnection]);
}
/**
* Creates and returns a proxy associated with aConnection
* which will provide a link to a remote object whose reference
* locally is anObject.
*/
+ (NSDistantObject*) proxyWithTarget: (unsigned)anObject
connection: (NSConnection*)aConnection
{
return AUTORELEASE([placeHolder initWithTarget: anObject
connection: aConnection]);
}
/**
* Returns the [NSConnection] instance with which the receiver is
* associated.
*/
- (NSConnection*) connectionForProxy
{
return _connection;
}
/**
* NSProxy subclasses must override -init or an exception will be thrown. This
* calls the forwarding mechanism to invoke -init on the remote object.
*/
- (id) init
{
NSMethodSignature *sig;
NSInvocation *inv;
id ret;
sig = [self methodSignatureForSelector: _cmd];
inv = [NSInvocation invocationWithMethodSignature: sig];
[inv setTarget: self];
[inv setSelector: _cmd];
[self forwardInvocation: inv];
[inv getReturnValue: &ret];
return ret;
}
- (void) dealloc
{
[self finalize];
if (_sigs != 0) [(NSMutableDictionary*)_sigs release];
[super dealloc];
}
- (void) encodeWithCoder: (NSCoder*)aRmc
{
unsigned proxy_target;
uint8_t proxy_tag;
NSConnection *encoder_connection;
/*
if ([aRmc isKindOfClass: [NSPortCoder class]] == NO)
{
[NSException raise: NSGenericException
format: @"NSDistantObject objects only "
@"encode with NSPortCoder class"];
}
*/
encoder_connection = [(NSPortCoder*)aRmc connection];
NSAssert(encoder_connection, NSInternalInconsistencyException);
if (![encoder_connection isValid])
[NSException
raise: NSGenericException
format: @"Trying to encode to an invalid Connection.\n"
@"You should request NSConnectionDidDieNotification's and\n"
@"release all references to the proxy's of invalid Connections."];
proxy_target = _handle;
if (encoder_connection == _connection)
{
if (_object)
{
/*
* This proxy is a local to us, remote to other side.
*/
proxy_tag = PROXY_LOCAL_FOR_SENDER;
if (debug_proxy)
NSLog(@"Sending a proxy, will be remote 0x%x connection %p\n",
proxy_target, _connection);
[aRmc encodeValueOfObjCType: @encode(__typeof__(proxy_tag))
at: &proxy_tag];
[aRmc encodeValueOfObjCType: @encode(__typeof__(proxy_target))
at: &proxy_target];
/*
* Tell connection this object is being vended.
*/
[_connection vendLocal: self];
}
else
{
/*
* This proxy is a local object on the other side.
*/
proxy_tag = PROXY_LOCAL_FOR_RECEIVER;
if (debug_proxy)
NSLog(@"Sending a proxy, will be local 0x%x connection %p\n",
proxy_target, _connection);
[aRmc encodeValueOfObjCType: @encode(__typeof__(proxy_tag))
at: &proxy_tag];
[aRmc encodeValueOfObjCType: @encode(__typeof__(proxy_target))
at: &proxy_target];
}
}
else
{
/*
* This proxy will still be remote on the other side
*/
NSPort *proxy_connection_out_port = [_connection sendPort];
NSDistantObject *localProxy;
NSAssert(proxy_connection_out_port,
NSInternalInconsistencyException);
NSAssert([proxy_connection_out_port isValid],
NSInternalInconsistencyException);
NSAssert(proxy_connection_out_port != [encoder_connection sendPort],
NSInternalInconsistencyException);
proxy_tag = PROXY_REMOTE_FOR_BOTH;
/*
* Get a proxy to refer to self - we send this to the other
* side so we will be retained until the other side has
* obtained a proxy to the original object via a connection
* to the original vendor.
*/
localProxy = [NSDistantObject proxyWithLocal: self
connection: encoder_connection];
if (debug_proxy)
NSLog(@"Sending triangle-connection proxy 0x%x "
@"proxy-conn %p to-proxy 0x%x to-conn %p\n",
localProxy->_handle, localProxy->_connection,
proxy_target, _connection);
/*
* It's remote here, so we need to tell other side where to form
* triangle connection to
*/
[aRmc encodeValueOfObjCType: @encode(__typeof__(proxy_tag))
at: &proxy_tag];
[aRmc encodeValueOfObjCType: @encode(__typeof__(localProxy->_handle))
at: &localProxy->_handle];
[aRmc encodeValueOfObjCType: @encode(__typeof__(proxy_target))
at: &proxy_target];
[aRmc encodeBycopyObject: proxy_connection_out_port];
/*
* Tell connection that localProxy is being vended.
*/
[encoder_connection vendLocal: localProxy];
}
}
/*
* This method needs to be implemented to actually do anything.
*/
- (void) forwardInvocation: (NSInvocation*)anInvocation
{
if (debug_proxy)
NSLog(@"NSDistantObject forwardInvocation: %@\n", anInvocation);
if (![_connection isValid])
[NSException
raise: NSGenericException
format: @"Trying to send message to an invalid Proxy.\n"
@"You should request NSConnectionDidDieNotification's and\n"
@"release all references to the proxy's of invalid Connections."];
/* We could be released while the connection is forwarding, so we need
* to retain self. But, the remote end couild raise an exception, so
* we can't rely on being able to release again; use autorelease.
*/
AUTORELEASE(RETAIN(self));
[_connection forwardInvocation: anInvocation forProxy: self];
}
- (id) initWithCoder: (NSCoder*)aCoder
{
DESTROY(self);
[NSException raise: NSGenericException
format: @"NSDistantObject decodes from placeholder"];
return nil;
}
/**
* Initialises and returns a proxy associated with aConnection
* which will hold a reference to the local object anObject.
*/
- (id) initWithLocal: (id)anObject connection: (NSConnection*)aConnection
{
NSAssert([aConnection isValid], NSInternalInconsistencyException);
_object = RETAIN(anObject);
_handle = 0;
_connection = RETAIN(aConnection);
self = [_connection retainOrAddLocal: self forObject: anObject];
if (debug_proxy)
NSLog(@"Created new local=%p object %p target 0x%x connection %p\n",
self, _object, _handle, _connection);
return self;
}
/**
* Initialises and returns a proxy associated with aConnection
* which will provide a link to a remote object whose reference
* locally is target.
*/
- (id) initWithTarget: (unsigned)target connection: (NSConnection*)aConnection
{
NSAssert([aConnection isValid], NSInternalInconsistencyException);
_object = nil;
_handle = target;
_connection = RETAIN(aConnection);
/*
* We register this object with the connection using it.
* Conceivably this could result in self being changed.
*/
self = [_connection retainOrAddProxy: self forTarget: target];
if (debug_proxy)
NSLog(@"Created new proxy=%p target 0x%x connection %p\n",
self, _handle, _connection);
return self;
}
/**
* <p>Returns the method signature describing the arguments and return
* types of the method in the object referred to by the receiver
* which implements the aSelector message.
* </p>
* <p>This method may need to refer to another process (causing relatively
* slow network communication) and approximately double the time taken for
* sending a distributed objects message, so you are advised to use the
* -setProtocolForProxy: method to avoid this occurring.
* </p>
*/
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
{
if (0 == aSelector)
{
return nil;
}
if (_object != nil)
{
return [_object methodSignatureForSelector: aSelector];
}
else
{
/*
* Evil hack to prevent recursion - if we are asking a remote
* object for a method signature, we can't ask it for the
* signature of methodSignatureForSelector:, so we hack in
* the signature required manually :-(
*/
if (sel_isEqual(aSelector, _cmd))
{
static NSMethodSignature *sig = nil;
if (sig == nil)
{
sig = RETAIN([NSMethodSignature signatureWithObjCTypes: "@@::"]);
}
return sig;
}
if (sel_isEqual(aSelector, @selector(methodType)))
{
static NSMethodSignature *sig = nil;
if (sig == nil)
{
sig = RETAIN([NSMethodSignature signatureWithObjCTypes: "r*@:"]);
}
return sig;
}
if (_protocol != nil)
{
struct objc_method_description mth;
mth = GSProtocolGetMethodDescriptionRecursive(_protocol, aSelector, YES, YES);
if (mth.name == NULL && mth.types == NULL)
{
// Search for class method
mth = GSProtocolGetMethodDescriptionRecursive(_protocol, aSelector, YES, NO);
}
if (mth.types)
return [NSMethodSignature signatureWithObjCTypes: mth.types];
}
if (_sigs != 0)
{
NSMutableDictionary *d = (NSMutableDictionary*)_sigs;
NSString *s = NSStringFromSelector(aSelector);
NSMethodSignature *m = [d objectForKey: s];
if (m != nil) return m;
}
{
id m = nil;
id inv;
id sig;
DO_FORWARD_INVOCATION(methodSignatureForSelector:, aSelector);
if ([m isProxy] == YES)
{
const char *types;
types = [m methodType];
/* Create a local method signature.
*/
m = [NSMethodSignature signatureWithObjCTypes: types];
}
if (m != nil)
{
NSMutableDictionary *d = (NSMutableDictionary*)_sigs;
NSString *s = NSStringFromSelector(aSelector);
if (d == nil)
{
d = [NSMutableDictionary new];
_sigs = (void*)d;
}
[d setObject: m forKey: s];
}
return m;
}
}
}
/**
* <p>A key method for Distributed Objects performance. This sets the
* a protocol that the distant object referred to by the proxy should
* conform to. When messages in that protocol are sent to the proxy,
* the proxy knows that it does not need to ask the remote object for
* the method signature in order to send the message to it, but can
* send the message straight away based on the local method signature
* information obtained from the protocol.
* </p>
* <example>
* if ([anObj isProxy] == YES)
* {
* [anObj setProtocolForProxy: @protocol(MyProtocol)];
* }
* </example>
* <p>It is <em>highly recommended</em> that you make use of this facility,
* but you must beware that versions of the compiler prior to 3.3 suffer a
* serious bug with respect to the @protocol directive. If the protocol
* referred to is not declared and implemented in the file where @protocol
* is used to refer to the protocol by name, a runtime error will occur
* when you try to use it.
* </p>
* <p>Beware, if you don't use this method to set the protocol, the system
* might well ask the remote process for method signature information, and
* the remote process might get it <em>wrong</em>. This is because the
* class of the remote object needs to have been declared to conform to the
* protocol in order for it to know about any protocol qualifiers (the
* keywords <code>bycopy, byref, in, out, inout,</code> and
* <code>oneway</code>). If the author of the server process forgot to do
* this, the type information returned from that process may not be what
* you are expecting.
* </p>
* The class of the server object should be declared like this ...
* <example>
* @interface MyServerClass : NSObject <MyProtocol>
* ...
* @end
* </example>
*/
- (void) setProtocolForProxy: (Protocol*)aProtocol
{
_protocol = aProtocol;
}
@end
/**
* Adds some backwards-compatibility and other methods.
*/
@implementation NSDistantObject(GNUstepExtensions)
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0;
}
/**
* Used by the garbage collection system to tidy up when a proxy is destroyed.
*/
- (void) finalize
{
if (_connection)
{
if (debug_proxy > 3)
NSLog(@"retain count for connection (%p) is now %lx\n",
_connection, [_connection retainCount]);
/*
* A proxy for local object retains its target - so we release it.
* For a local object the connection also retains this proxy, so we
* can't be deallocated unless we are already removed from the
* connection, and there is no need to remove self from connection.
*
* A proxy has a nil local object, and retains it's connection so
* that the connection will continue to exist as long as there is
* a something to use it.
* So we release our reference to the connection here just as soon
* as we have removed ourself from the connection.
*/
if (_object == nil)
[_connection removeProxy: self];
else
DESTROY(_object);
RELEASE(_connection);
}
}
- (Class) classForCoder
{
return object_getClass(self);
}
/**
* Returns the class of the receiver.
*/
- (Class) classForPortCoder
{
return object_getClass(self);
}
/**
* If a protocol has been set for the receiver, this method tests to
* see that the set protocol conforms to aProtocol. Otherwise, the
* remote object is checked to see whether it conforms to aProtocol.
*/
- (BOOL) conformsToProtocol: (Protocol*)aProtocol
{
if (_protocol != nil)
{
return protocol_conformsToProtocol(_protocol, aProtocol);
}
else
{
return [(id)self _conformsToProtocolNamed: protocol_getName(aProtocol)];
}
}
- (BOOL) respondsToSelector: (SEL)aSelector
{
BOOL m = NO;
id inv;
id sig;
DO_FORWARD_INVOCATION(respondsToSelector:, aSelector);
return m;
}
- (id) replacementObjectForCoder: (NSCoder*)aCoder
{
return self;
}
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
{
return self;
}
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
{
if (0 == NSHashGet(exclude, self))
{
Class c = object_getClass(self);
NSUInteger size = class_getInstanceSize(c);
return size;
}
return 0;
}
@end
@implementation Protocol (DistributedObjectsCoding)
- (Class) classForPortCoder
{
return object_getClass(self);
}
- (id) replacementObjectForPortCoder: (NSPortCoder*)aRmc;
{
if ([aRmc isBycopy])
return self;
else
return [NSDistantObject proxyWithLocal: self
connection: [aRmc connection]];
}
@end
|