/* 
   NSMethodSignature.m

   Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
   All rights reserved.

   Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>

   This file is part of libFoundation.

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.

   We disclaim all warranties with regard to this software, including all
   implied warranties of merchantability and fitness, in no event shall
   we be liable for any special, indirect or consequential damages or any
   damages whatsoever resulting from loss of use, data or profits, whether in
   an action of contract, negligence or other tortious action, arising out of
   or in connection with the use or performance of this software.
*/

#include <config.h>
#include <Foundation/common.h>
#include <Foundation/NSString.h>
#include <Foundation/NSException.h>
#include <Foundation/NSMethodSignature.h>
#include <Foundation/exceptions/GeneralExceptions.h>

#include <extensions/objc-runtime.h>

/*
 * Return the type of the nth argument. The target and selector are not
 * counted as arguments.
 */
static const char*
get_nth_argument_type_with_qualifiers(char* types, int index)
{
    const char* seltype = types;

    // skip return type
    seltype = objc_skip_argspec(seltype);

    while(index--)
        seltype = objc_skip_argspec(seltype);

    return seltype;
}

static const char*
get_nth_argument_type(char* types, int index)
{
    return objc_skip_type_qualifiers(
                get_nth_argument_type_with_qualifiers(types, index));
}

static NSString*
isolate_type(const char* types)
{
    const char* p = objc_skip_typespec(types);
    return [NSString stringWithCString:types length:(unsigned)(p - types)];
}

@implementation NSMethodSignature

+ (NSMethodSignature*)signatureWithObjCTypes:(const char*)_types
{
    NSMethodSignature* signature;
    const char* p;

    if(!_types) {
	THROW([[InvalidArgumentException new]
		setReason:@"Null types passed to signatureWithObjCTypes:"]);
	return nil;
    }

    signature = [[NSMethodSignature alloc] autorelease];

    /* Determine if _types also contains the position info. If not, determine
	a new one. This encoding is used later instead of original one. */
    p = objc_skip_typespec(_types);
    if(!isdigit(*p)) {
	CUMULATIVE_ARGS cumulative_args;
	int stack_argsize = 0;
	id encoding = [[NSMutableString new] autorelease];
	const char* retval = _types;

	/* Skip returned value. */
	_types = objc_skip_typespec(_types);

	signature->numberOfArguments = 0;

	INIT_CUMULATIVE_ARGS(cumulative_args);
	while(*_types) {
	    [encoding appendString:
		    FUNCTION_ARG_ENCODING(cumulative_args,
					isolate_type(_types),
					stack_argsize)];
	    _types = objc_skip_typespec(_types);
	    signature->numberOfArguments++;
	}
	encoding = [NSString stringWithFormat:@"%@%d%@",
				isolate_type(retval), stack_argsize, encoding];
	signature->types = Strdup([encoding cString]);
    }
    else {
	signature->types = Strdup(_types);

	/* Compute no of arguments. The first type is the return type. */
	for(signature->numberOfArguments = -1;
		    *_types; signature->numberOfArguments++)
	    _types = objc_skip_argspec(_types);
    }

    return signature;
}

- (void)dealloc
{
    Free(types);
    [super dealloc];
}

- (unsigned)hash
{
    return hashpjw(types, Strlen(types));
}

- (BOOL)isEqual:anotherSignature
{
    return [anotherSignature isKindOfClass:isa]
	    && !Strcmp(types, [anotherSignature types]);
}

- (NSArgumentInfo)argumentInfoAtIndex:(unsigned)index
{
    NSArgumentInfo argInfo;

    argInfo.type = get_nth_argument_type(types, index);
    argInfo.size = objc_sizeof_type(argInfo.type);
    argInfo.offset = Atoi(objc_skip_typespec(argInfo.type));
    return argInfo;
}

- (unsigned)frameLength
{
    return [self sizeOfStackArguments];
}

- (BOOL)isOneway
{
    return objc_get_type_qualifiers(types) & _F_ONEWAY;
}

- (unsigned)methodReturnLength
{
    int size = objc_sizeof_type(objc_skip_type_qualifiers(types));
    return size;
}

- (const char*)methodReturnType
{
    return objc_skip_type_qualifiers(types);
}

- (unsigned)numberOfArguments
{
    return numberOfArguments;
}

@end /* NSMethodSignature */


@implementation NSMethodSignature (Extensions)

- (const char*)types	{ return types; }

- (unsigned)sizeOfStackArguments
{
    const char* ptype = objc_skip_typespec(types);
    return Atoi(ptype);
}

- (unsigned)typeSpecifiersOfArgumentAt:(int)index
{
    if(index >= numberOfArguments) {
        THROW([[IndexOutOfRangeException alloc]
		initForSize:numberOfArguments index:index]);
        return 0;
    }

    return objc_get_type_qualifiers(
            get_nth_argument_type_with_qualifiers(types, index));
}

- (BOOL)isInArgumentAt:(int)index
{
    unsigned flags = [self typeSpecifiersOfArgumentAt:index];
    return !flags || (flags & _F_IN);
}

- (BOOL)isOutArgumentAt:(int)index
{
    return [self typeSpecifiersOfArgumentAt:index] & _F_OUT;
}

- (BOOL)isBycopyArgumentAt:(int)index
{
    return [self typeSpecifiersOfArgumentAt:index] & _F_BYCOPY;
}

@end /* NSMethodSignature (Extensions) */
