/*
   mframe.m

 -- CHECKME: what is really still used?
 -- CHECKME: why don't we use method_get_next_argument (arg_frame, &type)) from libobjc?
 
   Implementation of functions for dissecting/making method calls
 
   These functions can be used for dissecting and making method calls
   for many different situations.  They are used for distributed objects.

   Copyright (C) 1994, 1996, 1998 Free Software Foundation, Inc.
   
   Author:  Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
   Date:	Oct 1994
   
   This file is part of the mySTEP Library and is provided
   under the terms of the GNU Library General Public License.

*/ 

#include "mframe.h"

#import "NSPrivate.h"
#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSData.h>
#import <Foundation/NSException.h>

#if 1

#include <Foundation/NSInvocation.h>
#ifndef __APPLE__
// #include <avcall.h>
#endif

typedef struct _NSInvocation_t {
	@defs(NSInvocation)
} NSInvocation_t;

typedef struct _callframe_t {
	int nargs;
	void *rtype;
	void **args;
} callframe_t;

callframe_t *
callframe_from_info (NSArgumentInfo *info, int numargs, void **retval)
{
unsigned size = sizeof(callframe_t);
unsigned align = __alignof(double);
unsigned offset = 0;
callframe_t *cframe;
void *buf;
int i;

	if (numargs > 0)
		{
		if (size % align != 0)
			size += align - (size % align);

		offset = size;
		size += numargs * sizeof(void*);
		if (size % align != 0)
			size += (align - (size % align));

		for (i = 0; i < numargs; i++)
			{
			size += info[i+1].size;
		
			if (size % align != 0)
				size += (align - size % align);
			}
		}

	if (retval)					// If we need space allocated to store a return 
		{						// value, make room for it at the end of the
		unsigned full = size;	// callframe in order to do a single malloc.
		unsigned pos;

		if (full % align != 0)
			full += (align - full % align);

		pos = full;
		full += MAX(info[0].size, sizeof(smallret_t));
		cframe = buf = objc_calloc(full, 1);
		if (cframe)
			*retval = (char *) buf + pos;
		}
	else
		cframe = buf = objc_calloc(size, 1);

	if (cframe)
		{
		cframe->nargs = numargs;
		cframe->args = (void *) ((char *) buf + offset);
		offset += numargs * sizeof(void*);
		if (offset % align != 0)
			offset += align - (offset % align);

		for (i = 0; i < cframe->nargs; i++)
			{
			cframe->args[i] = (char *) buf + offset;
		
			offset += info[i+1].size;
		
			if (offset % align != 0)
				offset += (align - offset % align);
			}
		}

	return cframe;
}

void
callframe_set_arg(callframe_t *cframe, int index, void *buffer, int size)
{
	if (index >= 0 && index < cframe->nargs)
		memcpy(cframe->args[index], buffer, size);
}

void
callframe_get_arg(callframe_t *cframe, int index, void *buffer, int size)
{
	if (index >= 0 && index < cframe->nargs)
		memcpy(buffer, cframe->args[index], size);
}

void *
callframe_arg_addr(callframe_t *cframe, int index)
{
	return (index < 0 || index >= cframe->nargs) ? NULL : cframe->args[index];
}

#if FFCALL

/*
	Recursively calculate the offset using the offset of the previous sub-type
*/
static int
gs_offset(const char *type, int index)
{
int offset;
const char *subtype;
  
	if (index == 0)
		return 0;
	subtype = type;
	while (*subtype != _C_STRUCT_E && *subtype++ != '=');	// skip "<name>="

	offset = (gs_offset(type, index-1) + objc_sizeof_type(&subtype[index-1])
			+ objc_alignof_type(&subtype[index]) - 1)
			& -(long)objc_alignof_type(&subtype[index]);

	return offset;
}

/* Determines if the structure type can be returned entirely in registers.
   See the avcall or vacall man pages for more info. FIXME: I'm betting
   this won't work if a structure contains another structure */
int
gs_splittable (const char *type)
{
int i, numtypes;
const char *subtype = type;
int result;
  
	while (*subtype != _C_STRUCT_E && *subtype++ != '=');	// skip "<name>="
		numtypes = 0;
	while (*subtype != _C_STRUCT_E)
		{
		numtypes++;
		subtype = objc_skip_typespec (subtype);
		}
	subtype = type;
	while (*subtype != _C_STRUCT_E && *subtype++ != '=');	// skip "<name>="

	result = 1;
	for (i = 0; i < numtypes; i++)
		{
		result = result && (gs_offset(type, i) / sizeof(__avword) 
						== (gs_offset(type, i)+objc_sizeof_type(&subtype[i])-1)
						/ sizeof(__avword));
		}
//printf("Splittable for %s is %d\n", type, result);
	return result;
}

/*
	This is implemented as a function so it can be used by other
	routines (like the DO forwarding)
*/
void
GSFFCallInvokeWithTargetAndImp(NSInvocation *_inv, id anObject, IMP imp)
{
int i;
av_alist alist;
NSInvocation_t *inv = (NSInvocation_t*)_inv;
void *retval = inv->_retval;
#if 0
NSLog(@"GSFFCallInvokeWithTargetAndImp %@", _inv);
#endif
  /* Do an av call starting with the return type */
#undef CASE_TYPE
#define CASE_TYPE(_T, _V, _F)				\
	case _T:					\
	  _F(alist, imp, retval);	       		\
          break;

	switch (*inv->_info[0].type)
		{
		case _C_ID:
			av_start_ptr(alist, imp, id, retval);
			break;
		case _C_CLASS:
			av_start_ptr(alist, imp, Class, retval);
			break;
		case _C_SEL:
			av_start_ptr(alist, imp, SEL, retval);
			break;
		case _C_PTR:
			av_start_ptr(alist, imp, void *, retval);
			break;
		case _C_CHARPTR:
			av_start_ptr(alist, imp, char *, retval);
			break;
	  
		CASE_TYPE(_C_CHR,  char, av_start_char)
		CASE_TYPE(_C_UCHR, unsigned char, av_start_uchar)
		CASE_TYPE(_C_SHT,  short, av_start_short)
		CASE_TYPE(_C_USHT, unsigned short, av_start_ushort)
		CASE_TYPE(_C_INT,  int, av_start_int)
		CASE_TYPE(_C_UINT, unsigned int, av_start_uint)
		CASE_TYPE(_C_LNG,  long, av_start_long)
		CASE_TYPE(_C_ULNG, unsigned long, av_start_ulong)
		CASE_TYPE(_C_LNG_LNG,  long long, av_start_longlong)
		CASE_TYPE(_C_ULNG_LNG, unsigned long long, av_start_ulonglong)
		CASE_TYPE(_C_FLT,  float, av_start_float)
		CASE_TYPE(_C_DBL,  double, av_start_double)

		case _C_STRUCT_B:
			{
			int split = 0;
			
			if (inv->_info[0].size > sizeof(long)
					&& inv->_info[0].size <= 2*sizeof(long))
				split = gs_splittable(inv->_info[0].type);

			_av_start_struct(alist, imp, inv->_info[0].size, split, retval);
			break;
			}
		case _C_VOID:
			av_start_void(alist, imp);
			break;
		default:
			NSCAssert1(0, @"GSFFCallInvocation: Type '%s' not implemented", 
						inv->_info[0].type);
			break;
		}

	av_ptr(alist, id, anObject);				// Set target and selector
	av_ptr(alist, SEL, inv->_selector);

	for (i = 2; i < inv->_numArgs; i++)			// Set remaining arguments
		{
		const char *type = inv->_info[i+1].type;
		unsigned size = inv->_info[i+1].size;
//		void *datum = callframe_arg_addr((callframe_t *)inv->_cframe, i);

#undef CASE_TYPE
#define CASE_TYPE(_T, _V, _F)			\
	case _T:							\
	  {									\
	    _V c;							\
			[_inv getArgument:&c atIndex:i];  \
		/* memcpy(&c, datum, size); */	\
            _F(alist, c);				\
	    break;							\
	  }

		switch (*type)
			{
			case _C_ID:
				{
				id obj;
				[_inv getArgument:&obj atIndex:i];
//				memcpy(&obj, datum, size);
				av_ptr(alist, id, obj);
				break;
				}
			case _C_CLASS:
				{
				Class obj;
				[_inv getArgument:&obj atIndex:i];
//				memcpy(&obj, datum, size);
				av_ptr(alist, Class, obj);
				break;
				}
			case _C_SEL:
				{
				SEL sel;
				[_inv getArgument:&sel atIndex:i];
//				memcpy(&sel, datum, size);
				av_ptr(alist, SEL, sel);
				break;
				}
			case _C_PTR:
				{
				void *ptr;
				[_inv getArgument:&ptr atIndex:i];
//				memcpy(&ptr, datum, size);
				av_ptr(alist, void *, ptr);
				break;
				}
			case _C_CHARPTR:
				{
				char *ptr;
				[_inv getArgument:&ptr atIndex:i];
//				memcpy(&ptr, datum, size);
				av_ptr(alist, char *, ptr);
				break;
				}
			
			CASE_TYPE(_C_CHR,  char, av_char)
			CASE_TYPE(_C_UCHR, unsigned char, av_uchar)
			CASE_TYPE(_C_SHT,  short, av_short)
			CASE_TYPE(_C_USHT, unsigned short, av_ushort)
			CASE_TYPE(_C_INT,  int, av_int)
			CASE_TYPE(_C_UINT, unsigned int, av_uint)
			CASE_TYPE(_C_LNG,  long, av_long)
			CASE_TYPE(_C_ULNG, unsigned long, av_ulong)
			CASE_TYPE(_C_LNG_LNG,  long long, av_longlong)
			CASE_TYPE(_C_ULNG_LNG, unsigned long long, av_ulonglong)
			CASE_TYPE(_C_FLT,  float, av_float)
			CASE_TYPE(_C_DBL,  double, av_double)

			case _C_STRUCT_B:
				{ // needs to create a buffer of enough size to get the argument
					char *bfr;
					bfr=objc_malloc(inv->_info[i+1].size);
					[_inv getArgument:bfr atIndex:i];
					_av_struct(alist, size, inv->_info[i+1].align, bfr);
					objc_free(bfr);
					break;
				}
			default:
				NSCAssert1(0, @"GSFFCallInvocation: Argument Type '%s' not \
							implemented", type);
				break;
			}
		}

	av_call(alist);									// Call it
}
#endif
#endif

#if 0

/* 
   For encoding and decoding the method arguments, we have to know where
   to find things in the "argframe" as returned by __builtin_apply_args.

   For some situations this is obvious just from the selector type 
   encoding, but structures passed by value cause a problem because some
   architectures actually pass these by reference, i.e. use the
   structure-value-address mentioned in the gcc/config/_/_.h files.

   These differences are not encoded in the selector types.

   Below is my current guess for which architectures do this.
   xxx I really should do this properly by looking at the gcc config values.

   I've also been told that some architectures may pass structures with
   sizef(structure) > sizeof(void*) by reference, but pass smaller ones by
   value.  The code doesn't currently handle that case.
*/

const char *
NSGetSizeAndAlignment(const char *typePtr, unsigned *sizep, unsigned *alignp)
{
NSArgumentInfo info;

	typePtr = mframe_next_arg(typePtr, &info);
	if (sizep)
		*sizep = info.size;
	if (alignp)
		*alignp = info.align;
	return NULL;	// !!!!! should return pointer the position of the next data type
}

//
// the main purpose of the following function seems to correct the signature for
// methods returning structs (e.g. NSPoint) which get passed a hidden argument with
// a pointer where the returned struct is to be stored at and they effectively return (void)
//

char *
mframe_build_signature(const char *typePtr, int *size, int *narg, char *buf)
{
	MFRAME_ARGS	cum;
	BOOL doMalloc = NO;
	const char *types;
	char *start;
	char *dest;
	int total = 0;
	int count = 0;

//	NSLog(@"mframe_build_signature of %s", typePtr);

	if (buf == 0)		// If we have not been given a buffer, alloc space on
		{				// the stack for the largest concievable type encoding.
		doMalloc = YES;
//		buf = alloca((strlen(typePtr)+1)*16);
		buf = objc_malloc((strlen(typePtr)+1)*16);	// always allocate on heap
		}
	NSLog(@"buf=%08x", buf);
										// Copy the return type info (including 
	types = objc_skip_typespec(typePtr);// qualifiers) into the buffer
	strncpy(buf, typePtr, types - typePtr);
	buf[types-typePtr] = '\0'; 
										// Point to the return type, initialise 
										// size of stack args, and skip to the 
										// first argument.
	types = objc_skip_type_qualifiers(typePtr);
	MFRAME_INIT_ARGS(cum, types);
	
// NSLog(@"cum=%d", cum);

	types = objc_skip_typespec(types);
	if (*types == '+')
		types++;

//	NSLog(@"types=%s", types);

	while (isdigit(*types))
		types++;						// Where to start putting encoding info
										// Leave enough room for the size of 
	start = &buf[strlen(buf)+10];		// the stack args to be stored after
	dest = start;						// the return type.

	while (types && *types)				// Now step through all the arguments. 
		{								// Copy any type qualifiers, but let 
		const char *qual = types;		// the macro write all the other info 
										// into the buffer.
		types = objc_skip_type_qualifiers(types);
		while (qual < types)			// If there are any type qualifiers, 
			*dest++ = *qual++;			// copy the through to the destination.

		MFRAME_ARG_ENCODING(cum, types, total, dest);	// (re)encode in case cum was > 0
		count++;
		}
	*dest = '\0';
										// Write the total size of the stack 
										// arguments after the return type,
										// then copy the remaining type info to 
										// fill the gap.
	sprintf(&buf[strlen(buf)], "%d", total);
	dest = &buf[strlen(buf)];
	while (*start)
		*dest++ = *start++;
	*dest = '\0';
										// If we have written into a local 
	if (doMalloc)						// buffer, we need to allocate memory
		{								// in which to return our result.
//		char *tmp = objc_malloc(dest - buf + 1);

//		strcpy(tmp, buf);
//		buf = tmp;
		buf=objc_realloc(buf, dest - buf + 1);	// shrink to what we really need to keep
		}
										// If the caller wants to know the 
	if (size)							// total size of the stack and/or the
		*size = total;					// number of arguments, return them in
										// the appropriate variables.
	if (narg)
		*narg = count;

	NSLog(@"size=%d narg=%d -> %08x:%s", total, count, buf, buf);
	return buf;
}

#endif

const char *
mframe_next_arg(const char *typePtr, NSArgumentInfo *info)
{
	NSArgumentInfo local;					// Step through method encoding
	BOOL flag;								// information extracting details.

	if (info == 0)
		info = &local;						// dummy storage
	
	// Skip past any type qualifiers,
	flag = YES;								// return them if caller wants them
	info->qual = 0;	// no qualifier
	info->floatAsDouble = NO;
	while (flag)
		{
		switch (*typePtr)
			{
			case _C_CONST:  info->qual |= _F_CONST; break;
			case _C_IN:     info->qual |= _F_IN; break;
			case _C_INOUT:  info->qual |= _F_INOUT; break;
			case _C_OUT:    info->qual |= _F_OUT; break;
			case _C_BYCOPY: info->qual |= _F_BYCOPY; break;
#ifdef _C_BYREF
			case _C_BYREF:  info->qual |= _F_BYREF; break;
#endif
			case _C_ONEWAY: info->qual |= _F_ONEWAY; break;
			default: flag = NO; continue;
			}
		if (flag)
			typePtr++;
		}

	info->type = typePtr;

#if MFRAME_STRUCT_BYREF
	info->byRef = (*typePtr == _C_STRUCT_B || *typePtr == _C_UNION_B || *typePtr == _C_ARY_B);
#else
	info->byRef = NO;
#endif
	
	switch (*typePtr++)				// Scan for size and alignment information.
		{
		case _C_ID:
			info->size = sizeof(id);
			info->align = __alignof__(id);
			break;

		case _C_CLASS:
			info->size = sizeof(Class);
			info->align = __alignof__(Class);
			break;

		case _C_SEL:
			info->size = sizeof(SEL);
			info->align = __alignof__(SEL);
			break;

		case _C_CHR:
			info->size = sizeof(char);
			info->align = __alignof__(char);
			break;

		case _C_UCHR:
			info->size = sizeof(unsigned char);
			info->align = __alignof__(unsigned char);
			break;

		case _C_SHT:
			info->size = sizeof(short);
			info->align = __alignof__(short);
			break;

		case _C_USHT:
			info->size = sizeof(unsigned short);
			info->align = __alignof__(unsigned short);
			break;

		case _C_INT:
			info->size = sizeof(int);
			info->align = __alignof__(int);
			break;

		case _C_UINT:
			info->size = sizeof(unsigned int);
			info->align = __alignof__(unsigned int);
			break;

		case _C_LNG:
			info->size = sizeof(long);
			info->align = __alignof__(long);
			break;

		case _C_ULNG:
			info->size = sizeof(unsigned long);
			info->align = __alignof__(unsigned long);
			break;

		case _C_LNG_LNG:
			info->size = sizeof(long long);
			info->align = __alignof__(long long);
			break;

		case _C_ULNG_LNG:
			info->size = sizeof(unsigned long long);
			info->align = __alignof__(unsigned long long);
			break;

		case _C_FLT:
#if MFRAME_FLT_IN_FRAME_AS_DBL
			// I guess we should set align/size differently...
			info->floatAsDouble = YES;
			info->size = sizeof(double);
			info->align = __alignof__(double);
#else
			info->size = sizeof(float);
			info->align = __alignof__(float);
#endif
			break;

		case _C_DBL:
			info->size = sizeof(double);
			info->align = __alignof__(double);
			break;

		case _C_PTR:
			info->size = sizeof(char*);
			info->align = __alignof__(char*);
			if (*typePtr == '?')
				typePtr++;
			else
				{
				typePtr = mframe_next_arg(typePtr, &local);
				info->isReg = local.isReg;
				info->offset = local.offset;
				}
			break;

		case _C_ATOM:
		case _C_CHARPTR:
			info->size = sizeof(char*);
			info->align = __alignof__(char*);
			break;
		
		case _C_ARY_B:
			{
			int	length = atoi(typePtr);
		
			while (isdigit(*typePtr))
				typePtr++;

			typePtr = mframe_next_arg(typePtr, &local);
			info->size = length * ROUND(local.size, local.align);
			info->align = local.align;
			typePtr++;								// Skip end-of-array
			}
			break; 
		
		case _C_STRUCT_B:
			{
//			struct { int x; double y; } fooalign;
			struct { unsigned char x; } fooalign;
			int acc_size = 0;
			int acc_align = __alignof__(fooalign);

			while (*typePtr != _C_STRUCT_E)			// Skip "<name>=" stuff.
				if (*typePtr++ == '=')
					break;
													// Base structure alignment 
			if (*typePtr != _C_STRUCT_E)			// on first element.
				{
				typePtr = mframe_next_arg(typePtr, &local);
				if (typePtr == 0)
					return 0;						// error

				acc_size = ROUND(acc_size, local.align);
				acc_size += local.size;
				acc_align = MAX(local.align, __alignof__(fooalign));
				}
													// Continue accumulating 
			while (*typePtr != _C_STRUCT_E)			// structure size.
				{
				typePtr = mframe_next_arg(typePtr, &local);
				if (typePtr == 0)
					return 0;						// error

				acc_size = ROUND(acc_size, local.align);
				acc_size += local.size;
				}
			info->size = acc_size;
			info->align = acc_align;
//printf("_C_STRUCT_B  size %d align %d\n",info->size,info->align);
			typePtr++;								// Skip end-of-struct
			}
			break;

		case _C_UNION_B:
			{
			int	max_size = 0;
			int	max_align = 0;

			while (*typePtr != _C_UNION_E)			// Skip "<name>=" stuff.
				if (*typePtr++ == '=')
					break;

			while (*typePtr != _C_UNION_E)
				{
				typePtr = mframe_next_arg(typePtr, &local);
				if (typePtr == 0)
					return 0;						// error
				max_size = MAX(max_size, local.size);
				max_align = MAX(max_align, local.align);
				}
			info->size = max_size;
			info->align = max_align;
			typePtr++;								// Skip end-of-union
			}
			break;
		
		case _C_VOID:
			info->size = 0;
			info->align = __alignof__(char*);
			break;
		
		default:
			return 0;
		}

	if (typePtr == 0)
		return 0;									// error
						// If we had a pointer argument, we will already have 
						// gathered (and skipped past) the argframe offset 
						// info - so we don't need to (and can't) do it here.
	if (info->type[0] != _C_PTR || info->type[1] == '?')
		{
		if (*typePtr == '+')					// May tell the caller if item 
			{									// is stored in a register.
			typePtr++;
			info->isReg = YES;
			}
		else 
			if (info->isReg)
				info->isReg = NO;
												// May tell the caller what the 
		info->offset = 0;						// stack/register offset is for
		while (isdigit(*typePtr))				// this argument.
			info->offset = info->offset * 10 + (*typePtr++ - '0');
		}

	return typePtr;
}

#if 0

int
method_types_get_number_of_arguments (const char *type)
{
int i = 0;						// Return the number of arguments that the 
								// method MTH expects.  Note that all methods 
	while (*type)				// need two implicit arguments `self' & `_cmd'
		{
		type = objc_skip_argspec (type);
		i += 1;
		}

	return i - 1;
}

int
method_types_get_size_of_stack_arguments (const char *type)
{										// Return the size of the argument 
	type = objc_skip_typespec (type);	// block needed on the stack to invoke
	return atoi (type);					// the method MTH.  This may be zero, 
}										// if all args are passed in registers.

int
method_types_get_size_of_register_arguments(const char *types)
{
const char *type = strrchr(types, '+');

	return (type) ? atoi(++type) + sizeof(void*) : 0;
}
			// To fix temporary bug in method_get_next_argument() on NeXT boxes
			// FIX ME Perhaps this isn't working with the NeXT runtime?

// get +n from type specifier to know location of next argument 

char *
method_types_get_next_argument (arglist_t argf, const char **type)
{
//	NSLog(@"method_types_get_next_argument(%08x, %s)", (unsigned) argf, *type);
	{
	const char *t = objc_skip_argspec (*type);
	arglist_t argframe = (void*)argf;

	if (*t == '\0')
		return 0;

	*type = t;
	t = objc_skip_typespec (t);

	if (*t == '+')
		return argframe->arg_regs + atoi(++t);		// arg_regs is a char[] correctly aligned, so we return a pointer into the argument frame
#if NeXT 							// FIX ME This -8 needed
	return argframe->arg_ptr + (atoi(t) - 8);	// on my 68k NeXT box
#else
#if Linux_ARM
	return argframe->arg_ptr + (atoi(t) - 4);	// on my Zaurus 5500G
#else
	return argframe->arg_ptr + atoi(t);		// arg_ptr interprets the argframe as a pointer and we return the object pointed to + the offset
#endif
#endif
	}
}
#endif

#if 0
/* mframe_dissect_call()

   This function encodes the arguments of a method call.

   Call it with an ARGFRAME that was returned by __builtin_args(), and
   a TYPE string that describes the input and return locations,
   i.e. from sel_get_types() or Method->method_types.

   The function ENCODER will be called once with each input argument.

   Returns YES iff there are any outparameters---parameters that for
   which we will have to get new values after the method is run,
   e.g. an argument declared (out char*). 
*/

BOOL
mframe_dissect_call (arglist_t argframe,
					 const char *type,
					 void (*encoder)(int argnum, void *datum, const char *type, int flags, id arg1),
					 id arg1
					 )
{
unsigned flags;
char *datum;
int argnum;
char *sig=0;
BOOL out_parameters = NO;

BOOL pass_pointers = NO;	// pass pointers to and from functions in other
							// languages rather than using the DO behaviour 
							// which is to copy a single obj when given a ptr.
#ifndef Linux_ARM	// here we have to rebuild the signature in any case!
	if (*type == _C_STRUCT_B || *type == _C_UNION_B || *type == _C_ARY_B)
#endif
		{ // method returns struct/union/array - needs to properly process pointer for return value if MFRAME_STACK_STRUCT
		// variable datum is used here as a temporary buffer pointer for a different purpose
//		datum = alloca((strlen(type)+1)*10);
//		type = mframe_build_signature(type, 0, 0, datum);
		type=sig=mframe_build_signature(type, 0, 0, 0);	// malloc buffer
		}
	// Enumerate all the arguments in ARGFRAME, and call ENCODER
    // for each one.  METHOD_TYPES_GET_NEXT_ARGUEMENT() returns 0
	// when there are no more arguments, otherwise it returns a 
	// pointer to the argument in the ARGFRAME.
	for (datum = method_types_get_next_argument(argframe, &type), argnum = 0;
			datum;
			datum = method_types_get_next_argument(argframe, &type), argnum++)
		{										// Get the type qualifiers,
	//	NSLog(@"mframe_dissect_call type=%s", type);
		flags = objc_get_type_qualifiers(type);	// like IN, OUT, INOUT, ONEWAY.
							// Skip over type qualifiers, so now TYPE is
							// pointing directly at the char corresponding to 
							// the arg's type, as defined in <objc/objc-api.h>
		type = objc_skip_type_qualifiers(type);
							// Decide how, (or whether), to encode the arg
							// depending on its FLAGS and TYPE.  Only the first
							// two cases involve params that may potentially
	   // be passed by reference, and thus only the
	   // first two may change the value of OUT_PARAMETERS
//	NSLog(@"mframe_dissect_call - encoder=%08x argnum=%d datum=%08x flags=%x type=%s", encoder, argnum, datum, flags, type);
		switch (*type)
			{
			case _C_CHARPTR:							// Handle a (char*) arg
							// If the char* is qualified as an OUT parameter, 
							// or if it not explicitly qualified as an IN 
							// parameter, then we will have to get this char* 
							// again after the method is run, because the 
							// method may have changed it.  Set OUT_PARAMETERS 
							// accordingly.
				if ((flags & _F_OUT) || !(flags & _F_IN))
					out_parameters = YES;
						// If the char* is qualified as an IN parameter, or not 
						// explicity qualified as an OUT param, then encode it.
				if ((flags & _F_IN) || !(flags & _F_OUT))
					(*encoder) (argnum, datum, type, flags, arg1);
				break;

			case _C_PTR:	// If the pointer's value is qualified as an OUT
							// parameter, or if it not explicitly qualified as 
							// an IN parameter, then we will have to get the 
							// value pointed to again after the method is run, 
							// because the method may have changed it.  Set 
							// OUT_PARAMETERS accordingly.
				if ((flags & _F_OUT) || !(flags & _F_IN))
					out_parameters = YES;
				if (pass_pointers) 
					{
					if ((flags & _F_IN) || !(flags & _F_OUT))
						(*encoder) (argnum, datum, type, flags, arg1);
					}
				else		// Handle an arg that is a pointer to a non-char.
					{		// But (void*) and (anything**) is not allowed.

							// The arg is a pointer to something; increment 
					type++;	// TYPE so we can see what it is a pointer to.

							// If the pointer's value is qualified as an IN 
							// parameter, or not explicity qualified as an OUT 
							// parameter, then encode it.
					if ((flags & _F_IN) || !(flags & _F_OUT))
						(*encoder) (argnum, *(void**)datum, type, flags, arg1);
					}
				break;

			case _C_STRUCT_B:			// Handle struct and array arguments.
			case _C_UNION_B:
			case _C_ARY_B:	// Whether DATUM points to the data, or points to a 
							// pointer that points to the data, depends on the 
							// value of MFRAME_STRUCT_BYREF. Do the right thing
							// so that ENCODER gets a ptr directly to the data.
#if MFRAME_STRUCT_BYREF
				(*encoder) (argnum, *(void**)datum, type, flags, arg1);
#else
				(*encoder) (argnum, datum, type, flags, arg1);
#endif
				break;

			default:					// Handle arguments of all other types.
				(*encoder) (argnum, datum, type, flags, arg1);
			}				// Return a BOOL indicating whether or not there
//		NSLog(@"encoder=%08x", encoder);
		}					// are parameters that were passed by reference; we  
							// will need to get those values again after the
	if(sig)
		objc_free(sig);		// we had allocated a signature buffer locally
	return out_parameters;	// method has finished executing because the
}							// execution of the method may have changed them.

/* mframe_do_call()

   This function decodes the arguments of method call, builds an
   argframe of type arglist_t, and invokes the method using
   __builtin_apply; then it encodes the return value and any
   pass-by-reference arguments.

   ENCODED_TYPES should be a string that describes the return value
   and arguments.  It's argument types and argument type qualifiers
   should match exactly those that were used when the arguments were
   encoded with mframe_dissect_call()---mframe_do_call() uses
   ENCODED_TYPES to determine which variable types it should decode.

   ENCODED_TYPES is used to get the types and type qualifiers, but not
   to get the register and stack locations---we get that information
   from the selector type of the SEL that is decoded as the second
   argument.  In this way, the ENCODED_TYPES may come from a machine
   of a different architecture.  Having the original ENCODED_TYPES is
   good, just in case the machine running mframe_do_call() has some
   slightly different qualifiers.  Using different qualifiers for
   encoding and decoding could lead to massive confusion.


   DECODER should be a pointer to a function that obtains the method's
   argument values.  For example:

     void my_decoder (int argnum, void *data, const char *type)

     ARGNUM is the number of the argument, beginning at 0.
     DATA is a pointer to the memory where the value should be placed.
     TYPE is a pointer to the type string of this value.

     mframe_do_call() calls this function once for each of the methods
     arguments.  The DECODER function should place the ARGNUM'th
     argument's value at the memory location DATA.
     mframe_do_call() calls this function once with ARGNUM -1, DATA 0,
     and TYPE 0 to denote completion of decoding.


     If DECODER malloc's new memory in the course of doing its
     business, then DECODER is responsible for making sure that the
     memory will get free eventually.  For example, if DECODER uses
     -decodeValueOfCType:at:withName: to decode a char* string, you
     should remember that -decodeValueOfCType:at:withName: malloc's
     new memory to hold the string, and DECODER should autorelease the
     malloc'ed pointer, using the NSData class.


   ENCODER should be a pointer to a function that records the method's
   return value and pass-by-reference values.  For example:

     void my_encoder (int argnum, void *data, const char *type, int flags)

     ARGNUM is the number of the argument; this will be -1 for the
       return value, and the argument index for the pass-by-reference
       values; the indices start at 0.
     DATA is a pointer to the memory where the value can be found.
     TYPE is a pointer to the type string of this value.
     FLAGS is a copy of the type qualifier flags for this argument; 
       (see <objc/objc-api.h>).

     mframe_do_call() calls this function after the method has been
     run---once for the return value, and once for each of the
     pass-by-reference parameters.  The ENCODER function should place
     the value at memory location DATA wherever the user wants to
     record the ARGNUM'th return value.

  PASS_POINTERS is a flag saying whether pointers should be passed
  as pointers (for local stuff) or should be assumed to point to a
  single data item (for distributed objects).
*/

void
mframe_do_call (const char *encoded_types,
				void(*decoder)(int argnum, void *datum, const char *type, id arg1),
				id decoder_arg1,
				void(*encoder)(int argnum, void *datum, const char *type, int flags, id *arg1, int arg2, id arg3),
				id *encoder_arg1,
				int encoder_arg2,
				id encoder_arg3
				)
{
const char *type;		// method type string obtained from the target's 
						// OBJC_METHOD struct for the selector we're sending.

const char *tmptype;	// pointer into the local variable TYPE string.

const char *etmptype;	// pointer into the argument ENCODED_TYPES string.

id object;				// target object that will receive the message.

SEL selector;			// selector for message we're sending to the TARGET.

IMP method_implementation;		// OBJECT's implementation of the SELECTOR.

// int stack_argsize;		// num bytes for holding arguments passed on the stack.

// int reg_argsize;		// num bytes bytes for holding args passed in registers

// arglist_t argframe;		// structure for holding the arguments to the method.

char *datum;			// ptr into ARGFRAME; points at individual arguments.

unsigned flags;			// Type qualifier flags; see <objc/objc-api.h>

int argnum;				// Which argument number are we processing now?

//void *retframe;		// ptr to the memory holding the ret value of method.

BOOL out_parameters = NO;	// Does the method have any arguments that are 
							// passed by reference? If so, we need to encode 
							// them, since the method may have changed them.

callframe_t *cframe;		// cif information for calling the method
NSMethodSignature *sig;
void *retval;
NSInvocation_t *inv;
id objToFree;
// id datToFree;
BOOL pass_pointers = NO;	// pass pointers to and from functions in other
							// languages rather than using the DO behaviour 
							// which is to copy a single obj when given a ptr.

							// Decode the object, (which is always the first 
							// arg to a method), into the local variable OBJECT
NSLog(@"mframe_do_call(%08x %s)", encoded_types, encoded_types);
	(*decoder) (0, &object, @encode(id), decoder_arg1);
	NSCParameterAssert (object);
	NSLog(@"decoded object=%@", object);
							// Decode the selector, (which is always the second 
							// argument to a method), into the local variable 
							// SELECTOR. FIX ME @encode(SEL) produces "^v" in 
							// gcc 2.5.8.  It should be ":"
	(*decoder) (1, &selector, ":", decoder_arg1);
	NSCParameterAssert (selector);
	NSLog(@"decoded selector=%@", NSStringFromSelector(selector));
			// Get the "selector type" for this method.  The "selector type" is
			// a string that lists the return and argument types, and also
			// indicates in which registers and where on the stack the args
			// should be placed before the method call.  The selector type
			// string we get here should have the same arg and return types
			// as the ENCODED_TYPES string, but it will have different register
			// and stack locations if the ENCODED_TYPES came from a machine of 
			// a different architecture.
#if NeXT_runtime
	{
    Method m = class_getInstanceMethod(object->isa, selector);

	if (!m) 
		abort();
	type = m->method_types;
	}
#elif 0
	{
    Method_t m = class_get_instance_method (object->class_pointer, selector);

    NSCParameterAssert (m);
    type = m->method_types;
	}
#else
	type = sel_get_type (selector);
#endif /* NeXT_runtime */

						// Make sure we successfully got the method type, and 
						// that its types match the ENCODED_TYPES.
//	NSLog(@"selector type=%s", type);
	NSCParameterAssert (type);
	NSCParameterAssert (sel_types_match(encoded_types, type));

						// Build the cif frame
	sig = [NSMethodSignature signatureWithObjCTypes: type];
	cframe = callframe_from_info([sig _methodInfo],
								 [sig numberOfArguments],
								 &retval);
//	NSLog(@"cframe=%08x", cframe);
						// Put OBJECT and SELECTOR into the ARGFRAME.

//	tmptype = type;		// Init our tmp pointers into the method type strings.
	tmptype = objc_skip_argspec (type);
	etmptype = objc_skip_argspec (encoded_types);
						// Get a pointer into ARGFRAME, pointing to the 
						// location where the first argument is to be stored.
//	datum = method_types_get_next_argument (argframe, &tmptype);
//	NSCParameterAssert (datum);
	NSCParameterAssert (*tmptype == _C_ID);
						// Put the target object there
//	*(id*)datum = object;
	callframe_set_arg(cframe, 0, &object, sizeof(id));
						// Get a pointer into ARGFRAME, pointing to the 
						// location where the second argument is to be stored.
	tmptype = objc_skip_argspec (tmptype);
	etmptype = objc_skip_argspec(etmptype);
//	datum = method_types_get_next_argument(argframe, &tmptype);
//	NSCParameterAssert (datum);
	NSCParameterAssert (*tmptype == _C_SEL);
//	*(SEL*)datum = selector;						// Put the selector there.
	callframe_set_arg(cframe, 1, &selector, sizeof(SEL));
//	NSLog(@"tmptype=%s", tmptype);

							// Decode args after OBJECT and SELECTOR, and put
							// them into ARGFRAME.  Step TMPTYPE and ETMPTYPE
							// in lock-step through their method type strings. 	
//	for (datum = method_types_get_next_argument (argframe, &tmptype),
//			etmptype = objc_skip_argspec (etmptype), argnum = 2;
//			datum;
//			datum = method_types_get_next_argument (argframe, &tmptype),
//			etmptype = objc_skip_argspec (etmptype), argnum++)

	tmptype = objc_skip_argspec (tmptype);
	etmptype = objc_skip_argspec (etmptype);
	for (argnum = 2; *tmptype != '\0';
			tmptype = objc_skip_argspec (tmptype),
			etmptype = objc_skip_argspec (etmptype), argnum++)
		{					// Get type qualifiers, like IN, OUT, INOUT, ONEWAY
		flags = objc_get_type_qualifiers (etmptype);
							// Skip over type qualifiers, so now TYPE is
							// pointing directly at the char corresponding to 
							// the arg's type, as defined in <objc/objc-api.h>
		tmptype = objc_skip_type_qualifiers(tmptype);

							// Setup information in context.
		datum = callframe_arg_addr(cframe, argnum);
//		ctxt->datum = callframe_arg_addr(cframe, argnum);
//		ctxt->type = tmptype;
//		ctxt->flags = flags;

							// Decide how, (or whether or not), to decode the 
							// argument depending on its FLAGS and TMPTYPE.  
							// Only the first two cases involve parameters that 
							// may potentially be passed by reference, and thus 
							// only the first two may change the value of 
							// OUT_PARAMETERS.  *** Note: This logic must match 
							// exactly the code in mframe_dissect_call(); that
//		NSLog(@"argnum=%d tmptype=%s etmptype=%s", argnum, tmptype, etmptype);
		switch (*tmptype)	// function should encode exactly what we decode.
			{
			case _C_CHARPTR:							// Handle a (char*) arg
							// If the char* is qualified as an OUT parameter, 
							// or if it not explicitly qualified as an IN 
							// parameter, then we will have to get this char* 
							// again after the method is run, because the 
							// method may have changed it.  Set OUT_PARAMETERS 
							// accordingly.
				if ((flags & _F_OUT) || !(flags & _F_IN))
					out_parameters = YES;
							// If the char* is qualified as an IN parameter, or 
							// not explicity qualified as an OUT parameter, 
							// then decode it. Note: decoder allocates memory 
							// for holding string, and it is also responsible 
							// for making sure that the memory gets freed 
							// eventually, (usually through the autorelease of 
							// NSData object).
				if ((flags & _F_IN) || !(flags & _F_OUT))
					(*decoder) (argnum, datum, tmptype, decoder_arg1);
				break;
	
			case _C_PTR:	// If the pointer's value is qualified as an OUT
							// parameter, or if it not explicitly qualified as 
							// an IN parameter, then we will have to get the 
							// value pointed to again after the method is run, 
							// because the method may have changed it.  Set 
							// OUT_PARAMETERS accordingly.
				if ((flags & _F_OUT) || !(flags & _F_IN))
					out_parameters = YES;
				if (pass_pointers)
					{
					if ((flags & _F_IN) || !(flags & _F_OUT))
						(*decoder) (argnum, datum, tmptype, decoder_arg1);
					}
				else		// Handle an arg that is a pointer to a non-char
					{		// But (void*) and (anything**) is not allowed.
							// argument is a pointer to something; increment 
							// TYPE so we can see what it is a pointer to.
					tmptype++;
							// Alloc some memory to be pointed to, and to hold
							// the value.  Note that it is allocated on the
							// stack, and methods that want to keep the data
							// pointed to, will have to make their own copies.
					NSLog(@"call alloca()");
					*(void**)datum = alloca (objc_sizeof_type (tmptype));
							// If the pointer's value is qualified as an IN 
							// parameter, or not explicity qualified as an OUT 
							// parameter, then decode it.
					if ((flags & _F_IN) || !(flags & _F_OUT))
						(*decoder) (argnum, *(void**)datum, tmptype, decoder_arg1);
					}
				break;
	
			case _C_STRUCT_B:					// Handle struct and array args
			case _C_UNION_B:					
			case _C_ARY_B:	// Whether DATUM points to the data, or points to a 
							// pointer that points to the data, depends on the 
							// value of MFRAME_STRUCT_BYREF.  Do the right 
							// thing so that ENCODER gets a pointer to directly 
							// to the data.
#if MFRAME_STRUCT_BYREF
							// Allocate some memory to be pointed to, and to 
							// hold the data.  Note that it is allocated on the 
							// stack, and methods that want to keep the data 
							// pointed to, will have to make their own copies.
				NSLog(@"call alloca()");
				*(void**)datum = alloca (objc_sizeof_type(tmptype));
				(*decoder) (argnum, *(void**)datum, tmptype, decoder_arg1);
#else
				(*decoder) (argnum, datum, tmptype, decoder_arg1);
#endif
				break;
		
			default:					// Handle arguments of all other types.
					// NOTE FOR OBJECTS: Unlike [Decoder decodeObjectAt:...,
					// this function does not generate a reference to the
					// object; the object may be autoreleased; if the method
					// wants to keep a reference to the object, it will have to
					// -retain it.
//				NSLog(@"(*decoder)(%d, %08x, %s)", argnum, datum, tmptype);
				(*decoder) (argnum, datum, tmptype, decoder_arg1);
//				NSLog(@"(*decoder)(%d, %08x, %s) done", argnum, datum, tmptype);
			}
//		NSLog(@"... argnum=%d tmptype=%s etmptype=%s", argnum, tmptype, etmptype);
		}			// End of for() loop that enumerates the method's arguments
	(*decoder) (-1, 0, 0, decoder_arg1);
														// Invoke the method!

					// Find the target obj's implementation of this selector.
	method_implementation = objc_msg_lookup (object, selector);
	NSCParameterAssert (method_implementation);
					// Send the message to the target, and get the return value 
					// in RETFRAME.  The args will still be in ARGFRAME, so we 
					// can get the pass-by-reference info from there.
//	retframe = __builtin_apply((void(*)(void))method_implementation, 
//								argframe, 
//								stack_argsize);
	inv = (NSInvocation_t *)NSAllocateObject([NSInvocation class], NSDefaultMallocZone());
	inv->_retval = retval;
	inv->_selector = selector;
	inv->_cframe = cframe;
	inv->_info = [sig _methodInfo];
	inv->_numArgs = [sig numberOfArguments];
//	ctxt->objToFree = (id)inv;
	objToFree = (id)inv;
	NSLog(@"GSFFCallInvokeWithTargetAndImp(%@, %@, %08x)", inv, object, method_implementation);
//	exit(0); // stop here to check NSLog
	GSFFCallInvokeWithTargetAndImp((NSInvocation *)inv, object, 
					method_implementation);
//	ctxt->objToFree = nil;
	objToFree = nil;
	NSDeallocateObject((NSInvocation *)inv);

					// Encode the return value and pass-by-reference values, if 
					// there are any.  This logic must match exactly that in
					// mframe_build_return().  OUT_PARAMETERS should be true 
					// here in exactly the same situations as it was true in 
					// mframe_dissect_call().

					// Get the qualifier type of the return value.
	flags = objc_get_type_qualifiers (encoded_types);
					// Get the return type; store it our two temporary char*'s.
	etmptype = objc_skip_type_qualifiers (encoded_types);
	tmptype = objc_skip_type_qualifiers (type);

					// Only encode return values if there is a non-void return 
					// value, a non-oneway void return value, or if there are 
					// values that were passed by reference.
	switch (*tmptype)				// If there is a return value, encode it.
		{
		case _C_VOID:
			if ((flags & _F_ONEWAY) == 0)
				{
	   			int	dummy = 0;
          		(*encoder) (-1, (void*)&dummy, @encode(int), 0, encoder_arg1, encoder_arg2, encoder_arg3);
				}
			break;					// No return value to encode; do nothing.

		case _C_PTR:
			if (pass_pointers)
				(*encoder) (-1, retval, tmptype, flags, encoder_arg1, encoder_arg2, encoder_arg3);
			else
				{		// The argument is a pointer to something; increment 
						// TYPE so we can see what it is a pointer to.
				tmptype++;
									// Encode the value that was pointed to.
				(*encoder) (-1, *(void**)retval, tmptype, flags, encoder_arg1, encoder_arg2, encoder_arg3);
				}
			break;
							// _C_INT: _C_UINT: _C_LNG: _C_ULNG: _C_CHARPTR:
							// _C_ID:   I think this assumes that
		default:			// sizeof(int) == sizeof(void*)
     		(*encoder) (-1, retval, tmptype, flags, encoder_arg1, encoder_arg2, encoder_arg3);
    	}
				// Encode the values returned by reference.  Note: this logic
				// must match exactly the code in mframe_build_return(); that
				// function should decode exactly what we encode here.

	if (out_parameters)		// Step through all the arguments, finding the ones
		{					// that were passed by reference. 
//		for (datum = method_types_get_next_argument (argframe, &tmptype), 
//				argnum = 1, etmptype = objc_skip_argspec (etmptype);
//				datum;
//				datum = method_types_get_next_argument (argframe, &tmptype), 
//				argnum++, etmptype = objc_skip_argspec (etmptype))

		tmptype = objc_skip_argspec (tmptype);
		etmptype = objc_skip_argspec (etmptype);
		for (argnum = 0; *tmptype != '\0';
				tmptype = objc_skip_argspec (tmptype), argnum++,
				etmptype = objc_skip_argspec (etmptype))
			{
							// Get type qualifiers, like IN, OUT, INOUT, ONEWAY
			flags = objc_get_type_qualifiers(etmptype);
							// Skip over the type qualifiers, so now TYPE is
							// pointing directly at the char corresponding to 
						 	// the arg's type, as defined in <objc/objc-api.h>
			tmptype = objc_skip_type_qualifiers (tmptype);

							// Decide how, (or whether or not), to encode the
							// argument depending on its FLAGS and TMPTYPE.
			if ((*tmptype == _C_PTR) && ((flags & _F_OUT) || !(flags & _F_IN)))
				{			// The argument is a pointer (to a non-char), and 
							// the pointer's value is qualified as an OUT 
							// parameter, or it not explicitly qualified as an 
							// IN parameter, then it is a pass-by-reference
							// argument.  The argument is a pointer to 
							// something; increment TYPE so we can see what it 
							// is a pointer to.  And then encode it.
				tmptype++;
				datum = *(void**)callframe_arg_addr(cframe, argnum);
//				(*encoder) (argnum, *(void**)datum, tmptype, flags);
				(*encoder) (argnum, datum, tmptype, flags, encoder_arg1, encoder_arg2, encoder_arg3);
	    		}
			else 
				if (*tmptype == _C_CHARPTR && ((flags & _F_OUT) 
						|| !(flags & _F_IN)))
					{		// The argument is a pointer char string, and the
							// pointer's value is qualified as an OUT param, or
							// it not explicitly qualified as an IN parameter, 
							// then it is a pass-by-reference argument.  Encode 
							// it.  FIX ME Perhaps we could save time and space
							// by saving a copy of the string before the method 
							// call, and then comparing it to this string; if 
							// it didn't change, don't bother sending it back
					datum = callframe_arg_addr(cframe, argnum);
	      			(*encoder) (argnum, datum, tmptype, flags, encoder_arg1, encoder_arg2, encoder_arg3);
					}
			}
		}

	objc_free(cframe);
}
#endif

#if OLD

typedef struct { id many[8];} __big;		// For returning structures ...etc

static __big return_block (void* data)		{ return *(__big*)data; }
static char return_char (char data)			{ return data; }  // char types
static double return_double (double data)	{ return data; }  // double
static float return_float (float data)		{ return data; }  // float
static short return_short (short data)		{ return data; }  // short types

static retval_t apply_block(void* data)
{
void *args = __builtin_apply_args();

	return __builtin_apply((apply_t)return_block, args, sizeof(void*));
}

static retval_t apply_char(char data)
{
void *args = __builtin_apply_args();

	return __builtin_apply((apply_t)return_char, args, sizeof(void*));
}

static retval_t apply_float(float data)
{
void *args = __builtin_apply_args();

	return __builtin_apply((apply_t)return_float, args, sizeof(float));
}

static retval_t apply_double(double data)
{
void *args = __builtin_apply_args();

	return __builtin_apply((apply_t)return_double, args, sizeof(double));
}

static retval_t apply_short(short data)
{
void *args = __builtin_apply_args();

	return __builtin_apply((apply_t)return_short, args, sizeof(void*));
}

/* mframe_build_return()

   This function decodes the values returned from a method call,
   builds a retframe of type retval_t that can be passed to GCC's
   __builtin_return(), and updates the pass-by-reference arguments in
   ARGFRAME.  This function returns a retframe pointer.

   In the function that calls this one, be careful about calling more
   functions after this one.  The memory for the retframe is
   alloca()'ed, not malloc()'ed, and therefore is on the stack and can
   be tromped-on by future function calls.

-- has been changed by hns@computer.org to use malloc() instead of alloca() just to be safe

   The callback function is finally called with the 'type' set to a nul pointer
   to tell it that the return value and all return parameters have been
   dealt with.  This permits the function to do any tidying up necessary.
*/

retval_t
mframe_build_return (arglist_t argframe,
					 const char *type,
					 BOOL out_parameters,
					 void(*decoder)(int argnum, void *datum, const char *type, int flags, id *arg1, id arg2, int arg3),
					 id *arg1, id arg2, int arg3)
{
retval_t retframe = NULL;			// ptr to mem that will hold return value
int retsize;						// size in bytes of RETFRAME
int argnum;							// argument number are we processing now
int flags;							// Type qualifier flags; <objc/objc-api.h>
const char *tmptype;				// pointer into the TYPE string.

void *datum;						// pointer into the ARGFRAME; points at 
									// individual arguments.
const char *rettype;
char *sig=0;

BOOL pass_pointers = NO;	// pass pointers to and from functions in other
							// languages rather than using the DO behaviour 
							// which is to copy a single obj when given a ptr.

NSLog(@"mframe_build_return() type=%s", type);

if (*type == _C_STRUCT_B || *type == _C_UNION_B || *type == _C_ARY_B)
		{
//		tmptype = alloca((strlen(type)+1)*10);
//		type = mframe_build_signature(type, 0, 0, (char*)tmptype);
	type = sig = mframe_build_signature(type, 0, 0, (char*)0);
		NSLog(@"replaced by type=%s", type);
		}
	
					// Get the return type qualifier flags, and the return type
	flags = objc_get_type_qualifiers(type);
	tmptype = objc_skip_type_qualifiers(type);
	rettype = tmptype;

					// Decode the return value and pass-by-reference values, if 
					// there are any.  OUT_PARAMETERS should be the value 
					// returned by mframe_dissect_call().

		// xxx What happens if method declared "- (oneway) foo: (out int*)ip;"
    	// xxx What happens with method declared "- (in char *) bar;"
   		// xxx Is this right?  Do we also have to check _F_ONEWAY?
	if (out_parameters || *tmptype != _C_VOID || (flags & _F_ONEWAY) == 0)
		{					// ARGNUM == -1 signifies to DECODER() that this is 
							// the return value, not an argument.

							// If there is a return value, decode it, and put 
							// it in retframe.
		if (*tmptype != _C_VOID || (flags & _F_ONEWAY) == 0)
			{
			if (*tmptype == _C_VOID)	// Get the size of the returned value.
				retsize = sizeof(void*);
			else
				retsize = objc_sizeof_type (tmptype);
							// Allocate memory on the stack to hold the return 
							// value.  It should be at least 4 * sizeof(void*).
							// FIX ME We need to test retsize's less than 4.
							// Also note that if we return structures using a 
							// structure-value-address, we are potentially 
							// alloca'ing much more than we need here.
							// Find out about returning structures by reference
							// on non--structure-value-address machines, and
							// potentially just always
							// alloca(RETFRAME_SIZE == sizeof(void*)*4)
			retframe = alloca (MAX(retsize, sizeof(void*)*4));
	  
			switch (*tmptype)
				{
				case _C_PTR:
					if (pass_pointers)
						(*decoder) (-1, retframe, tmptype, flags, arg1, arg2, arg3);
					else
						{					// Returning a pointer to something
						unsigned retLength;	// Increment TYPE so we can see 
											// what it is a pointer to.
						tmptype++;
						retLength = objc_sizeof_type(tmptype);
											// Allocate memory to hold the
											// value we're pointing to.
						*(void**)retframe = objc_malloc (retLength);
											// We are responsible for making 
											// sure this memory gets free'd
											// eventually.  Ask NSData class to 
											// autorelease it.
						[NSData dataWithBytesNoCopy: *(void**)retframe
								length: retLength];
											// Decode the return value into the 
											// memory we allocated.
						(*decoder) (-1, *(void**)retframe, tmptype, flags, arg1, arg2, arg3);
						}
					break;

				case _C_STRUCT_B: 
				case _C_UNION_B:
				case _C_ARY_B:
						// The argument is a structure or array returned by 
						// value.  xxx Can C array's be returned by value?
					*(void**)retframe=MFRAME_GET_STRUCT_ADDR(argframe,tmptype);
						// Decode the return value into the memory we allocated
	      			(*decoder) (-1, *(void**)retframe, tmptype, flags, arg1, arg2, arg3);
	      			break;

				case _C_FLT: 
				case _C_DBL:
					(*decoder) (-1, ((char*)retframe), tmptype, flags, arg1, arg2, arg3);
					break;

				case _C_VOID:
					(*decoder) (-1, retframe, @encode(int), 0, arg1, arg2, arg3);
					break;

				default:
						// (Among other things, _C_CHARPTR is handled here).
						// Special case BOOL (and other types smaller than int)
						// because retframe doesn't actually point to the char
						// xxx What about structures smaller than int's that
						// are passed by reference on true structure reference-
						// passing architectures?  Is this the right test?
						// Use sizeof(int) instead?
					(*decoder) (-1, retframe, tmptype, flags, arg1, arg2, arg3);
				}
			}
									// Decode the values returned by reference.  
									// Note: this logic must match exactly the 
									// code in mframe_do_call(); which should 
									// decode exactly what we encode here
		if (out_parameters)
			{						// Step through all the arguments, finding 
									// the ones that were passed by reference.
			datum = method_types_get_next_argument(argframe, &tmptype);
			for (argnum = 0; datum;
					(datum =method_types_get_next_argument(argframe,&tmptype)),
					argnum++)
				{			// Get type qualifiers, like IN, OUT, INOUT, ONEWAY
				flags = objc_get_type_qualifiers(tmptype);
							// Skip over type qualifiers, so now TYPE is
							// pointing directly at the char corresponding to 
							// the arg's type, as defined in <objc/objc-api.h>
				tmptype = objc_skip_type_qualifiers(tmptype);
							// Decide how, (or whether or not), to encode the 
							// argument depending on its FLAGS and TMPTYPE.  
				if(*tmptype == _C_PTR &&((flags & _F_OUT) || !(flags & _F_IN)))
					{		// The argument is a pointer (to a non-char), and 
							// the pointer's value is qualified as an OUT 
							// parameter, or it not explicitly qualified as an 
							// IN parameter, then it is a pass-by-reference
							// argument.  The argument is a pointer to 
							// something; increment TYPE so we can see what it 
							// is a pointer to.  And then encode it.
					tmptype++;
							// FIX ME 1. Note that a (char**) is malloc'ed anew
							// here.  Yuck.  If the returned string is smaller
							// than the original, we should just put it there; 
							// if the returned string is bigger, I don't know
							// what to do.  FIX ME 2. __builtin_return can't 
							// return structures by value?
					(*decoder) (argnum, *(void**)datum, tmptype, flags, arg1, arg2, arg3);
					}
				else 
					if (*tmptype == _C_CHARPTR
							&& ((flags & _F_OUT) || !(flags & _F_IN)))
						{	// The argument is a pointer char string, and the
							// pointer's value is qualified as an OUT param, or
							// it not explicitly qualified as an IN parameter, 
							// then it is a pass-by-reference argument.  Encode 
							// it.  FIX ME Perhaps we could save time and space
							// by saving a copy of the string before the method 
							// call, and then comparing it to this string; if 
							// it didn't change, don't bother sending it back
						(*decoder) (argnum, datum, tmptype, flags, arg1, arg2, arg3);
						}
				}
			}
		(*decoder) (0, 0, 0, 0, arg1, arg2, arg3);				// Tell it we have finished.
		}
	else									// matches if (out_parameters). We
		retframe = alloca (sizeof(void*));	// are just returning void, but
											// retframe needs to point to 
	switch (*rettype)						// something or else we can crash.
		{
		case _C_CHR:
		case _C_UCHR:
			return apply_char(*(char*)retframe);
		case _C_DBL:
			return apply_double(*(double*)retframe);
		case _C_FLT:
			return apply_float(*(float*)retframe);
		case _C_SHT:
		case _C_USHT:
			return apply_short(*(short*)retframe);
		}

	if(sig)
		objc_free(sig);	// get rid of local buffer
	return retframe;							// Return the retval_t pointer 
}												// to the return value.

arglist_t
mframe_create_argframe(const char *types, void **retbuf)
{
arglist_t argframe = objc_calloc(MFRAME_ARGS_SIZE, 1);
const char*	rtype = objc_skip_type_qualifiers(types);
int	stack_argsize = atoi(objc_skip_typespec(rtype));
											// Allocate the space for variables 
	if (stack_argsize)						// passed on the stack.
		argframe->arg_ptr = objc_calloc(stack_argsize, 1);
	else
		argframe->arg_ptr = 0;

	if (*rtype == _C_STRUCT_B || *rtype == _C_UNION_B || *rtype == _C_ARY_B)
		{						// If we haven't been passed a pointer to the
								// location in which to store a returned struct
								// allocate space and return the address of the 
		if (*retbuf == 0)		// allocated space.
			*retbuf = objc_calloc(objc_sizeof_type(rtype), 1);

		MFRAME_SET_STRUCT_ADDR(argframe, rtype, *retbuf);
		}

	return argframe;
}

void
mframe_destroy_argframe(const char *types, arglist_t argframe)
{
const char*	rtype = objc_skip_type_qualifiers(types);
int stack_argsize = atoi(objc_skip_typespec(rtype));

	if (stack_argsize)
		objc_free(argframe->arg_ptr);

	objc_free(argframe);
}

BOOL
mframe_decode_return (const char *type, void* buffer, void* retframe)
{
int size = 0;

	type = objc_skip_type_qualifiers(type);
	NSGetSizeAndAlignment(type, &size, 0);

	switch (*type)
		{
		case _C_ID:
			{
			inline id retframe_id(void *f)			{ __builtin_return (f); }

			*(id*)buffer = retframe_id(retframe);
			break;
			}
		case _C_CLASS:
			{
			inline Class retframe_Class(void *f)	{ __builtin_return (f); }

			*(Class*)buffer = retframe_Class(retframe);
			break;
			}
		case _C_SEL:
			{
			inline SEL retframe_SEL(void *f)		{ __builtin_return (f);}

			*(SEL*)buffer = retframe_SEL(retframe);
			break;
			}
		case _C_CHR:
		case _C_UCHR:
			{
			inline unsigned char retframe_char(void *f)
				{
				__builtin_return (f);
				}

			*(unsigned char*)buffer = retframe_char(retframe);
			break;
			}
		case _C_SHT:
		case _C_USHT:
			{
			inline unsigned short retframe_short(void *f)
				{
				__builtin_return (f);
				}
			*(unsigned short*)buffer = retframe_short(retframe);
			break;
			}
		case _C_INT:
		case _C_UINT:
			{
			inline unsigned int retframe_int(void *f)
				{
				__builtin_return (f);
				}
			*(unsigned int*)buffer = retframe_int(retframe);
			break;
 			}
		case _C_LNG:
		case _C_ULNG:
			{
			inline unsigned long retframe_long(void *f)
				{
				__builtin_return (f);
				}
			*(unsigned long*)buffer = retframe_long(retframe);
			break;
			}
		case _C_LNG_LNG:
		case _C_ULNG_LNG:
			{
			inline unsigned long long retframe_longlong(void *f)
				{
				__builtin_return (f);
				}
			*(unsigned long long*)buffer = retframe_longlong(retframe);
			break;
			}
		case _C_FLT:
			{
			inline float retframe_float(void *f)	{ __builtin_return (f); }

			*(float*)buffer = retframe_float(retframe);
			break;
			}
		case _C_DBL:
			{
			inline double retframe_double(void *f)	{ __builtin_return (f); }

			*(double*)buffer = retframe_double(retframe);
			break;
			}
		case _C_PTR:
		case _C_ATOM:
		case _C_CHARPTR:
			{
			inline char* retframe_pointer(void *f)	{ __builtin_return (f); }

			*(char**)buffer = retframe_pointer(retframe);
			break;
			}
		case _C_ARY_B:
		case _C_STRUCT_B:
		case _C_UNION_B:
			{
			typedef struct {
				char val[size];
				} block;
			inline block retframe_block(void *f)	{ __builtin_return (f); }

			*(block*)buffer = retframe_block(retframe);
			break;
			}
		case _C_VOID:
			break;
		
		default:
			return NO;								// Unknown type
		}

	return YES;
}

void *
mframe_handle_return(const char* type, void* retval, arglist_t argframe)
{
// we better should not pass memory allocated by alloca() back to caller!!!!
//	retval_t retframe = alloca(MFRAME_RESULT_SIZE);
//	NSLog(@"called alloca()");
	static char ret[MFRAME_RESULT_SIZE];
	switch (*type)
		{
		case _C_CHR:
		case _C_UCHR:
			return apply_char(*(char*)retval);
		case _C_DBL:
			return apply_double(*(double*)retval);
		case _C_FLT:
			return apply_float(*(float*)retval);
		case _C_SHT:
		case _C_USHT:
			return apply_short(*(short*)retval);
		case _C_ARY_B:
		case _C_UNION_B:
		case _C_STRUCT_B:
			{
			int size = objc_sizeof_type(type);
#if 1
			void *dest = MFRAME_GET_STRUCT_ADDR(argframe, type);

			memcpy(dest, retval, size);
#else
			if (size > 8)
				return apply_block(*(void**)retval);

			memcpy(ret, retval, size);
#endif
			break;
			}
		default:
			memcpy(ret, retval, objc_sizeof_type(type));
		case _C_VOID:
			break;
		}

	return (retval_t) ret;
}

#endif
