/* frame.h
   Copyright (C) 2005 Free Software Foundation, Inc.

This file is part of Mysaifu JVM

Mysaifu JVM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

Mysaifu JVM is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
*/

#ifndef FRAME_H_INCLUDED
#define FRAME_H_INCLUDED

#include "types.h"
#include "ClassFile.h"

/**
 * フレームの本体。
 * 1スレッドにつき１つ作成する。
 */
struct frame_body {
	/**
	 * フレーム内の要素数（32ビット単位）
	 */
	unsigned int count;

	/**
	 * フレーム内のリミット
	 */
	unsigned int limit_count;

	/**
	 * フレームのデータ本体
	 */
	__int32* data;

	/**
	 * 参照値が入る領域
	 */
	__int32* references;

	/**
	 * カレントフレーム
	 */
	frame* current_frame;
	
	/**
	 * 直前に実行したinstanceof演算子の左辺
	 */
	ClassFile* instanceof_lvalue;

	/**
	 * 直前に実行されたinstanceof演算子の右辺
	 */
	const java_utf8* instanceof_rvalue;
	
	/**
	 * 直前に実行されたinstanceof演算子の結果
	 */
	int instanceof_result;

	/**
	 * カレントスレッド
	 */
	jobject current_thread;

	/**
	 * JNIEnv
	 */
	JNIEnv* env;

	/**
	 * ローカル参照
	 */
	frame* local_references_frame;

	/**
	 * 例外。
	 */
	jthrowable current_exception;
};


/**
 * フレーム構造体
 * メソッド呼び出し単位で作成する
 */
struct frame {
	/**
	 * スタックフレームの本体
	 */
	frame_body* body;

	/**
	 * １つ手前のフレーム
	 */
	frame* previous_frame;

	/**
	 * カレントクラス
	 */
	ClassFile* current_class_file;
	
	/**
	 * カレントメソッド
	 */
	method_info* current_method_info;

	/**
	 * カレントプログラム・カウンタ
	 */
	u2 current_pc;

	/**
	 * ローカル変数の先頭アドレス
	 */
	__int32* local_variables;

	/**
	 * ローカル変数の参照値フラグ
	 */
	__int32* local_variables_references;

	/**
	 * オペランド・スタックの先頭アドレス
	 */
	__int32* operand_stack;
	
	/**
	 * オペランド・スタックの参照値フラグ
	 */
	__int32* operand_stack_references;

	/**
	 * オペランド・スタックの現在位置
	 */
	unsigned int operand_stack_position;

	/**
	 * synchronizedメソッドの場合のlockオブジェクト
	 */
	jobject lock;
};

/**
 * 高速なistore/fstore命令
 * （試験的に実装）
 */
#define FAST_STORE(stack_base, pposition, local_base, index) \
{ \
	(*pposition)--; \
	local_base[(index)] = stack_base[*pposition]; \
}

/**
 * store命令
 */
#define STORE(stack, index) \
{ \
	const unsigned int stack_position = (stack)->operand_stack_position - 1; \
	(stack)->local_variables[(index)] = (stack)->operand_stack[stack_position]; \
	(stack)->operand_stack_position = stack_position; \
}

#define STORE2(stack, index) \
{ \
	const unsigned int stack_position = (stack)->operand_stack_position - 2; \
	*((__int64*) (&(stack)->local_variables[index])) \
			= *((__int64*) &(stack)->operand_stack[stack_position]); \
	(stack)->operand_stack_position = stack_position; \
}

/**
 * astore命令
 */
#define ASTORE_N(stack, index) \
{ \
	const unsigned int stack_position = (stack)->operand_stack_position - 1; \
	(stack)->local_variables_references[(index)] = \
	    (stack)->local_variables[(index)] = \
		    (stack)->operand_stack[stack_position]; \
	(stack)->operand_stack_position = stack_position; \
}

/**
 * 高速なiload,fload 命令
 * （試験的に実装）
 */
#define FAST_LOAD(stack_base, pposition, local_base, index) \
{ \
	stack_base[*pposition] = local_base[(index)]; \
	(*pposition)++; \
}

 /**
 * load 命令
 */
#define LOAD(stack, index) \
{ \
	const unsigned int stack_position = (stack)->operand_stack_position; \
	(stack)->operand_stack[stack_position] = (stack)->local_variables[(index)]; \
	(stack)->operand_stack_position++; \
}

#define LOAD2(stack, index) \
{ \
	const unsigned int stack_position = (stack)->operand_stack_position; \
	*((__int64*) (&(stack)->operand_stack[stack_position])) \
			= *((__int64*) &(stack)->local_variables[index]); \
	(stack)->operand_stack_position += 2; \
}

/**
 * aload 命令
 */
#define ALOAD_N(stack, index) \
{ \
	unsigned int stack_position = (stack)->operand_stack_position; \
	(stack)->operand_stack_references[stack_position] = \
	    (stack)->operand_stack[stack_position] = \
		    (stack)->local_variables[(index)]; \
	(stack)->operand_stack_position++; \
}

#define	POP_DISCARD(stack) \
{ \
	(stack)->operand_stack_position--; \
}

#define POP2_DISCARD(stack) \
{ \
	(stack)->operand_stack_position -= 2; \
}

#define FAST_POP_DISCARD_N(pposition, n) \
{ \
	*pposition -= n; \
}

#define POP_DISCARD_N(stack, n) \
{ \
	(stack)->operand_stack_position -= n; \
}

/**
 * 高速なPUSH_INT
 * (試験的に実装)
 */
#define	FAST_PUSH_INT(stack_base, pposition, value) \
{ \
    stack_base[*pposition] = (value); \
	(*pposition)++; \
}

#define	PUSH_INT(stack, value) \
{ \
    const unsigned int position = (stack)->operand_stack_position; \
    (stack)->operand_stack[position] = (value); \
	(stack)->operand_stack_position++; \
}

#define	PUSH_LONG(stack, value) \
{ \
	*((__int64*) &(stack)->operand_stack[(stack)->operand_stack_position]) = (value); \
	(stack)->operand_stack_position += 2; \
}

/**
 * 高速なPUSH_FLOAT
 * (試験的に実装)
 */
#define	FAST_PUSH_FLOAT(stack_base, pposition, value) \
{ \
    *((float*) &stack_base[*pposition]) = (value); \
	(*pposition)++; \
}

#define PUSH_FLOAT(stack, value) \
{ \
	*((float*) &(stack)->operand_stack[(stack)->operand_stack_position]) = (value); \
	(stack)->operand_stack_position++; \
}

#define PUSH_DOUBLE(stack, value) \
{ \
	*((double*) &(stack)->operand_stack[(stack)->operand_stack_position]) = (value); \
	(stack)->operand_stack_position += 2; \
}

#define PUSH_OBJECT(stack, value) \
{ \
	const unsigned int position = (stack)->operand_stack_position; \
	(stack)->operand_stack_references[position] = \
		(stack)->operand_stack[position] = (__int32) (value); \
    (stack)->operand_stack_position++; \
}

/**
 * 高速なPOP_INT
 * (試験的に実装）
 */
#define FAST_POP_INT(stack_base, pposition, value) \
{ \
	(*pposition)--; \
	value = stack_base[*pposition]; \
}

#define POP_INT(stack, value) \
{ \
	(stack)->operand_stack_position--; \
	value = (stack)->operand_stack[(stack)->operand_stack_position]; \
}

#define POP_LONG(stack, value) \
{ \
	(stack)->operand_stack_position -= 2; \
	value = *((__int64*) &(stack)->operand_stack[(stack)->operand_stack_position]); \
}

/**
 * 高速なPOP_FLOAT
 * （試験的に実装）
 */
#define FAST_POP_FLOAT(stack_base, pposition, value) \
{ \
	(*pposition)--; \
	value = *((float*) &stack_base[(*pposition)]); \
}

#define POP_FLOAT(stack, value) \
{ \
	(stack)->operand_stack_position--; \
	value = *((float*) &(stack)->operand_stack[(stack)->operand_stack_position]); \
}

#define POP_DOUBLE(stack, value) \
{ \
	(stack)->operand_stack_position -= 2; \
	value = *((double*) &(stack)->operand_stack[(stack)->operand_stack_position]); \
}

#define POP_OBJECT(stack, value) \
{ \
	(stack)->operand_stack_position--; \
	value = *((jobject*) &(stack)->operand_stack[(stack)->operand_stack_position]); \
}

/**
 * 高速なiinc
 */
#define	FAST_IINC_LOCAL(local_base, index, count) \
{ \
	local_base[(index)] += (count); \
}

#define IINC_LOCAL(stack, index, count) \
{ \
	(stack)->local_variables[(index)] += count; \
}

/**
 * オペランド・スタックに対して "dup" 命令を実行する
 */
#define DUP_STACK(stack) \
{ \
	const unsigned int position = (stack)->operand_stack_position; \
	/* referencesのコピーは不要かもしれない（コピー元のフラグがあるため） */ \
	(stack)->operand_stack_references[position] = \
	    (stack)->operand_stack_references[position - 1]; \
	(stack)->operand_stack[position] = \
		  (stack)->operand_stack[position - 1]; \
	(stack)->operand_stack_position++; \
}

/**
 * オペランドスタックに対して ADD 命令を実行する(カテゴリ１）
 */
#define ADD_STACK(stack, type) \
{ \
	/* アドレス計算処理を出来る限り少なくする */ \
	type * pdata = (type*) &stack->operand_stack[stack->operand_stack_position]; \
	*(pdata - 2) += *(pdata - 1); \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して ADD 命令を実行する(カテゴリ２）
 */
#define ADD2_STACK(stack, type) \
{ \
	/* アドレス計算処理を出来る限り少なくする */ \
	type * pdata = (type*) &stack->operand_stack[stack->operand_stack_position]; \
	*(pdata - 2) += *(pdata - 1); \
	POP2_DISCARD(stack); \
}

/**
 * オペランドスタックに対して SUB 命令を実行する(カテゴリ１）
 */
#define SUB_STACK(stack, type) \
{ \
	/* アドレス計算処理を出来る限り少なくする */ \
	type * pdata = (type*) &stack->operand_stack[stack->operand_stack_position]; \
	*(pdata - 2) -= *(pdata - 1); \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して SUB 命令を実行する(カテゴリ２）
 */
#define SUB2_STACK(stack, type) \
{ \
	/* アドレス計算処理を出来る限り少なくする */ \
	type * pdata = (type*) &stack->operand_stack[stack->operand_stack_position]; \
	*(pdata - 2) -= *(pdata - 1); \
	POP2_DISCARD(stack); \
}

/**
 * オペランドスタックに対して MUL 命令を実行する(カテゴリ１）
 */
#define MUL_STACK(stack, type) \
{ \
	/* アドレス計算処理を出来る限り少なくする */ \
	type * pdata = (type*) &stack->operand_stack[stack->operand_stack_position]; \
	*(pdata - 2) *= *(pdata - 1); \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して MUL 命令を実行する(カテゴリ２）
 */
#define MUL2_STACK(stack, type) \
{ \
	/* アドレス計算処理を出来る限り少なくする */ \
	type * pdata = (type*) &stack->operand_stack[stack->operand_stack_position]; \
	*(pdata - 2) *= *(pdata - 1); \
	POP2_DISCARD(stack); \
}

/**
 * オペランドスタックに対して iand 命令を実行する
 */
#define IAND_STACK(stack) \
{ \
	jint* pvalue = &stack->operand_stack[stack->operand_stack_position - 1]; \
	*(pvalue - 1) &= *pvalue; \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して ior 命令を実行する
 */
#define IOR_STACK(stack) \
{ \
	jint* pvalue = &stack->operand_stack[stack->operand_stack_position - 1]; \
	*(pvalue - 1) |= *pvalue; \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して ixor 命令を実行する
 */
#define IXOR_STACK(stack) \
{ \
	jint* pvalue = &stack->operand_stack[stack->operand_stack_position - 1]; \
	*(pvalue - 1) ^= *pvalue; \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して iand 命令を実行する
 */
#define LAND_STACK(stack) \
{ \
	jlong* pvalue = (jlong*) &stack->operand_stack[stack->operand_stack_position - 2]; \
	*(pvalue - 1) &= *pvalue; \
	POP2_DISCARD(stack); \
}

/**
 * オペランドスタックに対して lor 命令を実行する
 */
#define LOR_STACK(stack) \
{ \
	jlong* pvalue = (jlong*) &stack->operand_stack[stack->operand_stack_position - 2]; \
	*(pvalue - 1) |= *pvalue; \
	POP2_DISCARD(stack); \
}

/**
 * オペランドスタックに対して lxor 命令を実行する
 */
#define LXOR_STACK(stack) \
{ \
	jlong* pvalue = (jlong*) &stack->operand_stack[stack->operand_stack_position - 2]; \
	*(pvalue - 1) ^= *pvalue; \
	POP2_DISCARD(stack); \
}

/**
 * オペランドスタックに対して ishl 命令を実行する
 */
#define ISHL_STACK(stack) \
{ \
	jint* pvalue = &stack->operand_stack[stack->operand_stack_position - 1]; \
	/* シフト回数を0x1fでマスクする */ \
	*(pvalue - 1) <<= (*pvalue & 0x1f); \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して ishr 命令を実行する
 */
#define ISHR_STACK(stack) \
{ \
	jint* pvalue = &stack->operand_stack[stack->operand_stack_position - 1]; \
	/* シフト回数を0x1fでマスクする */ \
	*(pvalue - 1) >>= (*pvalue & 0x1f); \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して ishr 命令を実行する
 */
#define IUSHR_STACK(stack) \
{ \
	jint* pvalue = &stack->operand_stack[stack->operand_stack_position - 1]; \
	/* シフト回数を0x1fでマスクする */ \
	*((unsigned __int32*) pvalue - 1) >>= (*pvalue & 0x1f); \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して f2i 命令を実行する
 */
#define I2F_STACK(stack) \
{ \
    jint* pdata = (jint*) &stack->operand_stack[stack->operand_stack_position - 1]; \
	*((jfloat*) pdata) = (jfloat) *pdata; \
}

/**
 * オペランドスタックに対して l2i 命令を実行する
 */
#define L2I_STACK(stack) \
{ \
    jlong* pdata = (jlong*) &stack->operand_stack[stack->operand_stack_position - 2]; \
	*((jint*) pdata) = (jint) *pdata; \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して l2f 命令を実行する
 */
#define L2F_STACK(stack) \
{ \
    jlong* pdata = (jlong*) &stack->operand_stack[stack->operand_stack_position - 2]; \
	*((jfloat*) pdata) = (jfloat) *pdata; \
	POP_DISCARD(stack); \
}

/**
 * オペランドスタックに対して l2d 命令を実行する
 */
#define L2D_STACK(stack) \
{ \
    jlong* pdata = (jlong*) &stack->operand_stack[stack->operand_stack_position - 2]; \
	*((jdouble*) pdata) = (jdouble) *pdata; \
}

/**
 * オペランドスタックに対して f2i 命令を実行する
 */
#define F2I_STACK(stack) \
{ \
	/* スタックトップの値を float から int にキャストする */ \
    jfloat* pdata = (jfloat*) &stack->operand_stack[stack->operand_stack_position - 1]; \
	*((jint*) pdata) = (jint) *pdata; \
}

/**
 * オペランドスタックに対して d2f 命令を実行する
 */
#define D2F_STACK(stack) \
{ \
	/* スタックトップの値を double から float にキャストする */ \
    jdouble* pdata = (jdouble*) &stack->operand_stack[stack->operand_stack_position - 2]; \
	*((jfloat*) pdata) = (jfloat) *pdata; \
	POP_DISCARD(stack); \
}

/**
 * オペランド・スタックの任意の位置のデータを取得する
 *
 * @param	stack	スタック・フレーム。
 * @param	offset	スタックトップを０とした相対アドレスを指定する
 * @param	pvalue	取得したデータを格納するアドレス。
 */
#define PEEK_STACK_DATA(stack, offset, type, value) \
{ \
	value = *(type*) &(stack)->operand_stack[(stack)->operand_stack_position - offset - 1]; \
}

/**
 * オペランド・スタックの任意の位置のデータを取得する（高速版）
 *
 * @param	stack_base
 * @param	pposition	スタックポインタ
 * @param	offset		スタックトップを０とした相対アドレスを指定する
 * @param	type		読み取るデータのタイプ
 * @param	pvalue		取得したデータを格納するアドレス。
 */
#define FAST_PEEK_STACK_DATA(stack_base, pposition, offset, type, value) \
{ \
	value = *(type*) &stack_base[*pposition - offset - 1]; \
}

/**
 * frame関連の変数を初期化する
 */
void init_frame_settings(unsigned int java_stack_size);

/**
 * 一番先頭のフレームをセットアップする
 * スレッドの最初のメソッドを呼び出す前に、この関数を使用して最初のスタックフレームを
 * 作成する
 */
frame* alloc_root_frame(ClassFile* current_class_file);
frame* alloc_root_frame(ClassFile* current_class_file, unsigned int max_count);

/**
 * 一番先頭（底）のフレームを開放する
 * スレッドの終了後、この関数を呼び出してメモリを開放する
 */
void free_root_frame(frame* root_frame);

/**
 * JVM上に存在するルートフレームの数を返す
 *
 * @return	ルートフレーム数
 */
unsigned int get_root_frame_count();

/**
 * 指定されたインデックスに対応するルートスタックフレームを返す
 *
 * @param	index	インデックス
 * @return	ルートスタックフレームへのポインタ
 */
frame* get_root_frame(unsigned int index);

/**
 * 指定されたルートフレームの情報を返す。
 *
 * @param	root_frame	ルートフレーム
 * @param	pdata		フレーム内データへのポインタを返す。
 * @param	prefflags	参照値フラグのポインタを返す。
 * @param	pcount		フレーム内の有効データ数を返す。
 * @param	pcurrent_exception	カレントの例外を返す。
 * @return	正常にデータを取得できた場合true
 */
bool get_root_frame_info(frame* root_frame, __int32** pdata, __int32** preferences, unsigned int* pcount, jobject* pcurrent_exception);

/**
 * メソッド呼び出し前にフレームを初期化する。
 * メソッド呼び出しを行う前にこの関数を呼び出し、フレームを作成する。
 */
frame* enter_method(frame* caller_frame, ClassFile* target_class_file, method_info* target_method_info);

/**
 * メソッドから離れる。
 * メソッドが終了する際にこの関数を呼び出し、フレームを破棄する
 */
frame* leave_method(frame* current_frame);

/**
 * "dup_x1"命令を実行する
 */
void dup_x1(frame* stack);

/**
 * "dup_x2"命令を実行する
 */
void dup_x2(frame* stack);


/**
 * "dup2"命令を実行する
 */
void dup2(frame* stack);

/**
 * "dup2_x1"命令を実行する
 */
void dup2_x1(frame* stack);

/**
 * "dup2_x2"命令を実行する
 */
void dup2_x2(frame* stack);

/**
 * "swap"命令を実行する
 */
void swap(frame* stack);

/**
 * オペランド・スタックの任意の位置のデータを取得する
 *
 * @param	stack	フレーム。
 * @param	offset	スタックトップを０とした相対アドレスを指定する
 * @param	pvalue	取得したデータを格納するアドレス。
 */
inline void get_stack_data(frame* stack, unsigned int offset, __int32* pvalue) {
	unsigned int pos = stack->operand_stack_position - 1 - offset;
	*pvalue = stack->operand_stack[pos];
}

/**
 * 指定されたインデックスに対応するローカル変数の値を返す
 */
inline __int32 get_int(frame* frame, int index) {
	return (int) frame->local_variables[index];
}

/**
 * 新しいローカル参照を作成する（フレームに格納し、ガベージコレクトされないようにする）
 */
jobject new_local_reference(frame* current_frame, jobject ref);

/**
 * 指定されたローカル参照を削除する（ガベージコレクト対象とする）
 */
void delete_local_reference(frame* current_frame, jobject ref);

/**
 * 新しいグローバル参照を作成する（ガベージコレクトされないようにする）
 */
jobject new_global_reference(jobject ref);

/**
 * 指定されたグローバル参照を削除する（ガベージコレクト対象とする）
 */
void delete_global_reference(jobject ref);

/**
 * 指定されたフレームに対応するスレッドのカレントフレームを返す。
 */
frame* get_current_frame(frame* frm);

/**
 * instanceofの結果をキャッシュに入れる
 */
void update_instanceof_cache(frame* frm, ClassFile* lvalue, const java_utf8* rvalue, int result);

/**
 * instanceofの結果をキャッシュから取得する
 * 負数が帰った場合にはキャッシュ内にヒットする条件が存在しないことを示す。
 */
int get_instanceof_cache(frame* frm, ClassFile* lvalue, const java_utf8* rvalue);

/**
 * 指定された frame のカレントスレッドを指定する
 * １つのframeにつき、この関数は1回しか呼び出せない。（2回目以降の呼び出しは無視される）
 */
void set_current_thread(frame* frm, jobject thread);

/**
 * 指定された frame のカレントスレッドを返す
 */
jobject get_current_thread(frame* frm);

/**
 * 指定されたThreadオブジェクトに対応するframeオブジェクトを返す
 * 対応するframeが存在しない場合はNULLを返す。
 */
frame* get_current_frame_of(jobject thread);

bool ensure_stack_capacity(frame* frm);

/**
 * JNIEnv* を取得する
 */
JNIEnv* get_JNIEnv(frame* frm);

/**
 * JNIEnv* を設定する
 */
void set_JNIEnv(frame* frm, JNIEnv* env);

/**
 * ローカル参照用frameをpushする
 */
int push_local_frame(frame* current_frame, frame* new_frame, int capacity);

/**
 * ローカル参照用 frame を pop する
 */
jobject pop_local_frame(frame* current_frame, jobject ref);

#endif