/**
 * YARV: Yet Another Ruby VM.
 *
 *
 * $Id: yarvcore.c 150 2005-03-01 04:54:42Z ko1 $
 * Create : K.S. 04/01/01 01:17:22
 *
 * Copyright (c) 2004 SASADA Koichi <ko1 at atdot.net>
 */

#include <ruby.h>
#include <node.h>

#include "yarvcore.h"

VALUE mYarvCore;
VALUE cYarvISeq;
VALUE cYarvInsn;
VALUE cYarvLabel;
VALUE cYarvVM;
VALUE cYarvThread;
VALUE mYarvInsns;
VALUE cYarvThrowObject;
VALUE cYarvEnv;
VALUE cYarvProc;

VALUE symIFUNC;
VALUE symCFUNC;

ID idPLUS;
ID idMINUS;
ID idLT;
ID idLTLT;
ID idEqq;
ID idBackquote;
ID idEqTilde;
ID idThrowState;
ID idThrowBackDFP;
ID idThrowObject;
ID idAREF;
ID idASET;
ID idIntern;
ID idMethodMissing;


#define MARK_UNLESS_NULL(ptr) if(ptr){rb_gc_mark(ptr);}
#define FREE_UNLESS_NULL(ptr) if(ptr){ruby_xfree(ptr);}


unsigned long yarvGlobalStateVersion = 1;


/* from Ruby 1.9 eval.c */
#ifdef HAVE_STDARG_PROTOTYPES
#include <stdarg.h>
#define va_init_list(a,b) va_start(a,b)
#else
#include <varargs.h>
#define va_init_list(a,b) va_start(a)
#endif


/************/
/* YARVCore */
/************/

/* temporalily */
static VALUE yarvCurrentRunningVM     = Qnil;
static VALUE yarvCurrentRunningThread = Qnil;

// void yarv_setup(void *p1, void *p2, void *p3);

VALUE yarv_get_current_running_vm(){
  return yarvCurrentRunningVM;
}

VALUE yarv_get_current_running_thread(){
  return yarvCurrentRunningThread;
}

/* rb_block_given */
int yarv_block_given_p(){
  VALUE th = yarv_get_current_running_thread();
  struct thread_object *thobj;

  GetThreadVal(th, thobj);

  if(thobj->lfp[0]){
    return 1;
  }
  else{
    return 0;
  }
}

VALUE thread_invoke_yield(VALUE self, int argc, VALUE *argv);

/* rb_yield_values */
VALUE yarv_yield_values(int argc, VALUE *argv){
  VALUE th;

  th = yarv_get_current_running_thread();
  return thread_invoke_yield(th, argc, argv);
}

VALUE thread_call0(VALUE self, VALUE klass, VALUE recv, VALUE id, ID oid,
                   int argc, VALUE *argv, NODE *body, int nosuper);

/* rb_call0 continued for yarv function */
VALUE yarv_call0(VALUE klass, VALUE recv, VALUE id, ID oid,
                 int argc, VALUE *argv, NODE *body, int nosuper){
  VALUE th   = yarv_get_current_running_thread();
  return thread_call0(th, klass, recv, id, oid, argc, argv, body, nosuper);
}


VALUE thread_call_super(VALUE self, int argc, const VALUE *argv);

VALUE yarv_call_super(int argc, const VALUE *argv){
  VALUE th   = yarv_get_current_running_thread();
  return thread_call_super(th, argc, argv);
}

VALUE thread_backtrace(VALUE th, int level);

VALUE yarv_backtrace(int level){
  VALUE th = yarv_get_current_running_thread();
  return thread_backtrace(th, level);
}

VALUE yarv_caller(VALUE self, VALUE level){
  if(!IS_YARV_WORKING()){
    return rb_funcall(Qnil, rb_intern("caller"), 1, level);
  }
  else{
    return yarv_backtrace(FIX2INT(level));
  }
}

RUBY_EXTERN int rb_thread_critical;
RUBY_EXTERN int ruby_in_eval;
RUBY_EXTERN int ruby_nerrs;
RUBY_EXTERN NODE* ruby_eval_tree;

static VALUE vm_eval(VALUE self, VALUE iseq);

VALUE yarv_load(char *file){
  NODE *node;
  VALUE argv[5];
  volatile VALUE iseq;
  volatile int critical;
  
  critical = rb_thread_critical;
  rb_thread_critical = Qtrue;
  {
    ruby_in_eval++;
    rb_load_file(file);
    ruby_in_eval--;
    node = ruby_eval_tree;
  }
  rb_thread_critical = critical;

  if(ruby_nerrs > 0){
    return 0;
  }
  
  argv[0] = (VALUE)node;
  argv[1] = rb_str_new2("top");
  argv[2] = rb_str_new2(file);
  argv[3] = Qfalse;
  argv[4] = ISEQ_TYPE_TOP;
  iseq = rb_class_new_instance(5, argv, cYarvISeq);

  vm_eval(yarv_get_current_running_vm(), iseq);
  return 0;
}


VALUE *thread_svar(VALUE self, int cnt);
VALUE *yarv_svar(int cnt){
  return thread_svar(yarv_get_current_running_thread(), cnt);
}

static int yarv_iterate(NODE *node){
  VALUE th   = yarv_get_current_running_thread();
  struct thread_object *thobj;
  GetThreadVal(th, thobj);
  
  thobj->block_ptr = node;
  return 0;
}

static VALUE compile_string(VALUE str, VALUE file, VALUE line){
  NODE *node;
  node = rb_compile_string(StringValueCStr(file), str, NUM2INT(line));
  
  if(ruby_nerrs > 0){
    VALUE str;
    ruby_nerrs = 0;
    rb_exc_raise(ruby_errinfo);
  }

  return (VALUE)node;
}

static VALUE yarvcore_eval_parsed(VALUE self, VALUE iseq){
  volatile VALUE vm;
  VALUE ret;
  vm = rb_class_new_instance(0, 0, cYarvVM);
  
  yarvCurrentRunningVM = vm;
  SET_YARV_START();
  ret = vm_eval(vm, iseq);
  SET_YARV_STOP();
  return ret;
}

static VALUE yarvcore_eval(VALUE self, VALUE str, VALUE file, VALUE line){
  VALUE node = compile_string(str, file, line);
  VALUE argv[5];
  volatile VALUE iseq, vm;
  VALUE ret;
  
  argv[0] = node;
  argv[1] = rb_str_new2("main");
  argv[2] = file;
  argv[3] = Qfalse;
  argv[4] = ISEQ_TYPE_TOP;
  iseq = rb_class_new_instance(5, argv, cYarvISeq);

  return yarvcore_eval_parsed(self, iseq);
}

static VALUE yarvcore_parse(VALUE self, VALUE str, VALUE file, VALUE line){
  VALUE node = compile_string(str, file, line);
  VALUE argv[5];
  VALUE iseq;
  
  argv[0] = node;
  argv[1] = rb_str_new2("main");
  argv[2] = file;
  argv[3] = Qfalse;
  argv[4] = ISEQ_TYPE_TOP;
  iseq = rb_class_new_instance(5, argv, cYarvISeq);
  return iseq;
}

static void yarv_free(void *ptr){
  if(ptr){
    ruby_xfree(ptr);
  }
}

VALUE iseq_jitcompile(VALUE iseq);
static VALUE yarv_jitcompile(VALUE self, VALUE obj, VALUE selector){
  if(IS_YARV_WORKING()){
    ID id;
    NODE *mn;
    VALUE klass;
    klass = CLASS_OF(obj);
    id = rb_to_id(selector);
    mn = rb_method_node(klass, id);
    if(mn == 0 ||
       nd_type(mn) != YARV_METHOD_NODE){
      dp(obj);
      dp(selector);
      
      rb_bug("This method is not YARV method");
      /* unreachable */
    }

    /* JIT Compile */
    return iseq_jitcompile((VALUE)mn->nd_body);
  }
  else{
    return Qnil;
  }
}

/***********************/
/* InstructionSequence */
/***********************/

static void iseq_free(void *ptr){
  struct iseq_object *iseqobj;
  
  if(ptr){
    iseqobj = ptr;
    FREE_UNLESS_NULL(iseqobj->iseq);
#ifdef DISPATCH_DIRECT_THREADED_CODE
    FREE_UNLESS_NULL(iseqobj->iseq_dt);
#endif
    FREE_UNLESS_NULL(iseqobj->insn_info_tbl);
    FREE_UNLESS_NULL(iseqobj->local_tbl);
    FREE_UNLESS_NULL(iseqobj->catch_table);
    FREE_UNLESS_NULL(iseqobj->arg_opt_tbl);
    FREE_UNLESS_NULL(iseqobj->compile_data);
    ruby_xfree(ptr);
  }
}

static void iseq_mark(void *ptr){
  struct iseq_object *iseqobj;
  if(ptr){
    iseqobj = ptr;
    MARK_UNLESS_NULL(iseqobj->iseq_mark_ary);
    MARK_UNLESS_NULL(iseqobj->name);
    MARK_UNLESS_NULL(iseqobj->file_name);
    MARK_UNLESS_NULL(iseqobj->klass_nest_stack);
    MARK_UNLESS_NULL(iseqobj->catch_table_ary);
    MARK_UNLESS_NULL(iseqobj->insns_ary);
    
    if(iseqobj->compile_data != 0){
      MARK_UNLESS_NULL(iseqobj->compile_data->current_block);
      MARK_UNLESS_NULL(iseqobj->compile_data->start_label);
      MARK_UNLESS_NULL(iseqobj->compile_data->end_label);
      MARK_UNLESS_NULL(iseqobj->compile_data->redo_label);
      MARK_UNLESS_NULL(iseqobj->compile_data->err_info);
      MARK_UNLESS_NULL(iseqobj->compile_data->compile_data);
    }
  }
}

static VALUE iseq_alloc(VALUE klass){
  VALUE volatile obj;
  struct iseq_object *iseqobj;

  obj = Data_Make_Struct(klass, struct iseq_object,
                         iseq_mark, iseq_free, iseqobj);
  return obj;
}

static VALUE iseq_init(VALUE self, VALUE node, VALUE name, VALUE file_name,
                       VALUE parent, VALUE type){
  struct iseq_object *iseqobj;
  VALUE  err;
  
  GetISeqVal(self, iseqobj);
  iseqobj->name            = name;
  iseqobj->file_name       = file_name;
  iseqobj->iseq_mark_ary   = rb_ary_new();
  iseqobj->catch_table_ary = rb_ary_new();
  iseqobj->type            = type;
  iseqobj->arg_rest        = -1;
  iseqobj->arg_block       = -1;

  iseqobj->compile_data    = ALLOC_N(struct iseq_compile_data, 1);
  MEMZERO(iseqobj->compile_data, struct iseq_compile_data, 1);
  
  if(parent && CLASS_OF(parent) == cYarvISeq){
    struct iseq_object *piseqobj;
    GetISeqVal(parent, piseqobj);
    iseqobj->parent_iseqobj = piseqobj;
  }
  
  iseq_compile(self, node);
  
  err = iseqobj->compile_data->err_info;
  
  ruby_xfree(iseqobj->compile_data);
  iseqobj->compile_data = 0;

  if(ruby_nerrs > 0){
    VALUE str;
    ruby_nerrs = 0;
    rb_exc_raise(err);
  }
  
  return self;
}

VALUE iseq_assemble_setup(VALUE self, VALUE args, VALUE locals, VALUE ary);

static VALUE iseq_assemble(VALUE self, VALUE args, VALUE locals, VALUE insn_ary){
  return iseq_assemble_setup(self, args, locals, insn_ary);
}

VALUE iseq_inspect(VALUE self){
  char   buff[0x100];
  struct iseq_object *iseqobj;
  
  GetISeqVal(self, iseqobj);
  snprintf(buff, sizeof(buff), "<ISeq:%s@%s>",
           RSTRING(iseqobj->name)->ptr,
           RSTRING(iseqobj->file_name)->ptr);

  return rb_str_new2(buff);
}

VALUE iseq_insns(VALUE self){
  struct iseq_object *iseqobj;
  GetISeqVal(self, iseqobj);
  
  return iseqobj->insns_ary;
}


/*********/
/* Label */
/*********/

static VALUE label_alloc(VALUE klass){
  VALUE obj;
  struct label_object *labelobj;

  obj = Data_Make_Struct(klass, struct label_object, 0, yarv_free, labelobj);
  return obj;
}

static VALUE label_init(VALUE self, VALUE label_no){
  struct label_object *labelobj;
  Data_Get_Struct(self, struct label_object, labelobj);
  labelobj->label_no = label_no;
  labelobj->sc_state = 0;
  return self;
}

static VALUE label_to_s(VALUE self){
  VALUE str;
  struct label_object *lobj;
  char  buff[0x100];

  GetLabelVal(self, lobj);
  snprintf(buff, sizeof(buff), "<L%03d>", lobj->label_no);
  return rb_str_new2(buff);
}

static VALUE label_position(VALUE self){
  struct label_object *lobj;
  GetLabelVal(self, lobj);

  return INT2FIX(lobj->position);
}

/***************/
/* Instruction */
/***************/

static void insn_mark(void *ptr){
  struct insn_object *insnobj;
  if(ptr){
    insnobj = ptr;
    if(insnobj->operands){
      rb_gc_mark(insnobj->operands);
    }
  }
}

static VALUE insn_alloc(VALUE klass){
  VALUE obj;
  struct insn_object *insnobj;

  obj = Data_Make_Struct(klass, struct insn_object, insn_mark, yarv_free, insnobj);
  return obj;
}

static VALUE insn_init(VALUE self, int insn_id, int line_no, VALUE operands){
  struct insn_object *insnobj;
  Data_Get_Struct(self, struct insn_object, insnobj);
  insnobj->insn_id  = insn_id;
  insnobj->line_no  = line_no;
  insnobj->operands = operands; /* 0 or RArray */
  insnobj->sc_state = 0;
  return self;
}

static VALUE insn_make(VALUE self, VALUE insn_id, VALUE line_no, VALUE operands){
  VALUE args[3];
  args[0] = FIX2INT(insn_id);
  args[1] = FIX2INT(line_no);
  args[2] = RARRAY(operands)->len == 0 ? 0 : operands;

  return rb_class_new_instance(3, args, cYarvInsn);
}

static VALUE insn_to_s(VALUE self){
  VALUE str;
  struct insn_object *insnobj;
  char  buff[0x100];
  
  GetInsnVal(self, insnobj);
  snprintf(buff, sizeof(buff),
           "<Insn:%s (%d)>", insn_name(insnobj->insn_id), insnobj->line_no);
  
  return rb_str_new2(buff);
}


static VALUE insn_insn_id(VALUE self){
  struct insn_object *insnobj;
  GetInsnVal(self, insnobj);
  
  return INT2FIX(insnobj->insn_id);
}

static VALUE insn_line_no(VALUE self){
  struct insn_object *insnobj;
  GetInsnVal(self, insnobj);
  return INT2FIX(insnobj->line_no);
}

static VALUE insn_operands(VALUE self){
  struct insn_object *insnobj;
  GetInsnVal(self, insnobj);
  return insnobj->operands;
}


/******/
/* VM */
/******/

static void vm_free(void *ptr){
  if(ptr){
    ruby_xfree(ptr);
  }
}

static void vm_mark(void *ptr){
  struct vm_object *vmobj;
  if(ptr){
    vmobj = ptr;
    rb_gc_mark(vmobj->main_thread);
  }
}

static VALUE vm_alloc(VALUE klass){
  VALUE volatile obj;
  struct vm_object *vmobj;
  obj = Data_Make_Struct(klass, struct vm_object, vm_mark, vm_free, vmobj);
  return obj;
}

static VALUE vm_init(VALUE self, VALUE node){
  /* allocate vm stack */
  struct vm_object *vmobj;
  VALUE argv[1];
  GetVMVal(self, vmobj);

  argv[0] = self;
  vmobj->main_thread = rb_class_new_instance(1, argv, cYarvThread);
  return self;
}


static VALUE thread_eval(VALUE self, VALUE iseq);

static VALUE vm_eval(VALUE self, VALUE iseq){
  struct vm_object *vm;
  VALUE ret;
  GetVMVal(self, vm);
  return thread_eval(vm->main_thread, iseq);
}


/**********/
/* Thread */
/**********/

static void thread_free(void *ptr){
  struct thread_object *thobj;
  if(ptr){
    thobj = ptr;
    if(thobj->stack){
      ruby_xfree(thobj->stack);
    }
    ruby_xfree(ptr);
  }
}

static void thread_mark(void *ptr){
  struct thread_object *thobj;
  if(ptr){
    thobj = ptr;
    if(thobj->stack){
      rb_gc_mark_locations(thobj->stack, thobj->stack_mark_pointer);
    }
    /* mark ruby objects */
    if(thobj->vm){
      rb_gc_mark(thobj->vm);
    }
    if(thobj->stat_insn_usage){
      rb_gc_mark(thobj->stat_insn_usage);
    }
    if(thobj->klass_nest_stack){
      rb_gc_mark(thobj->klass_nest_stack);
    }
  }
}

static VALUE thread_alloc(VALUE klass){
  VALUE volatile obj;
  struct thread_object *thobj;

  obj = Data_Make_Struct(klass, struct thread_object,
                         thread_mark, thread_free, thobj);
  return obj;
}

extern VALUE thread_set_top_stack(VALUE self, VALUE iseq);

static VALUE thread_init(VALUE self, VALUE vm){
  struct thread_object *thobj;
  GetThreadVal(self, thobj);

  /* allocate thread stack */
  thobj->stack      = ALLOC_N(VALUE, YARV_THREAD_STACK_SIZE);
  thobj->stack_size = YARV_THREAD_STACK_SIZE;
  thobj->stack_mark_pointer = thobj->stack + 0x20;
  thobj->klass_nest_stack = rb_ary_push(rb_ary_new(), rb_cObject);
  
  
  /* initialize registers */
  thobj->sp  = thobj->stack;
  thobj->lfp = 0;
  thobj->dfp = 0;
  thobj->cfp = 0;
  thobj->pc  = 0;

  thobj->vm  = vm;
  return self;
}

static VALUE thread_eval(VALUE self, VALUE iseq){
  int err;
  VALUE ret;
  struct thread_object *thobj;
  GetThreadVal(self, thobj);

  yarvCurrentRunningThread = self;
  thread_set_top_stack(self, iseq);
  ret = thread_eval_body(self);

  return ret;
}


/***************/
/* YarvEnv     */
/***************/

static void env_free(void *ptr){
  struct vm_env_object *envobj;
  if(ptr){
    envobj = ptr;
    if(envobj->env){
      ruby_xfree(envobj->env);
    }
    ruby_xfree(ptr);
  }
}

static void env_mark(void *ptr){
  struct vm_env_object *envobj;
  if(ptr){
    envobj = ptr;
    if(envobj->env){
      /* ToDo: should mark more restricted range */
      rb_gc_mark_locations(envobj->env, envobj->env + envobj->env_size);
    }
    if(envobj->prev_envobj){
      rb_gc_mark(envobj->prev_envobj);
    }
  }
}

static VALUE env_alloc(VALUE klass){
  VALUE obj;
  struct vm_env_object *envobj;
  obj = Data_Make_Struct(klass, struct vm_env_object,
                         env_mark, env_free, envobj);
  return obj;
}


/***************/
/* YarvProc    */
/***************/

static void proc_free(void *ptr){
 if(ptr){
    ruby_xfree(ptr);
  }
}

static void proc_mark(void *ptr){
  struct vm_proc_object *procobj;
  if(ptr){
    procobj = ptr;
    if(procobj->envobj){
      rb_gc_mark(procobj->envobj);
    }
    if(procobj->blockobj){
      rb_gc_mark(procobj->blockobj);
    }
  }
}

static VALUE proc_alloc(VALUE klass){
  VALUE obj;
  struct vm_proc_object *procobj;
  obj = Data_Make_Struct(klass, struct vm_proc_object,
                         proc_mark, proc_free, procobj);
  return obj;
}

static VALUE proc_call(int argc, VALUE *argv, VALUE proc){
  VALUE th = yarv_get_current_running_thread();
  return thread_invoke_proc_call(th, proc, argc, argv);
}


/********************************************************************/

static VALUE yarv_once(){
  return rb_yield(Qnil);
}
static VALUE cfunc(){
  rb_funcall(Qnil, rb_intern("rfunc"), 0, 0);
  rb_funcall(Qnil, rb_intern("rfunc"), 0, 0);
  return Qnil;
}


VALUE constant_pool_pool;

VALUE yarv_Array_each();
VALUE yarv_Integer_times();
// VALUE yarv_Hash_each();
VALUE insns_name_array();

void Init_yarvcore(){
  char opts[] = 
#if   defined(DISPATCH_DIRECT_THREADED_CODE)
    "[direct threaded code] "
#elif defined(DISPATCH_THREADED_CODE)
    "[threaded code] "
#endif

#ifdef FAKE_INLINE_METHOD_CACHE
    "[fake inline method cache] "
#endif

#ifdef OPT_BASIC_OPERATIONS
    "[optimize basic operation] "
#endif

#ifdef OPT_REGEXP_MATCH
    "[optimize regexp match] "
#endif

#ifdef OPT_STACK_CACHING
    "[stack caching] "
#endif

#ifdef INLINE_METHOD_CACHE
    "[inline method cache] "
#endif
      ;

#include "rev.inc"

  char ver[0x20];
  snprintf(ver, 0x20, "YARVCore %d.%d.%d", MAJOR_VER, MINOR_VER, DEVEL_VER);
  
  /* declare YARVCore module */
  mYarvCore = rb_define_module("YARVCore");
  rb_define_const(mYarvCore, "VERSION", rb_str_new2(ver));
  rb_define_const(mYarvCore, "MAJOR"  , INT2FIX(MAJOR_VER));
  rb_define_const(mYarvCore, "MINOR"  , INT2FIX(MINOR_VER));
  rb_define_const(mYarvCore, "REV"    , rb_str_new2(rev));
  rb_define_const(mYarvCore, "DATE"   , rb_str_new2(date));
  rb_define_const(mYarvCore, "OPTS"   , rb_str_new2(opts));

  /* YARVCore::USAGE_ANALISYS_* */
  rb_define_const(mYarvCore, "USAGE_ANALISYS_INSN", rb_hash_new());
  rb_define_const(mYarvCore, "USAGE_ANALISYS_REGS", rb_hash_new());
  rb_define_const(mYarvCore, "USAGE_ANALISYS_INSN_BIGRAM", rb_hash_new());
  

  /* YARVCore::InsnNameArray */
  rb_define_const(mYarvCore, "InsnNameArray", insns_name_array());
  
  rb_define_singleton_method(mYarvCore, "eval" , yarvcore_eval , 3);
  rb_define_singleton_method(mYarvCore, "parse", yarvcore_parse, 3);
  rb_define_singleton_method(mYarvCore, "eval_parsed", yarvcore_eval_parsed, 1);
  
  
  /* declare YARVCore::InstructionSequence */
  cYarvISeq = rb_define_class_under(mYarvCore, "InstructionSequence", rb_cObject);
  rb_define_alloc_func(cYarvISeq, iseq_alloc);
  rb_define_method(cYarvISeq, "initialize", iseq_init, 5);
  rb_define_method(cYarvISeq, "inspect",    iseq_inspect, 0);
  rb_define_method(cYarvISeq, "disasm",     iseq_disasm, 0);
  rb_define_method(cYarvISeq, "insns",      iseq_insns, 0);
  rb_define_method(cYarvISeq, "assemble",   iseq_assemble, 3);
  // rb_define_singleton_method(cYarvISeq, "new_from_insn_ary", iseq_init_from_insn_ary, 1);

  /* declare YARVCore::InstructionSequence::Instruction */
  cYarvInsn = rb_define_class_under(cYarvISeq, "Instruction", rb_cObject);
  rb_define_alloc_func(cYarvInsn, insn_alloc);
  rb_define_method(cYarvInsn, "initialize", insn_init, 3);
  rb_define_method(cYarvInsn, "to_s",       insn_to_s, 0);
  rb_define_method(cYarvInsn, "inspect",    insn_to_s, 0);
  rb_define_method(cYarvInsn, "insn_id",    insn_insn_id, 0);
  rb_define_method(cYarvInsn, "line_no",    insn_line_no, 0);
  rb_define_method(cYarvInsn, "operands",   insn_operands, 0);
  rb_define_singleton_method(cYarvInsn, "make", insn_make, 3);
  
  /* declare YARVCore::InstructionSequence::Label */
  cYarvLabel = rb_define_class_under(cYarvISeq, "Label", rb_cObject);
  rb_define_alloc_func(cYarvLabel, label_alloc);
  rb_define_method(cYarvLabel, "initialize", label_init, 1);
  rb_define_method(cYarvLabel, "to_s",       label_to_s, 0);
  rb_define_method(cYarvLabel, "inspect",    label_to_s, 0);
  rb_define_method(cYarvLabel, "position",   label_position, 0);  
  /* declare YARVCore::VM */
  cYarvVM = rb_define_class_under(mYarvCore, "VM", rb_cObject);
  rb_define_alloc_func(cYarvVM, vm_alloc);
  rb_define_method(cYarvVM, "initialize", vm_init, 0);
  rb_define_method(cYarvVM, "eval",       vm_eval, 1);

  /* declare YARVCore::VM::Thread */
  cYarvThread = rb_define_class_under(cYarvVM, "Thread", rb_cObject);
  rb_define_alloc_func(cYarvThread, thread_alloc);
  rb_define_method(cYarvThread, "initialize", thread_init, 1);
  rb_define_method(cYarvThread, "eval",       thread_eval, 1);

  /* declare YARVCore::VM::Env */
  cYarvEnv = rb_define_class_under(cYarvVM, "Env", rb_cObject);
  rb_define_alloc_func(cYarvEnv, env_alloc);

  /* declare YARVCore::VM::Proc */
  cYarvProc = rb_define_class_under(cYarvVM, "Proc", rb_cObject);
  rb_define_alloc_func(cYarvProc, proc_alloc);
  rb_define_method(cYarvProc, "call", proc_call, -1);
  
  /* misc */
  /* jitcompile(obj, selector) */
  rb_define_global_function("jitcompile", yarv_jitcompile, 2);
  
  /* declare YARVCore::ThrowObject */
  cYarvThrowObject = rb_define_class_under(mYarvCore, "ThrowObject", rb_cObject);

  /* declare YARVCore::Instructions */
  mYarvInsns = rb_define_module_under(mYarvCore, "Instructions");

  rb_define_global_function("once", yarv_once, 0);
  rb_define_global_function("cfunc", cfunc, 0);
  rb_define_global_function("yarv_caller", yarv_caller, 1);
  
  /* Integer#times */
  rb_define_method(rb_cInteger, "times", yarv_Integer_times, 0);
  
  /* Array#each */
  // rb_define_method(rb_cArray, "each", yarv_Array_each, 0);
  /* Hash#each */
  // rb_define_method(rb_cHash,"each", yarv_Hash_each, 0);

  yarv_setup(yarv_yield_values, yarv_call0, yarv_block_given_p, yarv_load,
             yarv_svar, yarv_iterate, yarv_call_super, yarv_backtrace);

  symIFUNC = ID2SYM(rb_intern("<IFUNC>"));
  symCFUNC = ID2SYM(rb_intern("<CFUNC>"));
  
  /* for optimize */
  idPLUS = rb_intern("+");
  idMINUS= rb_intern("-");
  idLT   = rb_intern("<");
  idLTLT = rb_intern("<<");
  idEqq  = rb_intern("===");
  idBackquote      = rb_intern("`");
  
  idAREF = rb_intern("[]");
  idASET = rb_intern("[]=");

  idIntern = rb_intern("intern");
  idMethodMissing = rb_intern("method_missing");
  
  idEqTilde        = rb_intern("=~");
  idThrowState     = rb_intern("#__ThrowState__");
  idThrowBackDFP   = rb_intern("#__ThrowBackDFP__");
  idThrowObject    = rb_intern("#__ThrowObject__");

  constant_pool_pool = rb_ary_new();
  rb_global_variable(&constant_pool_pool);

#if TEST_AOT_COMPILE
  Init_compiled();
#endif

}

