diff --git a/Source/Invocation.m b/Source/Invocation.m index 23cfe2b56..56e8f4424 100644 --- a/Source/Invocation.m +++ b/Source/Invocation.m @@ -1,5 +1,5 @@ /* Implementation for Objective-C Invocation object - Copyright (C) 1993,1994, 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. Written by: R. Andrew McCallum Date: May 1993 @@ -23,6 +23,10 @@ #include #include +#include +#include +#include +#include /* Deal with strrchr: */ #if STDC_HEADERS || HAVE_STRING_H @@ -41,30 +45,20 @@ #endif /* not STDC_HEADERS and not HAVE_STRING_H */ -/* - Put something like this in Collecting: - - withObjectsAtArgumentIndex: (unsigned)index - invoke: (Invocation*)invocation; - - putObjectsAtArgumentIndex: (unsigned)index - andInvoke: (Invocation*)invocation; - - invoke: (Invocation*)invocation - withObjectsAtArgumentIndex: (unsigned)index -*/ - @implementation Invocation - initWithReturnType: (const char *)enc { int l = strlen(enc); - OBJC_MALLOC(encoding, char, l + 1); - memcpy(encoding, enc, l); - encoding[l] = '\0'; - enc = objc_skip_type_qualifiers (encoding); + OBJC_MALLOC(return_type, char, l + 1); + memcpy(return_type, enc, l); + return_type[l] = '\0'; + enc = objc_skip_type_qualifiers (return_type); if (*enc != 'v') { /* Work around bug in objc_sizeof_type; it doesn't handle void type */ return_size = objc_sizeof_type (enc); - return_value = (*objc_malloc) (return_size); + return_value = (*objc_calloc) (1, return_size); } else { @@ -73,6 +67,50 @@ } return self; } + +- (void) encodeWithCoder: (id )coder +{ + [super encodeWithCoder: coder]; + [coder encodeValueOfCType: @encode(char*) + at: &return_type + withName: @"Invocation return type"]; + [coder encodeValueOfCType: @encode(unsigned) + at: &return_size + withName: @"Invocation return size"]; + if (return_size) + [coder encodeValueOfObjCType: return_type + at: return_value + withName: @"Invocation return value"]; +} + +- initWithCoder: (id )coder +{ + self = [super initWithCoder: coder]; + [coder decodeValueOfCType: @encode(char*) + at: &return_type + withName: NULL]; + [coder decodeValueOfCType: @encode(unsigned) + at: &return_size + withName: NULL]; + if (return_size) + { + return_value = (*objc_malloc) (return_size); + [coder decodeValueOfObjCType: return_type + at: return_value + withName: NULL]; + } + else + return_value = 0; + return self; +} + +- (Class) classForConnectedCoder: coder +{ + /* Make sure that Connection's always send us bycopy, + i.e. as our own class, not a Proxy class. */ + return [self class]; +} + - (void) invoke { [self subclassResponsibility:_cmd]; @@ -85,7 +123,7 @@ - (const char *) returnType { - return encoding; + return return_type; } - (unsigned) returnSize @@ -101,15 +139,71 @@ a return value yet. */ } +- (void) setReturnValue: (void*)addr +{ + if (return_value) + memcpy (return_value, addr, return_size); + /* xxx retain, if necessary. */ +} + - objectReturnValue { + switch (*return_type) + { +#define CASE_RETURN(C,T,S) \ + case C: return [NSNumber numberWith ## S: *(T*)return_value] + CASE_RETURN (_C_LNG, long, Long); + CASE_RETURN (_C_ULNG, unsigned long, UnsignedLong); + CASE_RETURN (_C_INT, int, Int); + CASE_RETURN (_C_UINT, unsigned int, UnsignedInt); + CASE_RETURN (_C_SHT, short, Short); + CASE_RETURN (_C_USHT, unsigned short, UnsignedShort); + CASE_RETURN (_C_CHR, char, Char); + CASE_RETURN (_C_UCHR, unsigned char, UnsignedChar); + CASE_RETURN (_C_FLT, float, Float); + CASE_RETURN (_C_DBL, double, Double); +#undef CASE_RETURN + case _C_PTR: + return [NSNumber numberWithUnsignedLong: (long) *(void**)return_value]; + case _C_CHARPTR: + return [NSString stringWithCString: *(char**)return_value]; + case _C_ID: + return *(id*)return_value; + case 'v': + return nil; + default: + [self notImplemented: _cmd]; + } + return 0; [self notImplemented: _cmd]; return nil; } - (int) intReturnValue { - [self notImplemented: _cmd]; + switch (*return_type) + { +#define CASE_RETURN(_C,_T) case _C: return (int) *(_T*)return_value + CASE_RETURN (_C_LNG, long); + CASE_RETURN (_C_ULNG, unsigned long); + CASE_RETURN (_C_INT, int); + CASE_RETURN (_C_UINT, unsigned int); + CASE_RETURN (_C_SHT, short); + CASE_RETURN (_C_USHT, unsigned short); + CASE_RETURN (_C_CHR, char); + CASE_RETURN (_C_UCHR, unsigned char); + CASE_RETURN (_C_CHARPTR, char*); + CASE_RETURN (_C_FLT, float); + CASE_RETURN (_C_DBL, double); + CASE_RETURN (_C_PTR, void*); +#undef CASE_RETURN + case _C_ID: + return [*(id*)return_value intValue]; + case 'v': + return 0; + default: + [self notImplemented: _cmd]; + } return 0; } @@ -121,27 +215,29 @@ return (*(char*)return_value != 0); case sizeof(short): return (*(short*)return_value != 0); - case sizeof(long): - return (*(long*)return_value != 0); + case sizeof(int): + return (*(int*)return_value != 0); } - [self notImplemented: _cmd]; + { + int i; + for (i = 0; i < return_size; i++) + if (*((char*)return_value + i) != 0) + return YES; + return NO; + } } - (void) dealloc { - OBJC_FREE(encoding); + if (*return_type == _C_ID) + [*(id*)return_value release]; + OBJC_FREE(return_type); [super dealloc]; } @end -#if 0 -@implementation CurriedInvocation - -@end -#endif - static int types_get_size_of_stack_arguments(const char *types) { @@ -185,20 +281,32 @@ my_method_get_next_argument (arglist_t argframe, @implementation ArgframeInvocation -/* This is the designated initializer. */ -- initWithArgframe: (arglist_t)frame type: (const char *)type +- (void) _retainArguments +{ + const char *tmptype; + + tmptype = return_type; + while ((datum = my_method_get_next_argument (argframe, &tmptype))) + { + tmptype = objc_skip_type_qualifiers (tmptype); + if (*tmptype == _C_ID) + [*(id*)datum retain]; + } +} + +- (void) _initArgframeFrom: (arglist_t)frame + withType: (const char*)type + retainArgs: (BOOL)f { int stack_argsize, reg_argsize; - - /* xxx we could just use the return part. Does this matter? */ - [super initWithReturnType:type]; + void *datum; /* allocate the argframe */ - stack_argsize = types_get_size_of_stack_arguments(type); + stack_argsize = types_get_size_of_stack_arguments (type); reg_argsize = types_get_size_of_register_arguments(type); - argframe = (arglist_t) (*objc_calloc)(1 ,sizeof(char*) + reg_argsize); + argframe = (arglist_t) (*objc_calloc) (1 ,sizeof(char*) + reg_argsize); if (stack_argsize) - argframe->arg_ptr = (*objc_calloc)(1, stack_argsize); + argframe->arg_ptr = (*objc_calloc) (1, stack_argsize); else argframe->arg_ptr = 0; @@ -209,8 +317,50 @@ my_method_get_next_argument (arglist_t argframe, (char*)frame + sizeof(char*), reg_argsize); memcpy(argframe->arg_ptr, frame->arg_ptr, stack_argsize); + if (f) + [self _retainArguments]; } +} +/* This is the designated initializer. */ +- initWithArgframe: (arglist_t)frame type: (const char *)type +{ + /* xxx we are just using the return part. Does this matter? */ + [super initWithReturnType: type]; + [self _initArgframeFrom: frame withType: type retainArgs: NO]; + + return self; +} + +- (void) encodeWithCoder: (id )coder +{ + const char *tmptype; + void *datum; + + [super encodeWithCoder: coder]; + tmptype = return_type; + while ((datum = my_method_get_next_argument(argframe, &tmptype))) + { + [coder encodeValueOfObjCType: tmptype + at: datum + withName: @"Invocation Argframe argument"]; + } +} + +- initWithCoder: (id )coder +{ + const char *tmptype; + void *datum; + + self = [super initWithCoder: coder]; + [self _initArgframeFrom: NULL withType: return_type retainArgs: NO]; + tmptype = return_type; + while ((datum = my_method_get_next_argument(argframe, &tmptype))) + { + [coder decodeValueOfObjCType: tmptype + at: datum + withName: NULL]; + } return self; } @@ -220,13 +370,28 @@ my_method_get_next_argument (arglist_t argframe, return self; } +- (void) retainArguments +{ + if (!args_retained) + { + if (argframe) + [self _retainArguments]; + args_retained = YES; + } +} + +- (BOOL) argumentsRetained +{ + return args_retained; +} + - (const char *) argumentTypeAtIndex: (unsigned)i { - const char *tmptype = encoding; + const char *tmptype = return_type; do { - tmptype = objc_skip_argspec(objc_skip_typespec(tmptype)); + tmptype = objc_skip_argspec (tmptype); } while (i--); return tmptype; @@ -239,19 +404,20 @@ my_method_get_next_argument (arglist_t argframe, - (void) getArgument: (void*)addr atIndex: (unsigned)i { - const char *tmptype = encoding; + const char *tmptype = return_type; void *datum; do datum = my_method_get_next_argument(argframe, &tmptype); - while (i--); + while (i-- && datum); + /* xxx Give error msg for null datum */ memcpy (addr, datum, objc_sizeof_type(tmptype)); } - (void) setArgumentAtIndex: (unsigned)i toValueAt: (const void*)addr { - const char *tmptype = encoding; + const char *tmptype = return_type; void *datum; do @@ -260,23 +426,72 @@ my_method_get_next_argument (arglist_t argframe, memcpy (datum, addr, objc_sizeof_type(tmptype)); } +- (void) _deallocArgframe +{ + if (argframe) + { + if (argframe->arg_ptr) + (*objc_free) (argframe->arg_ptr); + (*objc_free) (argframe); + } +} + +- (void) dealloc +{ + void *datum; + const char *tmptype = return_type; + while ((datum = my_method_get_next_argument(argframe, &tmptype))) + { + tmptype = objc_skip_type_qualifiers (tmptype); + if (*tmptype == _C_ID) + [*(id*)datum release]; + } + [self _deallocArgframe]; + [super dealloc]; +} + +#if 0 +- resetArgframeWithReturnType: (const char*)encoding +{ + [self _deallocArgframe]; + [self _allocArgframe]; +} +#endif + @end @implementation MethodInvocation +- (void) _initTargetAndSelPointers +{ + const char *tmptype = return_type; + target_pointer = (id*) my_method_get_next_argument (argframe, &tmptype); + sel_pointer = (SEL*) my_method_get_next_argument (argframe, &tmptype); +} + +/* This is the designated initializer */ - initWithArgframe: (arglist_t)frame selector: (SEL)sel { const char *sel_type; + if (! (sel_type = sel_get_type (sel)) ) sel_type = sel_get_type ( sel_get_any_typed_uid (sel_get_name (sel))); [self initWithArgframe: frame type: sel_type]; + [self _initTargetAndSelPointers]; + return self; +} + +- initWithCoder: (id )coder +{ + self = [super initWithCoder: coder]; + [self _initTargetAndSelPointers]; return self; } - initWithSelector: (SEL)s { [self initWithArgframe: NULL selector: s]; - [self setArgumentAtIndex: 1 toValueAt: &s]; + *sel_pointer = s; return self; } @@ -287,9 +502,11 @@ my_method_get_next_argument (arglist_t argframe, void *arg_datum; va_list ap; - [self initWithSelector:s]; - tmptype = encoding; + [self initWithArgframe: NULL selector: s]; + tmptype = return_type; datum = my_method_get_next_argument(argframe, &tmptype); + if (args_retained) + [target retain]; *((id*)datum) = target; datum = my_method_get_next_argument(argframe, &tmptype); *((SEL*)datum) = s; @@ -300,7 +517,13 @@ my_method_get_next_argument (arglist_t argframe, #define CASE_TYPE(_C,_T) case _C: *(_T*)datum = va_arg (ap, _T); break switch (*tmptype) { - CASE_TYPE(_C_ID, id); + case _C_ID: + *(id*)datum = va_arg (ap, id); + if (args_retained) + [*(id*)datum retain]; + break; + + CASE_TYPE(_C_SEL, SEL); CASE_TYPE(_C_LNG, long); CASE_TYPE(_C_ULNG, unsigned long); CASE_TYPE(_C_INT, int); @@ -309,7 +532,10 @@ my_method_get_next_argument (arglist_t argframe, CASE_TYPE(_C_USHT, unsigned short); CASE_TYPE(_C_CHR, char); CASE_TYPE(_C_UCHR, unsigned char); + CASE_TYPE(_C_FLT, float); + CASE_TYPE(_C_DBL, double); CASE_TYPE(_C_CHARPTR, char*); + CASE_TYPE(_C_PTR, void*); default: [self notImplemented: _cmd]; // memcpy (datum, va_arg (ap, void*), objc_sizeof_type(tmptype)); @@ -327,35 +553,53 @@ my_method_get_next_argument (arglist_t argframe, id target; id cl; SEL sel; + char *tmp_type = return_type; /* xxx This could be more efficient by using my_method_get_next_argument instead of -target and -selector. Or, even better, caching the memory offsets of the target and selector in the argframe. */ - target = [self target]; + target = *target_pointer; if (target == nil) return; cl = object_get_class (target); - sel = [self selector]; + sel = *sel_pointer; + /* xxx Perhaps we could speed things up by making this an ivar, + and caching it. */ imp = get_imp (cl, sel); assert(imp); ret = __builtin_apply((void(*)(void))imp, argframe, - types_get_size_of_stack_arguments(encoding)); - if (return_value) + types_get_size_of_stack_arguments(return_type)); + if (return_size) { - if (*encoding == 'd') + if (*return_type == _C_DBL) + /* DBL's are stored in a different place relative to RET. */ memcpy(return_value, (char*)ret + 2*sizeof(void*), return_size); + else if (*return_type == _C_ID) + { + if (*(id*)return_value != *(id*)ret) + { + if (return_retained) + { + if (*(id*)return_value) + [*(id*)return_value release]; + [*(id*)ret retain]; + } + *(id*)return_value = *(id*)ret; + } + } else - memcpy(return_value, ret, return_size); + { + memcpy(return_value, ret, return_size); + } } } - (void) invokeWithTarget: t { - /* xxx Could be more efficient. */ - [self setArgumentAtIndex:0 toValueAt:&t]; + [self setTarget: t]; [self invoke]; } @@ -366,35 +610,90 @@ my_method_get_next_argument (arglist_t argframe, - (SEL) selector { - SEL s; - [self getArgument:&s atIndex:1]; - return s; + return *sel_pointer; } - (void) setSelector: (SEL)s { - [self setArgumentAtIndex:1 toValueAt:&s]; if (sel_types_match(sel_get_type([self selector]), sel_get_type(s))) - [self setArgumentAtIndex:1 toValueAt:&s]; + *sel_pointer = s; else { + /* We need to reallocate the argframe */ [self notImplemented:_cmd]; - /* We will need to reallocate the argframe */ } } - target { - id t; - [self getArgument:&t atIndex:0]; - return t; + return *target_pointer; } - (void) setTarget: t { - [self setArgumentAtIndex:0 toValueAt:&t]; + if (*target_pointer != t) + { + if (args_retained) + { + [*target_pointer release]; + [t retain]; + } + *target_pointer = t; + } } +@end + +@implementation ObjectMethodInvocation + +- (void) _initArgObjectPointer +{ + const char *tmptype; + void *datum; + + tmptype = return_type; + my_method_get_next_argument (argframe, &tmptype); + my_method_get_next_argument (argframe, &tmptype); + do + { + datum = my_method_get_next_argument (argframe, &tmptype); + tmptype = objc_skip_type_qualifiers (tmptype); + } + while (datum && tmptype && *tmptype != _C_ID); + if (*tmptype != _C_ID) + [self error: "This method does not have an object argument."]; + arg_object_pointer = (id*) datum; +} + +- initWithArgframe: (arglist_t)frame selector: (SEL)sel +{ + [super initWithArgframe: frame selector: sel]; + [self _initArgObjectPointer]; + return self; +} + +- initWithCoder: (id )coder +{ + self = [super initWithCoder: coder]; + [self _initArgObjectPointer]; + return self; +} + +- (void) invokeWithObject: anObject +{ + if (*arg_object_pointer != anObject) + { + if (args_retained) + { + [*arg_object_pointer release]; + [anObject retain]; + } + *arg_object_pointer = anObject; + } + [self invoke]; +} + + @end @implementation VoidFunctionInvocation @@ -409,19 +708,88 @@ my_method_get_next_argument (arglist_t argframe, } #endif -- initWithFunction: (void(*)())f +- initWithVoidFunction: (void(*)())f { [super initWithReturnType: "v"]; function = f; + return self; +} + +/* Encode ourself as a proxies across Connection's; we can't encode + a function across the wire. */ +- classForConnectedCoder: coder +{ + return [[coder connection] proxyClass]; +} + +- (void) encodeWithCoder: (id )coder +{ + [self shouldNotImplement: _cmd]; +} + +- (void) invoke +{ + (*function) (); +} + +- (void) invokeWithObject +{ + [self shouldNotImplement: _cmd]; } @end +@implementation ObjectFunctionInvocation + +- initWithObjectFunction: (id(*)(id))f +{ + [super initWithReturnType: "@"]; + function = f; + return self; +} + +/* Encode ourself as a proxies across Connection's; we can't encode + a function across the wire. */ +- classForConnectedCoder: coder +{ + return [[coder connection] proxyClass]; +} + +- (void) encodeWithCoder: (id )coder +{ + [self shouldNotImplement: _cmd]; +} + +- (void) invoke +{ + [self invokeWithObject: nil]; +} + +- (void) invokeWithObject: anObject +{ + id r; + + r = (*function) (anObject); + if (*(id*)return_value != r) + { + if (args_retained) + { + [*(id*)return_value release]; + [r retain]; + } + *(id*)return_value = r; + } +} + +@end /* Many other kinds of Invocations are possible: SchemeInvocation, TclInvocation */ #if 0 +@implementation CurriedInvocation +@end + What is this nonsense? @interface StreamInvocation @interface LogInvocation