/****************************************************************************
 * KONOHA COPYRIGHT, LICENSE NOTICE, AND DISCRIMER  
 * 
 * Copyright (c) 2005-2008, Kimio Kuramitsu <kimio at ynu.ac.jp>
 *           (c) 2008-      Konoha Software Foundation  
 * All rights reserved.
 * 
 * You may choose one of the following two licenses when you use konoha. 
 * See www.konohaware.org/license.html for further information.
 * 
 * (1) GNU General Public License 2.0      (with    KONOHA_UNDER_GPL2)
 * (2) Konoha Software Foundation License 1.0
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *  
 ****************************************************************************/

#include "konoha_gc.h"

#ifndef KONOHA_VM_H_
#define KONOHA_VM_H_

#ifdef __cplusplus 
extern "C" {
#endif

/* ======================================================================== */

#define VM_DEBUG_ASSERT(c)        KNH_ASSERT(c)

int knh_Method_pctoline(Method *mtd, knhvmc_t *pc);

/* ======================================================================== */

//#define ebp_(n)                   ((Context*)ctx)->ebp[n]
#define ebp_(n)                   ((Context*)ctx)->ebp[(n)].o
#define sfp_(n)                   sfp[(n)].o
#define ofp_(n)                   KNH_FIELDn(sfp[0].o, (n))
#define oidx_(o,idx)              KNH_FIELDn((o), (idx))
#define i1_(n)                    (n)
#define u1_(n)                    (n)
#define u2_(n)                    (n)
#define mn_(n)                    (knh_methodn_t)(n)
#define c2_(n)                    (knh_type_t)(n)
#define e2_(n)                    (knh_expt_t)(n)
#define OBJ_(o)                   (o)

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

/* ======================================================================== */

#define NPC                       
#define VM_EBP(ctx,n)             ((Context*)ctx)->ebp[n].o
#define VM_FILE                   ""
#define VM_LINE                   0

/* ======================================================================== */

#define VM_MOV(ctx, v1, v2)   \
	knh_Object_RCinc(v2); \
	knh_Object_RCdec(v1); \
	if(knh_Object_isRC0(v1)) { \
		knh_Object_free(ctx, v1); \
	} \
	v1 = v2; \

#define VM_MOV__ngc(ctx, v1, v2)   \
	knh_Object_RCinc(v2); \
	knh_Object_RCdec((Object*)v1); \
	v1 = v2; \

#define METHOD_RETURN(ctx, sfp, v)      {\
		Object *retval_ = (Object*)(v); \
		VM_MOV__ngc(ctx, sfp[-1].o, retval_); \
		return; \
	}\

#define METHOD_RETURN_BOOLEAN(ctx, sfp, c)      {\
		if((c)) { \
			VM_MOV__ngc(ctx, sfp[-1].o, KNH_TRUE); \
		}else { \
			VM_MOV__ngc(ctx, sfp[-1].o, KNH_FALSE); \
		} \
		return; \
	}\

#define VM_HALT(ctx)  \
	KNH_THROWs(ctx, "Halt!!");     \
	
#define VM_RET(ctx, v)      \
	VM_MOV__ngc(ctx, sfp[-1].o, v); \
	return; \

#define VM_MOV_(ctx,v,v2)            KNH_SETv(ctx,v,v2)

#define VM_MOVE(ctx,v,v2)            VM_MOV(ctx,v,v2)
#define VM_MOVS(ctx,v,v2)            VM_MOV(ctx,v,v2)
#define VM_MOVO(ctx,v,v2)            VM_MOV(ctx,v,v2)
#define VM_MOVI(ctx,o,idx,v2)        VM_MOV(ctx,oidx_(o,idx),v2)

#define VM_MOVE__OIDX(ctx,v,o,idx)   VM_MOV(ctx,v,oidx_(o,idx))
#define VM_MOVS__OIDX(ctx,v,o,idx)   VM_MOV(ctx,v,oidx_(o,idx))
#define VM_MOVO__OIDX(ctx,v,o,idx)   VM_MOV(ctx,v,oidx_(o,idx))
#define VM_MOVI__OIDX(ctx,o,idx,o2,idx2)   VM_MOV(ctx,oidx_(o,idx),oidx_(o2,idx2))

#define VM_MOVSFP_IFNUL(ctx, n, v2) \
	if(IS_NULL(sfp[n].o)) {\
		KNH_SETv(ctx, sfp[n].o, v2);\
	}\

#define _VM_PUT_(ctx,v) \
	VM_MOV(ctx, ((Context*)ctx)->ebp[0].o, v); \

#define _VM_PUTI_(ctx,i) \
	((Context*)ctx)->ebp[0].op = (knh_stackop_t)i; \

#define _VM_PUSHI_(ctx,v) \
	((Context*)ctx)->ebp++; \
	((Context*)ctx)->ebp[0].op = (knh_stackop_t)v; \

#define _VM_PUSH__MN_(ctx,mn) \
	((Context*)ctx)->ebp++; \
	((Context*)ctx)->ebp[0].op = (knh_uint_t)mn; \
	
#define VM_PUSH(ctx,v) \
	((Context*)ctx)->ebp++; \
	VM_MOV(ctx, ((Context*)ctx)->ebp[0].o, v); \

#define VM_SHIFT(ctx,n) \
	((Context*)ctx)->ebp += (n); \

#define VM_PUSH__DEF(ctx, cid) \
	((Context*)ctx)->ebp++; \
	KNH_SETv(ctx, ((Context*)ctx)->ebp[0].o, knh_tClass_defaultValue(ctx, cid)); \

#define VM_CONST_CTX    0
#define VM_CONST_STDIN  1
#define VM_CONST_STDOUT 2
#define VM_CONST_STDERR 3

#define VM_PUT_SYSCONST(ctx, n) { \
		VM_MOV(ctx, ((Context*)ctx)->ebp[0].o, knh_tConst_systemValue(ctx, n)); \
	}\

#define VM_PUSH_SYSCONST(ctx, n) { \
		((Context*)ctx)->ebp++; \
		VM_MOV(ctx, ((Context*)ctx)->ebp[0].o, knh_tConst_systemValue(ctx, n)); \
	}\

#define VM_VARGV(ctx, n) { \
		KNH_SETv(ctx, sfp[n].o, knh_sfp_toArray(ctx, &sfp[n-1])); \
		((Context*)ctx)->ebp = &sfp[n]; \
	} \

#define KNH_SCALL(ctx, n) { \
		knh_sfp_t *mbp_ = ctx->ebp - (n) ; \
		KNH_ASSERT(IS_Method(mbp_[0].mtd)); \
		mbp_[0].op = 0; \
		(mbp_[0].mtd)->fcall_1(ctx, mbp_ + 1); \
		mbp_[0].op = 0; \
		((Context*)ctx)->ebp = mbp_; \
	} \
	
#define VM_SCALL(ctx, n) { \
		knh_sfp_t *mbp_ = ((Context*)ctx)->ebp - (n) ; \
		KNH_ASSERT(IS_Method(mbp_[0].mtd)); \
		KNH_ASSERT(IS_NOTNULL(mbp_[1].o)); \
		mbp_[0].op = (knh_stackop_t)pc; \
		(mbp_[0].mtd)->fcall_1(ctx, mbp_ + 1); \
		mbp_[0].op = 0; \
		((Context*)ctx)->ebp = mbp_; \
	} \

#define VM_NSCALL(ctx, n) { \
		knh_sfp_t *mbp_ = ((Context*)ctx)->ebp - (n) ; \
		if(IS_NULL(mbp_[1].o)) { \
			KNH_SETv(ctx, mbp_[0].o, KNH_NULL); \
		} \
		else { \
			KNH_ASSERT(IS_Method(mbp_[0].mtd)); \
			mbp_[0].op = (knh_stackop_t)pc; \
			(mbp_[0].mtd)->fcall_1(ctx, mbp_ + 1); \
			mbp_[0].op = 0; \
		} \
		((Context*)ctx)->ebp = mbp_; \
	} \

	
#define VM_CALL(ctx, n, mn) { \
		knh_sfp_t *mbp_ = ctx->ebp - (n) ; \
		KNH_SETv(ctx, mbp_[0].o, knh_Method_ufind(ctx, knh_Object_cid(mbp_[1].o), mn)); \
		KNH_ASSERT(IS_Method(mbp_[0].mtd)); \
		mbp_[0].op = (knh_stackop_t)pc; \
		(mbp_[0].mtd)->fcall_1(ctx, mbp_ + 1); \
		mbp_[0].op = 0; \
		((Context*)ctx)->ebp = mbp_; \
	} \

#define VM_DCALL(ctx, n, mn) { \
		knh_sfp_t *mbp_ = ((Context*)ctx)->ebp - (n) ; \
		KNH_SETv(ctx, mbp_[0].o, knh_Method_ufind(ctx, knh_Object_cid(mbp_[1].o), mn)); \
		KNH_ASSERT(IS_Method(mbp_[0].mtd)); \
		knh_sfp_typecheck__DCALL(ctx, mbp_); \
		mbp_[0].op = (knh_stackop_t)pc; \
		(mbp_[0].mtd)->fcall_1(ctx, mbp_ + 1); \
		mbp_[0].op = 0; \
		((Context*)ctx)->ebp = mbp_; \
	} \


#define VM_SMAP(ctx, mpr)  { \
		knh_Mapper_t *mpr_ = (knh_Mapper_t*)mpr; \
		VM_DEBUG_ASSERT(IS_Mapper(mpr)); \
		KNH_SETv(ctx, ebp_(0), (mpr_)->fmap_1(ctx, ebp_(0), mpr_)); \
	} \

#define KNH_SMAP(ctx, mpr)  { \
		knh_Mapper_t *mpr_ = (knh_Mapper_t*)mpr; \
		KNH_SETv(ctx, ebp_(0), (mpr_)->fmap_1(ctx, ebp_(0), mpr_)); \
	} \


#define VM_SMAPE(ctx, mpr)  { \
		knh_Mapper_t *mpr_ = (knh_Mapper_t*)mpr; \
		VM_DEBUG_ASSERT(IS_Mapper(mpr)); \
		KNH_SETv(ctx, ebp_(0), (mpr_)->fmap_1(ctx, ebp_(0), mpr_)); \
		if(IS_NULL(ebp_(0))) { \
			knh_konohavm_throwException(ctx, new_Exception__Nue(ctx, (Nue*)ebp_(0))); \
		} \
	} \

#define VM_MAP(ctx, tcid)  { \
		knh_Mapper_t *mpr_ = knh_tmapper_find(ctx, knh_Object_cid(ebp_(0)), tcid); \
		KNH_SETv(ctx, ebp_(0), (mpr_)->fmap_1(ctx, ebp_(0), mpr_)); \
	} \
	
#define VM_MAPE(ctx, tcid)  { \
		knh_Mapper_t *mpr_ = knh_tmapper_find(ctx, knh_Object_cid(ebp_(0)), tcid); \
		KNH_SETv(ctx, ebp_(0), (mpr_)->fmap_1(ctx, ebp_(0), mpr_)); \
		if(IS_NULL(ebp_(0))) { \
			knh_konohavm_throwException(ctx, new_Exception__Nue(ctx, (Nue*)ebp_(0))); \
		} \
	} \

#define VM_NULLCHK(ctx, v) \
	if(IS_NULL(v)) { \
		knh_konohavm_throwException(ctx, new_Exception__Nue(ctx, (Nue*)v)); \
	} \

#define VM_TYPECHK(ctx, type, v) \
	if(!knh_Object_opTypeOf(ctx, v, type)) { \
		knh_konohavm_throwException(ctx, new_Exception__type(ctx, v, type)); \
	} \

	
#define VM_NEW(ctx, n, flag, cid) { \
		knh_sfp_t *mbp_ = ((Context*)ctx)->ebp - (n) ; \
		KNH_ASSERT(IS_Method(mbp_[0].mtd)); \
		KNH_SETv(ctx, mbp_[1].o, new_Object__init(ctx, flag, cid)); \
		mbp_[0].op = (knh_stackop_t)pc; \
		(mbp_[0].mtd)->fcall_1(ctx, mbp_ + 1); \
		mbp_[0].op = 0; \
		((Context*)ctx)->ebp = mbp_; \
	} \

#define VM_MT(ctx, mn) { \
		knh_sfp_t *mbp_ = ((Context*)ctx)->ebp; \
		KNH_SETv(ctx, ebp_(0), knh_Object_movableText(ctx, ebp_(0), mn, KNH_NULL)); \
		((Context*)ctx)->ebp = mbp_; \
	} \

#define VM_MT__OPT(ctx, mn, opt) { \
		knh_sfp_t *mbp_ = ((Context*)ctx)->ebp; \
		KNH_SETv(ctx, ebp_(0), knh_Object_movableText(ctx, ebp_(0), mn, opt)); \
		((Context*)ctx)->ebp = mbp_; \
	} \

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

#define VM_JMP(ctx, PC, JUMP) \
	PC; \
	goto JUMP; \
	
#define VM_SKIP(ctx, PC, JUMP) \
	if(!knh_Object_isDebug(sfp[0].o)) { \
		VM_JMP(ctx, PC, JUMP); \
	} \

#define VM_JMP_IFT(ctx, PC, JUMP, v) \
	if(BOOL_ISTRUE(v)) { \
		VM_JMP(ctx, PC, JUMP); \
	} \

#define VM_JMP_IFF(ctx, PC, JUMP, v) \
	if(BOOL_ISFALSE(v)) { \
		VM_JMP(ctx, PC, JUMP); \
	} \

#define VM_JMP_IFTNN(ctx, PC, JUMP, v) \
	if(IS_TRUE(v)) { \
		VM_JMP(ctx, PC, JUMP); \
	} \

#define VM_JMP_IFFN(ctx, PC, JUMP, v) \
	if(IS_FALSE(v)) { \
		VM_JMP(ctx, PC, JUMP); \
	} \

#define VM_JMP_IFN(ctx, PC, JUMP, v) \
	if(IS_NULL(v)) { \
		VM_JMP(ctx, PC, JUMP); \
	} \

#define VM_JMP_IFNN(ctx, PC, JUMP, v) \
	if(IS_NOTNULL(v)) { \
		VM_JMP(ctx, PC, JUMP); \
	} \

#define _VM_ITER_(ctx, cid, it) \
	KNH_SETv(ctx, it, knh_typesystem_foreach(ctx, it, cid)); \
	
#define _VM_FOREACH_(ctx, PC, JUMP, cid, v, it) \
	KNH_SETv(ctx, v, knh_Iterator_foreach(ctx, it, cid)); \
	if(IS_NULL(v)) { \
		VM_JMP(ctx, PC, JUMP); \
	} \

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

#define VM_TRY_BEGIN(ctx, PC, JUMP, hdr, e)  \
	if(!knh_ExceptionHandler_isCatching(((ExceptionHandler*)(hdr)))) { \
		if(KNH_SETJUMP(((ExceptionHandler*)(hdr))) != 0) { \
			KNH_SETv(ctx, e, DP(((ExceptionHandler*)(hdr)))->caught); \
			VM_JMP(ctx, PC, JUMP); \
		} \
		knh_ExceptionHandler_setCatching(((ExceptionHandler*)(hdr)), 1); \
	} \

#define VM_TRY_END(ctx, hdr) \
	knh_ExceptionHandler_setCatching(((ExceptionHandler*)(hdr)), 0) \

#define _VM_CATCH_(ctx, PC, JUMP, exptid, e) \
	if(!knh_texpt_isa(DP((Exception*)e)->eid, exptid)) { \
		VM_JMP(ctx, PC, JUMP); \
	} \
	knh_Exception_setCaught(e, 1); \

#define VM_THROW(ctx, e) \
	if(IS_bString((e))) { \
		knh_konohavm_throwException(ctx, new_Exception(ctx, (String*)(e))); \
	}else if(IS_Exception((e))) { \
		knh_konohavm_throwException(ctx, (Exception*)(e)); \
	}else { \
		knh_konohavm_throwException(ctx, new_Exception__type(ctx, (e), CLASS_Exception)); \
	} \

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

#define VM_PRINT(ctx, flag, s, mn, v) \
	knh_sfp_print(ctx, sfp, flag, s, mn, v)

#define VM_STACKDUMP(ctx)   knh_sfp_dump(ctx, sfp)

#define VM_NOP(ctx)

#ifdef OLD /******/

#define VM_RSHIFT(ctx,n) \
	((Context*)ctx)->ebp -= (n); \

#define VM_PRINT(ctx, mn, v) \
	knh_stackf_print(ctx, mn, v)

#define VM_PUSH_CALLED(ctx) \
	((Context*)ctx)->ebp++; \

#define VM_CALL_IDX(ctx, n) \
	VM_MOV(ctx, ebp_(-1), KNH_FIELDn(ebp_(-1), n)); \
	((Context*)ctx)->ebp--; \

#define VM_CALL_OMT0(ctx, mn) \
	KNH_SETv(ctx, ebp_(-1), knh_Object_toString(ctx, ebp_(-1), mn, KNH_NULL)); \
	((Context*)ctx)->ebp--; \

#define VM_CALL_OMT(ctx, mn, v) \
	KNH_SETv(ctx, ebp_(-1), knh_Object_toString(ctx, ebp_(-1), mn, v)); \
	((Context*)ctx)->ebp--; \

#define VM_CALL_IMT(ctx, mn, v) \
	KNH_SETv(ctx, ebp_(-1), knh_Iterator_toString(ctx, ebp_(-1), mn, v)); \
	((Context*)ctx)->ebp--; \

#define VM_FIND_MAP(ctx, cid)  \
	KNH_SETv(ctx, ebp_(-1), knh_mapdb_ufind(ctx, ebp_(-1), knh_Object_pcid(ebp_(-2)), cid)); \

#define VM_SETLINE(ctx, n) \
	knh_stackf_setline(ctx, sf, n)

#define VM_SYSLOG(ctx, type, flag, v) \
	knh_stackf_syslog(ctx, sf, type, flag, VM_FILE, VM_LINE, v)

#define VM_DIAGNOSE(ctx, n) \
	knh_stackf_diagnose(ctx, sf, n)

#endif

#ifdef __cplusplus 
}
#endif

#endif /*KONOHA_VM_H_*/
