/** nsmethodsignature - Program to test NSMethodSignature.
   Copyright (C) 2004 Free Software Foundation, Inc.

   Written by:  David Ayers  <d.ayers@inode.at>

   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 General Public
   License as published by the Free Software Foundation; either
   version 3 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
   General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
*/

#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSMethodSignature.h>
#include <Foundation/NSRunLoop.h>
#include <Foundation/NSConnection.h>
#include <Foundation/NSException.h>
#include <Foundation/NSDebug.h>

#include <GNUstepBase/GSObjCRuntime.h>


#define SRV_NAME @"nsmethodsignaturetest"

struct _MyLargeStruct
{
  double first;
  double second;
};
typedef struct _MyLargeStruct MyLargeStruct;

struct _MySmallStruct
{
  char first;
};
typedef struct _MySmallStruct MySmallStruct;

/*------------------------------------*/
@interface MyClass : NSObject
-(void)void_void;
-(id)id_void;

-(char)char_void;
-(unsigned char)uchar_void;
-(signed char)schar_void;

-(short)short_void;
-(unsigned short)ushort_void;
-(signed short)sshort_void;

-(int)int_void;
-(unsigned int)uint_void;
-(signed int)sint_void;

-(long)long_void;
-(unsigned long)ulong_void;
-(signed long)slong_void;

-(float)float_void;
-(double)double_void;

-(MyLargeStruct)largeStruct_void;
-(MySmallStruct)smallStruct_void;



-(void)void_id:(id)_id;

-(void)void_char:(char)_char;
-(void)void_uchar:(unsigned char)_char;
-(void)void_schar:(signed char)_char;

-(void)void_short:(short)_short;
-(void)void_ushort:(unsigned short)_short;
-(void)void_sshort:(signed short)_short;

-(void)void_int:(int)_int;
-(void)void_uint:(unsigned int)_int;
-(void)void_sint:(signed int)_int;

-(void)void_long:(long)_long;
-(void)void_ulong:(unsigned long)_long;
-(void)void_slong:(signed long)_long;

-(void)void_float:(float)_float;
-(void)void_double:(double)_double;

-(void)void_largeStruct:(MyLargeStruct)_str;
-(void)void_smallStruct:(MySmallStruct)_str;

-(void)void_float:(float)_float double:(double)_double;
-(void)void_double:(double)_double float:(float)_float;

-(MyLargeStruct)largeStruct_id:(id)_id
			  char:(char)_char
			 short:(short)_short
			   int:(int)_int
			  long:(long)_long
			 float:(float)_float
			double:(double)_double
		   largeStruct:(MyLargeStruct)_lstr
		   smallStruct:(MySmallStruct)_sstr;
-(MySmallStruct)smallStruct_id:(id)_id
			 uchar:(unsigned char)_uchar
			ushort:(unsigned short)_ushort
			  uint:(unsigned int)_uint
			 ulong:(unsigned long)_ulong
			 float:(float)_float
			double:(double)_double
		   largeStruct:(MyLargeStruct)_lstr
		   smallStruct:(MySmallStruct)_sstr;

-(const char *)runtimeSignatureForSelector:(SEL)selector;
-(const char *)mframeSignatureForSelector:(SEL)selector;
@end

@implementation MyClass
-(void)void_void {}
-(id)id_void { return 0; }

-(char)char_void { return 0; }
-(unsigned char)uchar_void { return 0; }
-(signed char)schar_void { return 0; }

-(short)short_void { return 0; }
-(unsigned short)ushort_void { return 0; }
-(signed short)sshort_void { return 0; }

-(int)int_void { return 0; }
-(unsigned int)uint_void { return 0; }
-(signed int)sint_void { return 0; }

-(long)long_void { return 0; }
-(unsigned long)ulong_void { return 0; }
-(signed long)slong_void { return 0; }

-(float)float_void { return 0; }
-(double)double_void { return 0; }

-(MyLargeStruct)largeStruct_void { MyLargeStruct str; return str; }
-(MySmallStruct)smallStruct_void { MySmallStruct str; return str; }



-(void)void_id:(id)_id {}

-(void)void_char:(char)_char {}
-(void)void_uchar:(unsigned char)_char {}
-(void)void_schar:(signed char)_char {}

-(void)void_short:(short)_short {}
-(void)void_ushort:(unsigned short)_short {}
-(void)void_sshort:(signed short)_short {}

-(void)void_int:(int)_int {}
-(void)void_uint:(unsigned int)_int {}
-(void)void_sint:(signed int)_int {}

-(void)void_long:(long)_long {}
-(void)void_ulong:(unsigned long)_long {}
-(void)void_slong:(signed long)_long {}

-(void)void_float:(float)_float {}
-(void)void_double:(double)_double {}

-(void)void_largeStruct:(MyLargeStruct)_str {}
-(void)void_smallStruct:(MySmallStruct)_str {}

-(void)void_float:(float)_float double:(double)_double {}
-(void)void_double:(double)_double float:(float)_float {}


-(MyLargeStruct)largeStruct_id:(id)_id
			  char:(char)_char
			 short:(short)_short
			   int:(int)_int
			  long:(long)_long
			 float:(float)_float
			double:(double)_double
		   largeStruct:(MyLargeStruct)_lstr
		   smallStruct:(MySmallStruct)_sstr { return _lstr; }

-(MySmallStruct)smallStruct_id:(id)_id
			 uchar:(unsigned char)_uchar
			ushort:(unsigned short)_ushort
			  uint:(unsigned int)_uint
			 ulong:(unsigned long)_ulong
			 float:(float)_float
			double:(double)_double
		   largeStruct:(MyLargeStruct)_lstr
		   smallStruct:(MySmallStruct)_sstr { return _sstr; }

-(const char *)runtimeSignatureForSelector:(SEL)selector
{
  GSMethod meth = GSGetMethod(isa, selector, YES, YES);
  return meth->method_types;
}

-(const char *)mframeSignatureForSelector:(SEL)selector
{
  const char *types = [self runtimeSignatureForSelector: selector];
  NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes: types];
  return [sig methodType];
}
@end

/*------------------------------------*/

int failed = 0;

void
test_mframe_build_signature(void)
{
  const char *mf_types;
  void *it = 0;
  GSMethod meth;
  GSMethodList list;
  Class cls = [MyClass class];
  NSMethodSignature *sig;
  unsigned int i;

  for (it = 0, list = class_nextMethodList(cls, &it);
       list != 0;
       list = class_nextMethodList(cls, &it))
    {
      id pool = [NSAutoreleasePool new];

      for (i = 0; i < list->method_count; i++)
	{
	  meth = &list->method_list[i];
	  sig = [NSMethodSignature signatureWithObjCTypes: meth->method_types];
	  mf_types = [sig methodType];
	  if (strcmp(meth->method_types, mf_types))
	    {
	      NSLog(@"sel: %s\nrts:%s\nmfs:%s",
		    GSNameFromSelector(meth->method_name),
		    meth->method_types, mf_types);
	      failed = 1;
	    }
	}

      [pool release];
    }

}

/*
   This test is useful if the nsmethodsignatureserver is running which
   was compiled with either a different GNUstep-base version or a different
   version of gcc.  It the server isn't found the test is skipped.
*/
void
test_compare_server_signature(void)
{
  id objct = [MyClass new];
  id proxy = [NSConnection rootProxyForConnectionWithRegisteredName: SRV_NAME
			   host: @"*"];
  if (proxy)
    {
      const char *rmtSig;
      const char *lclSig;

#define TEST_SEL(SELNAME) { \
      lclSig = [objct runtimeSignatureForSelector: @selector(SELNAME)]; \
      rmtSig = [proxy runtimeSignatureForSelector: @selector(SELNAME)]; \
      if (!GSSelectorTypesMatch(lclSig, rmtSig)) \
        NSLog(@"runtime: sel:%s\nlcl:%s\nrmt:%s", \
	      GSNameFromSelector(@selector(SELNAME)), \
	      lclSig, rmtSig); \
      lclSig = [objct mframeSignatureForSelector: @selector(SELNAME)]; \
      rmtSig = [proxy mframeSignatureForSelector: @selector(SELNAME)]; \
      if (!GSSelectorTypesMatch(lclSig, rmtSig)) \
        NSLog(@"mframe : sel:%s\nlcl:%s\nrmt:%s", \
	      GSNameFromSelector(@selector(SELNAME)), \
	      lclSig, rmtSig); \
      }

      TEST_SEL(void_void);
      TEST_SEL(id_void);
      TEST_SEL(char_void);
      TEST_SEL(uchar_void);
      TEST_SEL(schar_void);
      TEST_SEL(short_void);
      TEST_SEL(ushort_void);
      TEST_SEL(sshort_void);
      TEST_SEL(int_void);
      TEST_SEL(uint_void);
      TEST_SEL(sint_void);
      TEST_SEL(long_void);
      TEST_SEL(ulong_void);
      TEST_SEL(slong_void);
      TEST_SEL(float_void);
      TEST_SEL(double_void);
      TEST_SEL(largeStruct_void);
      TEST_SEL(smallStruct_void);

      TEST_SEL(void_id:);
      TEST_SEL(void_char:);
      TEST_SEL(void_uchar:);
      TEST_SEL(void_schar:);
      TEST_SEL(void_short:);
      TEST_SEL(void_ushort:);
      TEST_SEL(void_sshort:);
      TEST_SEL(void_int:);
      TEST_SEL(void_uint:);
      TEST_SEL(void_sint:);
      TEST_SEL(void_long:);
      TEST_SEL(void_ulong:);
      TEST_SEL(void_slong:);
      TEST_SEL(void_float:);
      TEST_SEL(void_double:);
      TEST_SEL(void_largeStruct:);
      TEST_SEL(void_smallStruct:);
      TEST_SEL(void_float:double:);
      TEST_SEL(void_double:float:);
      TEST_SEL(largeStruct_id:char:short:int:long:float:double:largeStruct:smallStruct:);
      TEST_SEL(smallStruct_id:uchar:ushort:uint:ulong:float:double:largeStruct:smallStruct:);

    }
  else
    {
      NSLog(@"Skipping test_compare_server_signature: proxy not found.");
    }
}

void
test_GSSelectorTypesMatch(void)
{
  const char *pairs[][2] = { {"@@::", "@12@0:4:8"},
			     {"@@::", "@12@+0:+4:+8"},
			     {"@@::", "@12@-0:-4:-8"},
			     {"@12@0:4:8", "@@::"},
			     {"@12@+0:+4:+8", "@@::"},
			     {"@12@-0:-4:-8", "@@::"},

			     {"@12@0:4:8", "@12@+0:+4:+8"},
			     {"@12@0:4:8", "@12@-0:-4:-8"},
			     {"@12@+0:+4:+8", "@12@0:4:8"},
			     {"@12@-0:-4:-8", "@12@0:4:8"},

			     {"@12@0:4:8", "@16@+4:+8:+12"},
			     {"@12@0:4:8", "@16@-4:-8:-12"},
			     {"@12@+0:+4:+8", "@16@4:8:12"},
			     {"@12@-0:-4:-8", "@16@4:8:12"},

			     {"{_MyLargeStruct2={_MyLargeStruct=dd}dd}@:",
			      "{??={??=dd}dd}16@0:4"},

			     {"{_MyLargeStruct=dd}56@+8:+12@+16c+23s+26i+28l24f28d32{_MyLargeStruct=dd}40{_MySmallStruct=c}44",
			      "{_MyLargeStruct=dd}46@+8:+12@+16c+17s+16i+20l+24f+28d24{_MyLargeStruct=dd}32{_MySmallStruct=c}45"},
			     {"{_MyLargeStruct=dd}56@+8:+12@+16c+23s+26i+28l24f28d32{_MyLargeStruct=dd}40{_MySmallStruct=c}44",
			      "{??=dd}46@+8:+12@+16c+17s+16i+20l+24f+28d24{??=dd}32{??=c}45"},
			     {0, 0} };
  unsigned int i = 0;

  while (pairs[i][0])
    {
      if (GSSelectorTypesMatch(pairs[i][0], pairs[i][1]) == NO)
	{
	  NSLog(@"pair %d does not match:\n%s\n%s",
		i, pairs[i][0], pairs[i][1]);
	  failed = 1;
	}
      i++;
    }
}

void
run_server(void)
{
  id obj = [MyClass new];
  NSConnection *conn = [NSConnection defaultConnection];

  [conn setRootObject: obj];
  if ([conn registerName: SRV_NAME] == NO)
    {
      NSLog(@"Failed to register name: " SRV_NAME );
      abort();
    }
  [[NSRunLoop currentRunLoop] run];
}

int
main(int argc, char *argv[])
{
  NSAutoreleasePool *pool;
  pool = [[NSAutoreleasePool alloc] init];

  if ([[[[NSProcessInfo processInfo] arguments] lastObject] isEqual: @"srv"])
    {
      run_server();
      abort();
    }

  NS_DURING
    {
      test_mframe_build_signature();
      test_compare_server_signature();
      test_GSSelectorTypesMatch();
      if (failed)
	[NSException raise: NSInternalInconsistencyException
		     format: @"discrepancies between gcc/mframe signatures"];

      NSLog(@"MethodSignature Test Succeeded.");
    }
  NS_HANDLER
    {
      NSLog(@"MethodSignature Test Failed:");
      NSLog(@"%@ %@ %@",
	    [localException name],
	    [localException reason],
	    [localException userInfo]);
    }
  NS_ENDHANDLER

  [pool release];

  exit(0);
}

