/* Copyright 2004 (C) George Peter Staplin
 * C with automatic self passing via machine code generated on the fly.
 * Revision 3
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/mman.h>

/* x86 op codes */
/*
#define PUSH_4_BYTES_OP 0x68
#define PUSH_LONG_OP PUSH_4_BYTES_OP
#define MOVE_LONG_TO_EAX 0xb8

#define CALL_EAX_A 0xff 
#define CALL_EAX_B 0xd0
#define RET 0xc3

#define ADD_8_ESP_A 0x83
#define ADD_8_ESP_B 0xc4
#define ADD_8_ESP_C 0x08

#define PUSH_4ESP_A 0xff
#define PUSH_4ESP_B 0x74
#define PUSH_4ESP_C 0x24
#define PUSH_4ESP_D 0x04
*/

unsigned char push_long[] = { 0x68 };
unsigned char move_long_to_eax[] = { 0xb8 };
unsigned char call_eax[] = { 0xff, 0xd0 };
unsigned char ret[] = { 0xc3 };
unsigned char add_8_esp[] = { 0x83, 0xc4, 0x08 };
unsigned char push_4_esp[] = { 0xff, 0x74, 0x24, 0x04 };
unsigned char nop = 0x90;


char *align (char *p) {
 return (char *) ((unsigned long)(p + 3) & (~3L));
}


void *alloc_exec_mem (size_t s) {
 static char *pool;
 static size_t amount_left;
 void *r;

 if ((NULL == pool) || (s > amount_left)) {
  pool = mmap (NULL, s + 2000, 
   (PROT_EXEC | PROT_READ | PROT_WRITE), (MAP_ANON | MAP_PRIVATE), -1, 0);
 
  if (MAP_FAILED == r) {
   perror ("unable to map memory");
   exit (EXIT_FAILURE);
  }
  amount_left = 2000;
  r = pool;
  pool += s;
 } else {
  r = pool;
  pool += s;
  amount_left -= s;
 }

 return r;
}

void *check_alloc (size_t s) {
 void *mem = malloc (s);

 if (NULL == mem) {
  perror ("unable to malloc");
  exit (EXIT_FAILURE);
 }
 return mem;
}

void dump_bytes ( FILE *fp, unsigned char *b, size_t s) {
 size_t i;
 fprintf (fp, "BEGIN DUMP\n");
 for (i = 0; i < s; ++i) {
  fprintf (fp, "[%d] = 0x%x = %d", i, b[i], b[i]);
  if (isprint(b[i]))
   fprintf (fp, " = %c", b[i]);
  fprintf (fp, "\n");
 }
 fprintf (fp, "END DUMP\n");
}

void pad_with_NOP (unsigned char **exep, int offset) {
 unsigned char *p;

 p = align ((*exep) + offset);

/*
 fprintf (stderr, "pad_with_NOP p %p *exep %p\n", p, *exep);
*/

 while (*exep < p) {
  **exep = nop; 
  *exep += 1;
 }
 
 /* i is our initial offset */
 *exep -= offset;
}

void *GenerateMethodCall (void *self, void *func) {
 unsigned char *exe;
 unsigned char *head;
 size_t s;

 s = sizeof (push_4_esp) + sizeof (push_long) + sizeof (long *) + 
  sizeof (move_long_to_eax) + sizeof (long *) + sizeof (call_eax) + 
  sizeof (add_8_esp) + sizeof (ret) + 4;

 printf ("GMC %u\n", s);


 exe = alloc_exec_mem (50);

 head = exe;

 /* Generate push of the argument to the method */

 memcpy (exe, push_4_esp, sizeof (push_4_esp));
 exe += sizeof (push_4_esp);

 pad_with_NOP (&exe, sizeof (push_long));

 /* Generate push of self self */
 memcpy (exe, push_long, sizeof (push_long));
 exe += sizeof (push_long);

 *(unsigned long **)exe = self;
 exe += sizeof (long *);

 /* Setup for a call to func */
 memcpy (exe, move_long_to_eax, sizeof (move_long_to_eax));
 exe += sizeof (move_long_to_eax);

 *(unsigned long **)exe = func;
 exe += sizeof (long *);
 
 /* call *%eax */
 memcpy (exe, call_eax, sizeof (call_eax));
 exe += sizeof (call_eax);

 /* Cleanup our stack */
 memcpy (exe, add_8_esp, sizeof (add_8_esp));
 exe += sizeof (add_8_esp);

 memcpy (exe, ret, sizeof (ret));

 /*no need to advance exe, as this is the end*/

 fprintf (stderr, "cur %p end %p\n", (exe + 1), (head + s));

 return head;
}

typedef struct MyClass {
 char *msg;
 int val;
 void (*set_msg) (char *msg);
 char *(*get_msg) (char *msg);
} MyClass;


void MyClass_set_msg (MyClass *self, char *msg) {
 printf ("set_msg to %p is %s\n", self, msg);
 self->msg = msg;
}

char *MyClass_get_msg (MyClass *self, void *p) {
 printf ("get_msg to %p\n", self);
 return self->msg;
}

MyClass *MyClass_new (void) {
 MyClass *obj = check_alloc (sizeof (MyClass));

 obj->msg = NULL;
 obj->val = 0;
 
 obj->get_msg = GenerateMethodCall (obj, &MyClass_get_msg);
 obj->set_msg = GenerateMethodCall (obj, &MyClass_set_msg);

 return obj;
}

int main () { 
 MyClass *obj;
 char *p = "Hello";

 if (NULL == p) {
  s:
   asm ("nop");
  e:
  return EXIT_FAILURE;
 }
 //dump_bytes (stderr, &&s, (&&e - &&s));
 
 obj = MyClass_new ();

 fprintf (stderr, "obj %p parameter %p\n", obj, p);

 obj->set_msg (p);

 printf ("obj->get_msg %s\n", obj->get_msg (NULL));


 /* Being able to do something like this would be cool:

 ball = Ball_new ();

 ball->("roll", 4, "inflate", 15);

 Those would be dynamic methods.  We could replace them in our object at any time during runtime via dlopen/dlsym.  We can use a hash table for lookup.
 
 ball->("learn", list ("inflate", inflate_method));

 How would list be implemented?  How would its memory be managed?
 */

 return EXIT_SUCCESS;
}
