/**
 * YARV: Yet Another Ruby VM.
 *
 *
 * $Id: yarvcore.c 460 2006-02-21 21:14:23Z ko1 $
 * Create : K.S. 04/01/01 01:17:22
 *
 * Copyright (c) 2004-2006 SASADA Koichi <ko1 at atdot.net>
 */

#include "ruby.h"
#include "node.h"

#include "yarv_version.h"
#include "yarvcore.h"
#include "yarv.h"

VALUE mYarvCore;
VALUE cYarvISeq;
VALUE cYarvVM;
VALUE cYarvThread;
VALUE mYarvInsns;
VALUE cYarvEnv;
VALUE cYarvProc;
VALUE cYarvBinding;

VALUE symIFUNC;
VALUE symCFUNC;

ID idPLUS;
ID idMINUS;
ID idMULT;
ID idDIV;
ID idMOD;
ID idLT;
ID idLTLT;
ID idLE;
ID idEq;
ID idEqq;
ID idBackquote;
ID idEqTilde;
ID idThrowState;
ID idThrowBackDFP;
ID idThrowObject;
ID idAREF;
ID idASET;
ID idIntern;
ID idMethodMissing;
ID idLength;
ID idLambda;
ID idGets;
ID idSucc;
ID idEach;
ID idRangeEachLT;
ID idRangeEachLE;
ID idArrayEach;
ID idTimes;
ID idEnd;
ID idBitblt;
ID idAnswer;

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

#define MARK_FREE_DEBUG 0

#if MARK_FREE_DEBUG
static int g_indent = 0;

static void
gc_debug_indent(void)
{
    int i;
    for (i = 0; i < g_indent; i++) {
	printf(" ");
    }
}
static void
MARK_REPORT_BODY(char *msg, int st, void *ptr)
{
    if (st == 0) {
	g_indent--;
    }
    gc_debug_indent();
    printf("mark: %s (%p)\n", msg, ptr);
    if (st) {
	g_indent++;
    }
    fflush(stdout);
}

#define MARK_REPORT(msg, st) MARK_REPORT_BODY(msg, st, ptr)
#define GC_INFO gc_debug_indent(); printf
#define FREE_REPORT(msg) printf("free: %s (%p)\n", msg, ptr)

#else
#define MARK_REPORT(msg, st)
#define FREE_REPORT(msg)
#define GC_INFO if(0)printf
#endif

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

VALUE yarv_new_iseqval(VALUE node, VALUE name, VALUE file,
		       VALUE parent, VALUE type, VALUE opt);
static VALUE th_eval(yarv_thread_t *th, VALUE iseq);

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

yarv_thread_t *yarvCurrentThread = 0;
yarv_vm_t *theYarvVM = 0;
static VALUE yarvVMArray = Qnil;

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

VALUE
yarv_load(char *file)
{
    NODE *node;
    volatile VALUE iseq;
    volatile int critical;
    yarv_thread_t *th = GET_THREAD();

    critical = rb_thread_critical;
    rb_thread_critical = Qtrue;
    {
	th->parse_in_eval++;
	rb_load_file(file);
	th->parse_in_eval--;
	node = ruby_eval_tree;
    }
    rb_thread_critical = critical;

    if (ruby_nerrs > 0) {
	return 0;
    }

    iseq = yarv_new_iseqval((VALUE)node, rb_str_new2("<top (required)>"),
			    rb_str_new2(file), Qfalse, ISEQ_TYPE_TOP, Qfalse);

    th_eval(GET_THREAD(), iseq);
    return 0;
}


VALUE *th_svar(yarv_thread_t *self, int cnt);

VALUE *
rb_svar(int cnt)
{
    return th_svar(GET_THREAD(), cnt);
}

VALUE
rb_backref_get(void)
{
    VALUE *var = rb_svar(1);
    if (var) {
	return *var;
    }
    return Qnil;
}

void
rb_backref_set(VALUE val)
{
    VALUE *var = rb_svar(1);
    *var = val;
}

VALUE
rb_lastline_get(void)
{
    VALUE *var = rb_svar(0);
    if (var) {
	return *var;
    }
    return Qnil;
}

void
rb_lastline_set(VALUE val)
{
    VALUE *var = rb_svar(0);
    *var = val;
}

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) {
	ruby_nerrs = 0;
	rb_exc_raise(GET_THREAD()->errinfo);	// TODO: check err
    }

    return (VALUE)node;
}

static VALUE
yarvcore_eval_iseq(VALUE iseq)
{
    return th_eval(GET_THREAD(), iseq);
}

static VALUE
th_compile_from_node(yarv_thread_t *th, NODE * node, VALUE file)
{
    VALUE iseq;
    if (th->base_block) {
	iseq = yarv_new_iseqval((VALUE)node,
				th->base_block->iseq->name,
				file,
				th->base_block->iseq->self,
				ISEQ_TYPE_EVAL, Qfalse);
    }
    else {
	iseq = yarv_new_iseqval((VALUE)node, rb_str_new2("<main>"), file,
				Qfalse, ISEQ_TYPE_TOP, Qfalse);
    }
    return iseq;
}

VALUE
th_compile(yarv_thread_t *th, VALUE str, VALUE file, VALUE line)
{
    NODE *node = (NODE *) compile_string(str, file, line);
    return th_compile_from_node(th, (NODE *) node, file);
}

VALUE
yarvcore_eval_parsed(VALUE node, VALUE file)
{
    VALUE iseq = th_compile_from_node(GET_THREAD(), (NODE *) node, file);
    return yarvcore_eval_iseq(iseq);
}

VALUE
yarvcore_eval(VALUE self, VALUE str, VALUE file, VALUE line)
{
    VALUE node;
    node = compile_string(str, file, line);
    return yarvcore_eval_parsed(node, file);
}

static VALUE
yarvcore_parse(VALUE self, VALUE str, VALUE file, VALUE line)
{
    VALUE node = compile_string(str, file, line);
    return yarv_new_iseqval(node, rb_str_new2("<main>"), file, Qfalse,
			    ISEQ_TYPE_TOP, Qfalse);
}


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

VALUE
yarv_new_iseqval(VALUE node, VALUE name, VALUE file,
		 VALUE parent, VALUE type, VALUE opt)
{
    VALUE argv[6];

    argv[0] = node;
    argv[1] = name;
    argv[2] = file;
    argv[3] = parent;
    argv[4] = type;
    argv[5] = opt;
    return rb_class_new_instance(6, argv, cYarvISeq);
}

static void
compile_data_free(struct iseq_compile_data *compile_data)
{
    if (compile_data) {
	struct iseq_compile_data_storage *cur, *next;
	cur = compile_data->storage_head;
	while (cur) {
	    next = cur->next;
	    ruby_xfree(cur);
	    cur = next;
	}
	ruby_xfree(compile_data);
    }
}

static void
iseq_free(void *ptr)
{
    yarv_iseq_t *iseq;
    FREE_REPORT("-> iseq");

    if (ptr) {
	iseq = ptr;
	GC_INFO("%s\n", RSTRING(iseq->name)->ptr);

	if (iseq->iseq != iseq->iseq_encoded) {
	    FREE_UNLESS_NULL(iseq->iseq_encoded);
	}
	
	FREE_UNLESS_NULL(iseq->iseq);
	FREE_UNLESS_NULL(iseq->insn_info_tbl);
	FREE_UNLESS_NULL(iseq->local_tbl);
	FREE_UNLESS_NULL(iseq->catch_table);
	FREE_UNLESS_NULL(iseq->arg_opt_tbl);
	compile_data_free(iseq->compile_data);
	ruby_xfree(ptr);
    }
    FREE_REPORT("<- iseq");
}

static void
iseq_mark(void *ptr)
{
    yarv_iseq_t *iseq;
    MARK_REPORT("-> iseq", 1);

    if (ptr) {
	iseq = ptr;
	GC_INFO("%s\n", RSTRING(iseq->name)->ptr);
	MARK_UNLESS_NULL(iseq->iseq_mark_ary);
	MARK_UNLESS_NULL(iseq->name);
	MARK_UNLESS_NULL(iseq->file_name);
	MARK_UNLESS_NULL((VALUE)iseq->cref_stack);
	MARK_UNLESS_NULL(iseq->klass);
	MARK_UNLESS_NULL(iseq->catch_table_ary);
	MARK_UNLESS_NULL((VALUE)iseq->node);
	MARK_UNLESS_NULL(iseq->cached_special_block);

	if (iseq->compile_data != 0) {
	    MARK_UNLESS_NULL(iseq->compile_data->mark_ary);
	    MARK_UNLESS_NULL(iseq->compile_data->err_info);
	}
    }
    MARK_REPORT("<- iseq", 0);
}

static VALUE
iseq_alloc(VALUE klass)
{
    VALUE volatile obj;
    yarv_iseq_t *iseq;

    obj = Data_Make_Struct(klass, yarv_iseq_t, iseq_mark, iseq_free, iseq);
    return obj;
}

static VALUE
prepare_iseq_build(yarv_iseq_t *iseq,
		   VALUE name, VALUE file_name,
		   VALUE parent, VALUE type, VALUE opt)
{
    iseq->name = name;
    iseq->defined_method_id = 0;
    iseq->file_name = file_name;
    iseq->iseq_mark_ary = rb_ary_new();
    RBASIC(iseq->iseq_mark_ary)->klass = 0;
    
    iseq->catch_table_ary = rb_ary_new();
    iseq->type = type;
    iseq->arg_rest = 0;
    iseq->arg_block = 0;
    iseq->klass = 0;
    iseq->special_block_builder = GC_GUARDED_PTR_REF(opt);
    iseq->cached_special_block_builder = 0;
    iseq->cached_special_block = 0;

    /* set class nest stack */
    if (type == ISEQ_TYPE_TOP) {
	/* toplevel is private */
	iseq->cref_stack = NEW_BLOCK(rb_cObject);
	iseq->cref_stack->nd_visi = NOEX_PRIVATE;
    }
    else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) {
	iseq->cref_stack = NEW_BLOCK(0); /* place holder */
    }
    else if (parent) {
	yarv_iseq_t *piseq;
	GetISeqVal(parent, piseq);
	iseq->cref_stack = piseq->cref_stack;
    }

    iseq->compile_data = ALLOC(struct iseq_compile_data);
    MEMZERO(iseq->compile_data, struct iseq_compile_data, 1);
    iseq->compile_data->mark_ary = rb_ary_new();
    RBASIC(iseq->compile_data->mark_ary)->klass = 0;

    iseq->compile_data->storage_head = iseq->compile_data->storage_current =
	(struct iseq_compile_data_storage *)
    ALLOC_N(char, INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE +
		sizeof(struct iseq_compile_data_storage));

    iseq->compile_data->storage_head->pos = 0;
    iseq->compile_data->storage_head->next = 0;
    iseq->compile_data->storage_head->size =
	INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE;
    iseq->compile_data->storage_head->buff =
	(char *)(&iseq->compile_data->storage_head->buff + 1);

    if (type == ISEQ_TYPE_TOP ||
	type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) {
	iseq->local_iseq = iseq;
    }
    else {
	yarv_iseq_t *piseq;
	GetISeqVal(parent, piseq);
	iseq->local_iseq = piseq->local_iseq;
    }

    if (RTEST(parent)) {
	yarv_iseq_t *piseq;
	GetISeqVal(parent, piseq);
	iseq->parent_iseq = piseq;
    }

    return Qtrue;
}

static VALUE
cleanup_iseq_build(yarv_iseq_t *iseq)
{
    compile_data_free(iseq->compile_data);
    iseq->compile_data = 0;
    if (ruby_nerrs > 0) {
	VALUE str = rb_str_buf_new2("compile error");
	ruby_nerrs = 0;
	rb_exc_raise(rb_exc_new3(rb_eSyntaxError, str));
    }
    return Qtrue;
}

static VALUE
iseq_init(VALUE self, VALUE node, VALUE name, VALUE file_name,
	  VALUE parent, VALUE type, VALUE opt)
{
    yarv_iseq_t *iseq;
    GetISeqVal(self, iseq);
    iseq->self = self;
    prepare_iseq_build(iseq, name, file_name, parent, type, opt);
    iseq_compile(self, node);
    cleanup_iseq_build(iseq);
    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];
    yarv_iseq_t *iseq;

    GetISeqVal(self, iseq);
    snprintf(buff, sizeof(buff), "<ISeq:%s@%s>",
	     RSTRING(iseq->name)->ptr, RSTRING(iseq->file_name)->ptr);

    return rb_str_new2(buff);
}

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

void native_thread_cleanup(void *);

static void
vm_free(void *ptr)
{
    FREE_REPORT("-> vm");
    if (ptr) {
	yarv_vm_t *vmobj = ptr;

	st_free_table(vmobj->living_threads);
	// TODO: MultiVM Instance
	// VM object should not be cleaned by GC
	// ruby_xfree(ptr);
	// theYarvVM = 0;
    }
    FREE_REPORT("<- vm");
}

static int
vm_mark_each_thread_func(st_data_t key, st_data_t value, st_data_t dummy)
{
    VALUE thval = (VALUE)key;
    rb_gc_mark(thval);
    return ST_CONTINUE;
}

static void
vm_mark(void *ptr)
{
    MARK_REPORT("-> vm", 1);
    GC_INFO("-------------------------------------------------\n");
    if (ptr) {
	yarv_vm_t *vm = ptr;
	st_foreach(vm->living_threads, vm_mark_each_thread_func, 0);
	MARK_UNLESS_NULL(vm->thgroup_default);
    }
    MARK_REPORT("<- vm", 0);
}

static VALUE
vm_alloc(VALUE klass)
{
    VALUE volatile obj;
    yarv_vm_t *vm;
    obj = Data_Make_Struct(klass, yarv_vm_t, vm_mark, vm_free, vm);
    return obj;
}

static VALUE
vm_init(VALUE self, VALUE node)
{
    /* allocate vm stack */
    yarv_vm_t *vm;
    GetVMVal(self, vm);
    vm->self = self;
    return self;
}

static VALUE
vm_eval(VALUE self, VALUE iseq)
{
    return th_eval(GET_THREAD(), iseq);
}


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

static void
thread_free(void *ptr)
{
    yarv_thread_t *th;
    FREE_REPORT("-> thread");

    if (ptr) {
	th = ptr;
	FREE_UNLESS_NULL(th->stack);
	FREE_UNLESS_NULL(th->top_local_tbl);
	if (th->local_storage) {
	    st_free_table(th->local_storage);
	}

	if (th->vm->main_thread == th) {
	    GC_INFO("main thread");
	}
	else {
	    ruby_xfree(ptr);
	}
    }
    FREE_REPORT("<- thread");
}

void yarv_machine_stack_mark(yarv_thread_t *th);

static void
thread_mark(void *ptr)
{
    yarv_thread_t *th = NULL;
    MARK_REPORT("-> thread", 1);
    if (ptr) {
	th = ptr;
	if (th->stack) {
	    VALUE *p = th->stack;
	    VALUE *sp = th->cfp->sp;
	    yarv_control_frame_t *cfp = th->cfp;
	    yarv_control_frame_t *limit_cfp =
		(void *)(th->stack + th->stack_size);

	    while (p < sp) {
		rb_gc_mark(*p++);
	    }
	    while (cfp != limit_cfp) {
		rb_gc_mark(cfp->proc);
		cfp = YARV_PREVIOUS_CONTROL_FRAME(cfp);
	    }
	}

	/* mark ruby objects */
	MARK_UNLESS_NULL(th->first_proc);
	MARK_UNLESS_NULL(th->first_args);

	MARK_UNLESS_NULL(th->thgroup);
	MARK_UNLESS_NULL(th->value);
	MARK_UNLESS_NULL(th->errinfo);

	rb_mark_tbl(th->local_storage);

	if (GET_THREAD() != th &&
	    th->machine_stack_start && th->machine_stack_end) {
	    yarv_machine_stack_mark(th);
	}
    }

    MARK_UNLESS_NULL(th->stat_insn_usage);
    MARK_REPORT("<- thread", 0);
}

static VALUE
thread_alloc(VALUE klass)
{
    VALUE volatile obj;
    yarv_thread_t *th;

    obj = Data_Make_Struct(klass, yarv_thread_t,
			   thread_mark, thread_free, th);
    return obj;
}

static void
th_init2(yarv_thread_t *th)
{
    MEMZERO(th, yarv_thread_t, 1);

    /* allocate thread stack */
    th->stack = ALLOC_N(VALUE, YARV_THREAD_STACK_SIZE);

    th->stack_size = YARV_THREAD_STACK_SIZE;
    th->cfp = (void *)(th->stack + th->stack_size);
    th->cfp--;

    th->cfp->pc = 0;
    th->cfp->sp = th->stack;
    th->cfp->bp = 0;
    th->cfp->lfp = th->stack;
    th->cfp->dfp = th->stack;
    th->cfp->self = Qnil;
    th->cfp->magic = 0;
    th->cfp->iseq = 0;
    th->cfp->proc = 0;
    th->cfp->block_iseq = 0;
    
    th->status = THREAD_RUNNABLE;
    th->errinfo = Qnil;
}

void
th_klass_init(yarv_thread_t *th)
{
    /* */
}

static void
th_init(yarv_thread_t *th)
{
    th_init2(th);
    th_klass_init(th);
}

static VALUE
thread_init(VALUE self)
{
    yarv_thread_t *th;
    yarv_vm_t *vm = GET_THREAD()->vm;
    GetThreadVal(self, th);

    th_init(th);
    th->self = self;
    th->vm = vm;
    return self;
}

VALUE th_eval_body(yarv_thread_t *th);
VALUE th_set_top_stack(yarv_thread_t *, VALUE iseq);
VALUE rb_f_binding(VALUE);

static VALUE
th_eval(yarv_thread_t *th, VALUE iseq)
{
    VALUE val;
    th_set_top_stack(th, iseq);

    if (!rb_const_defined(rb_cObject, rb_intern("TOPLEVEL_BINDING"))) {
	rb_define_global_const("TOPLEVEL_BINDING", rb_f_binding(Qnil));
    }
    val = th_eval_body(th);
    return val;
}


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

static void
env_free(void *ptr)
{
    yarv_env_t *env;
    FREE_REPORT("-> env");
    if (ptr) {
	env = ptr;
	FREE_UNLESS_NULL(env->env);
	ruby_xfree(ptr);
    }
    FREE_REPORT("<- env");
}

static void
env_mark(void *ptr)
{
    yarv_env_t *env;
    MARK_REPORT("-> env", 1);
    if (ptr) {
	env = ptr;
	if (env->env) {
	    /* TODO: should mark more restricted range */
	    GC_INFO("env->env\n");
	    rb_gc_mark_locations(env->env, env->env + env->env_size);
	}
	GC_INFO("env->prev_envval\n");
	MARK_UNLESS_NULL(env->prev_envval);

	if (env->block.iseq) {
	    //printf("env->block.iseq <%p, %d>\n",
	    //       env->block.iseq, BUILTIN_TYPE(env->block.iseq));
	    if (BUILTIN_TYPE(env->block.iseq) == T_NODE) {
		MARK_UNLESS_NULL((VALUE)env->block.iseq);
	    }
	    else {
		MARK_UNLESS_NULL(env->block.iseq->self);
	    }
	}
    }
    MARK_REPORT("<- env", 0);
}

static VALUE
env_alloc(VALUE klass)
{
    VALUE obj;
    yarv_env_t *env;
    obj = Data_Make_Struct(klass, yarv_env_t, env_mark, env_free, env);
    env->env = 0;
    env->prev_envval = 0;
    env->block.iseq = 0;
    return obj;
}


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

static void
proc_free(void *ptr)
{
    FREE_REPORT("-> proc");
    if (ptr) {
	ruby_xfree(ptr);
    }
    FREE_REPORT("<- proc");
}

static void
proc_mark(void *ptr)
{
    yarv_proc_t *proc;
    MARK_REPORT("-> proc", 1);
    if (ptr) {
	proc = ptr;
	MARK_UNLESS_NULL(proc->envval);
	MARK_UNLESS_NULL(proc->blockprocval);
	MARK_UNLESS_NULL((VALUE)proc->special_cref_stack);
	if (proc->block.iseq && YARV_IFUNC_P(proc->block.iseq)) {
	    MARK_UNLESS_NULL((VALUE)(proc->block.iseq));
	}
    }
    MARK_REPORT("<- proc", 0);
}

static VALUE
proc_alloc(VALUE klass)
{
    VALUE obj;
    yarv_proc_t *proc;
    obj = Data_Make_Struct(klass, yarv_proc_t, proc_mark, proc_free, proc);
    MEMZERO(proc, yarv_proc_t, 1);
    return obj;
}

static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    yarv_proc_t *proc;
    GetProcVal(procval, proc);
    return th_invoke_proc(GET_THREAD(), proc, proc->block.self, argc, argv);
}

static VALUE
proc_to_proc(VALUE self)
{
    return self;
}

VALUE
yarv_obj_is_proc(VALUE proc)
{
    if (TYPE(proc) == T_DATA &&
	RDATA(proc)->dfree == (RUBY_DATA_FUNC) proc_free) {
	return Qtrue;
    }
    else {
	return Qfalse;
    }
}

static VALUE
proc_arity(VALUE self)
{
    yarv_proc_t *proc;
    yarv_iseq_t *iseq;
    GetProcVal(self, proc);
    iseq = proc->block.iseq;
    if (iseq && BUILTIN_TYPE(iseq) != T_NODE) {
	if (iseq->arg_rest == 0 && iseq->arg_opts == 0) {
	    return INT2FIX(iseq->argc);
	}
	else {
	    return INT2FIX(-iseq->argc - 1);
	}
    }
    else {
	return INT2FIX(-1);
    }
}

int
rb_proc_arity(VALUE proc)
{
    return FIX2INT(proc_arity(proc));
}

static VALUE
proc_eq(VALUE self, VALUE other)
{
    if (self == other) {
	return Qtrue;
    }
    else {
	if (TYPE(other) != T_DATA &&
	    RBASIC(other)->klass == cYarvProc &&
	    CLASS_OF(self) == CLASS_OF(other)) {
	    yarv_proc_t *p1, *p2;
	    GetProcVal(self, p1);
	    GetProcVal(other, p2);
	    if (p1->block.iseq == p2->block.iseq && p1->envval == p2->envval) {
		return Qtrue;
	    }
	}
    }
    return Qfalse;
}

static VALUE
proc_hash(VALUE self)
{
    int hash;
    yarv_proc_t *proc;
    GetProcVal(self, proc);
    hash = (long)proc->block.iseq;
    hash ^= (long)proc->envval;
    hash ^= (long)proc->block.lfp >> 16;
    return INT2FIX(hash);
}

static VALUE
proc_to_s(VALUE self)
{
    VALUE str = 0;
    yarv_proc_t *proc;
    char *cname = rb_obj_classname(self);
    yarv_iseq_t *iseq;
    
    GetProcVal(self, proc);
    iseq = proc->block.iseq;

    if (YARV_NORMAL_ISEQ_P(iseq)) {
	int line_no = 0;
	
	if (iseq->insn_info_tbl) {
	    line_no = iseq->insn_info_tbl[0].line_no;
	}
	str = rb_sprintf("#<%s:%p@%s:%d>", cname, self,
			 RSTRING(iseq->file_name)->ptr,
			 line_no);
    }
    else {
	str = rb_sprintf("#<%s:%p>", cname, proc->block.iseq);
    }

    if (OBJ_TAINTED(self)) {
	OBJ_TAINT(str);
    }
    return str;
}

static VALUE
proc_dup(VALUE self)
{
    VALUE procval = proc_alloc(cYarvProc);
    yarv_proc_t *src, *dst;
    GetProcVal(self, src);
    GetProcVal(procval, dst);

    dst->block = src->block;
    dst->envval = src->envval;
    dst->safe_level = dst->safe_level;
    dst->special_cref_stack = src->special_cref_stack;
    
    return procval;
}

VALUE yarv_proc_dup(VALUE self)
{
    return proc_dup(self);
}
static VALUE
proc_clone(VALUE self)
{
    VALUE procval = proc_dup(self);
    CLONESETUP(procval, self);
    return procval;
}


/***************/
/* YarvBinding */
/***************/

static void
binding_free(void *ptr)
{
    yarv_binding_t *bind;
    FREE_REPORT("-> binding");
    if (ptr) {
	bind = ptr;
	ruby_xfree(ptr);
    }
    FREE_REPORT("<- binding");
}

static void
binding_mark(void *ptr)
{
    yarv_binding_t *bind;
    MARK_REPORT("-> binding", 1);
    if (ptr) {
	bind = ptr;
	MARK_UNLESS_NULL(bind->env);
	MARK_UNLESS_NULL((VALUE)bind->cref_stack);
    }
    MARK_REPORT("<- binding", 0);
}

static VALUE
binding_alloc(VALUE klass)
{
    VALUE obj;
    yarv_binding_t *bind;
    obj = Data_Make_Struct(klass, yarv_binding_t,
			   binding_mark, binding_free, bind);
    MEMZERO(bind, yarv_binding_t, 1);
    return obj;
}

static VALUE
binding_dup(VALUE self)
{
    VALUE bindval = rb_obj_alloc(cYarvBinding);
    yarv_binding_t *src, *dst;
    GetBindingVal(self, src);
    GetBindingVal(bindval, dst);
    dst->env = src->env;
    dst->cref_stack = src->cref_stack;
    return bindval;
}

static VALUE
binding_clone(VALUE self)
{
    VALUE bindval = binding_dup(self);
    CLONESETUP(bindval, self);
    return bindval;
}


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

static VALUE
yarv_once()
{
    return rb_yield(Qnil);
}

static VALUE
yarv_segv()
{
    volatile int *a = 0;
    *a = 0;
    return Qnil;
}

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

// VALUE yarv_Hash_each();
VALUE insns_name_array(void);
VALUE Init_yarvthread(void);
extern VALUE *rb_gc_stack_start;

VALUE rb_proc_s_new(int argc, VALUE *argv, VALUE klass);

VALUE
sdr(void)
{
    yarv_bug();
    return Qnil;
}

char yarv_version[0x20];
char *yarv_options = ""
#if   OPT_DIRECT_THREADED_CODE
    "[direct threaded code] "
#elif OPT_TOKEN_THREADED_CODE
    "[token threaded code] "
#elif OPT_CALL_THREADED_CODE
    "[call threaded code] "
#endif
#if FAKE_INLINE_METHOD_CACHE
    "[fake inline method cache] "
#endif
#if OPT_BASIC_OPERATIONS
    "[optimize basic operation] "
#endif
#if OPT_STACK_CACHING
    "[stack caching] "
#endif
#if OPT_OPERANDS_UNIFICATION
    "[operands unification] "
#endif
#if OPT_INSTRUCTIONS_UNIFICATION
    "[instructions unification] "
#endif
#if OPT_INLINE_METHOD_CACHE
    "[inline method cache] "
#endif
#if OPT_BLOCKINLINING
    "[block inlining] "
#endif
    ;

void
Init_yarvcore(void)
{
#include "rev.inc"

    snprintf(yarv_version, 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(yarv_version));
    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(yarv_options));

    /* 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_iseq", yarvcore_eval_iseq, 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, 6);
    rb_define_method(cYarvISeq, "inspect", iseq_inspect, 0);
    rb_define_method(cYarvISeq, "disasm", iseq_disasm, 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::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_global_const("Thread", cYarvThread);
    rb_define_alloc_func(cYarvThread, thread_alloc);
    rb_define_method(cYarvThread, "initialize", thread_init, 0);

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

    /* declare YARVCore::VM::Proc */
    rb_cProc = cYarvProc = rb_define_class_under(cYarvVM, "Proc", rb_cObject);
    rb_const_set(rb_cObject, rb_intern("Proc"), cYarvProc);
    rb_define_alloc_func(cYarvProc, proc_alloc);
    rb_define_singleton_method(cYarvProc, "new", rb_proc_s_new, -1);
    rb_define_method(cYarvProc, "call", proc_call, -1);
    rb_define_method(cYarvProc, "[]", proc_call, -1);
    rb_define_method(cYarvProc, "to_proc", proc_to_proc, 0);
    rb_define_method(cYarvProc, "clone", proc_clone, 0);
    rb_define_method(cYarvProc, "arity", proc_arity, 0);
    rb_define_method(cYarvProc, "clone", proc_clone, 0);
    rb_define_method(cYarvProc, "dup", proc_dup, 0);
    rb_define_method(cYarvProc, "==", proc_eq, 1);
    rb_define_method(cYarvProc, "eql?", proc_eq, 1);
    rb_define_method(cYarvProc, "hash", proc_hash, 0);
    rb_define_method(cYarvProc, "to_s", proc_to_s, 0);

    /* declare YARVCore::VM::Binding */
    cYarvBinding = rb_define_class_under(cYarvVM, "Binding", rb_cObject);
    rb_define_alloc_func(cYarvBinding, binding_alloc);
    rb_undef_method(CLASS_OF(cYarvBinding), "new");
    rb_define_method(cYarvBinding, "clone", binding_clone, 0);
    rb_define_method(cYarvBinding, "dup", binding_dup, 0);
    rb_define_global_function("binding", rb_f_binding, 0);

    /* misc */


    /* YARV test functions */

    rb_define_global_function("once", yarv_once, 0);
    rb_define_global_function("segv", yarv_segv, 0);
    rb_define_global_function("cfunc", cfunc, 0);
    rb_define_global_function("SDR", sdr, 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);

    symIFUNC = ID2SYM(rb_intern("<IFUNC>"));
    symCFUNC = ID2SYM(rb_intern("<CFUNC>"));

    /* for optimize */
    idPLUS = rb_intern("+");
    idMINUS = rb_intern("-");
    idMULT = rb_intern("*");
    idDIV = rb_intern("/");
    idMOD = rb_intern("%");
    idLT = rb_intern("<");
    idLTLT = rb_intern("<<");
    idLE = rb_intern("<=");
    idEq = rb_intern("==");
    idEqq = rb_intern("===");
    idBackquote = rb_intern("`");
    idEqTilde = rb_intern("=~");

    idAREF = rb_intern("[]");
    idASET = rb_intern("[]=");

    idEach = rb_intern("each");
    idTimes = rb_intern("times");
    idLength = rb_intern("length");
    idLambda = rb_intern("lambda");
    idIntern = rb_intern("intern");
    idGets = rb_intern("gets");
    idSucc = rb_intern("succ");
    idEnd = rb_intern("end");
    idRangeEachLT = rb_intern("Range#each#LT");
    idRangeEachLE = rb_intern("Range#each#LE");
    idArrayEach = rb_intern("Array#each");
    idMethodMissing = rb_intern("method_missing");

    idThrowState = rb_intern("#__ThrowState__");
    idThrowBackDFP = rb_intern("#__ThrowBackDFP__");
    idThrowObject = rb_intern("#__ThrowObject__");

    idBitblt = rb_intern("bitblt");
    idAnswer = rb_intern("the_answer_to_life_the_universe_and_everything");

#if TEST_AOT_COMPILE
    Init_compiled();
#endif

    // make vm
    {
	/* create vm object */
	VALUE vmval = rb_class_new_instance(0, 0, cYarvVM);
	yarv_vm_t *vm;
	yarv_thread_t *th;
	GetVMVal(vmval, vm);
	theYarvVM = vm;

	yarvVMArray = rb_ary_new();
	rb_global_variable(&yarvVMArray);
	rb_ary_push(yarvVMArray, vm->self);

	/* create main thread */
	vm->main_thread_val = rb_class_new_instance(0, 0, cYarvThread);
	GetThreadVal(vm->main_thread_val, th);

	vm->main_thread = th;
	vm->running_thread = th;
	GET_THREAD()->vm = vm;
	thread_free(GET_THREAD());
	th->vm = vm;
	yarv_set_current_running_thread(th);

	th->machine_stack_start = rb_gc_stack_start;
	Init_yarvthread();

	th->thgroup = th->vm->thgroup_default;

	vm->living_threads = st_init_numtable();
	st_insert(vm->living_threads, th->self, (st_data_t) th->thread_id);
    }
}

static void
test(void)
{
    int i;
    int *p;
    printf("!test!\n");
    for (i = 0; i < 1000000; i++) {
	p = ALLOC(int);
    }
}

void
Init_yarv(void)
{
    /* initialize main thread */
    yarv_thread_t *th = ALLOC(yarv_thread_t);
    th_init2(th);
    yarv_set_current_running_thread_raw(th);
}
