/* Copyright 2004 (C) George Peter Staplin
 * C with automatic self passing via machine code generated on the fly.
 */
#include <stdio.h>
#include <stdlib.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

void *alloc_exec_mem (size_t s) {
 void *r;

 r = mmap (NULL, s, (PROT_EXEC | PROT_READ | PROT_WRITE), (MAP_ANON | MAP_PRIVATE), -1, 0);
 if (MAP_FAILED == r) {
  perror ("unable to map memory");
  exit (EXIT_FAILURE);
 }
 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 *GenerateMethodCall (void *self, void *func) {
 unsigned char *exe = check_alloc (50);
 unsigned char *head;

 head = exe;

 /* Generate push of the argument to the method */
 *exe = PUSH_4ESP_A;
 exe++;
 *exe = PUSH_4ESP_B;
 exe++;
 *exe = PUSH_4ESP_C;
 exe++;
 *exe = PUSH_4ESP_D;
 exe++;

 /* Generate push of self self */
 *exe = PUSH_LONG_OP;
 exe++;
 *(unsigned long **)exe = self;
 exe += sizeof (long *);

 /* Setup for a call to func */
 *exe = MOVE_LONG_TO_EAX;
 exe++;
 *(unsigned long **)exe = func;
 exe += sizeof (long *);
 
 /* call *%eax */
 *exe = CALL_EAX_A;
 exe++;
 *exe = CALL_EAX_B;
 exe++;

 /* Cleanup our stack */
 *exe = ADD_8_ESP_A;
 exe++;
 *exe = ADD_8_ESP_B;
 exe++;
 *exe = ADD_8_ESP_C;
 exe++;

 *exe = RET;
 exe++;

 return head;
}

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


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

void MyClass_get_msg (MyClass *self, void *p) {
 printf ("get msg in %p is %s\n", self, 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";

 obj = MyClass_new ();

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

 obj->set_msg (p); obj->get_msg (NULL);

 return EXIT_SUCCESS;
}
