/* instruction.cpp
   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.
*/

#include "StdAfx.h"
#include "ClassFile.h"
#include "frame.h"
#include "java_object.h"
#include "class_loader.h"
#include "instruction.h"
#include "jni_funcs.h"
#include "monitor.h"
#include "java_utf8.h"
#include "common_funcs.h"
#include <Armintr.h>

// デバッグマクロ
#ifdef DEBUG
void dbg_utf8(const java_utf8* msg) {
	int bufflen = strlen(msg) * 2 + 1;
	TCHAR* buff = (TCHAR*) alloca(sizeof(TCHAR) * bufflen);
	convert_to_TCHAR(msg, buff, bufflen);
	DBG(buff);
}

void dbg_int(int i) {
	int bufflen = 12;
	TCHAR* buff = (TCHAR*) alloca(sizeof(TCHAR) * bufflen);
	_stprintf(buff, _T("%i"), i);
	DBG(buff);
}

void dbg_uint(unsigned int i) {
	int bufflen = 12;
	TCHAR* buff = (TCHAR*) alloca(sizeof(TCHAR) * bufflen);
	_stprintf(buff, _T("%u"), i);
	DBG(buff);
}

static TCHAR* g_instruction_names[256] = {
	_T("nop"),
	_T("aconst_null"),
	_T("iconst_m1"),
	_T("iconst_0"),
	_T("iconst_1"),
	_T("iconst_2"),
	_T("iconst_3"),
	_T("iconst_4"),
	_T("iconst_5"),
	_T("lconst_0"),
	_T("lconst_1"),
	_T("fconst_0"),
	_T("fconst_1"),
	_T("fconst_2"),
	_T("dconst_0"),
	_T("dconst_1"),
	_T("bipush"),
	_T("sipush"),
	_T("ldc"),
	_T("ldc_w"),
	_T("ldc2_w"),
	_T("iload"),
	_T("lload"),
	_T("fload"),
	_T("dload"),
	_T("aload"),
	_T("iload_0"),
	_T("iload_1"),
	_T("iload_2"),
	_T("iload_3"),
	_T("lload_0"),
	_T("lload_1"),
	_T("lload_2"),
	_T("lload_3"),
	_T("fload_0"),
	_T("fload_1"),
	_T("fload_2"),
	_T("fload_3"),
	_T("dload_0"),
	_T("dload_1"),
	_T("dload_2"),
	_T("dload_3"),
	_T("aload_0"),
	_T("aload_1"),
	_T("aload_2"),
	_T("aload_3"),
	_T("iaload"),
	_T("laload"),
	_T("faload"),
	_T("daload"),
	_T("aaload"),
	_T("baload"),
	_T("caload"),
	_T("saload"),
	_T("istore"),
	_T("lstore"),
	_T("fstore"),
	_T("dstore"),
	_T("astore"),
	_T("istore_0"),
	_T("istore_1"),
	_T("istore_2"),
	_T("istore_3"),
	_T("lstore_0"),
	_T("lstore_1"),
	_T("lstore_2"),
	_T("lstore_3"),
	_T("fstore_0"),
	_T("fstore_1"),
	_T("fstore_2"),
	_T("fstore_3"),
	_T("dstore_0"),
	_T("dstore_1"),
	_T("dstore_2"),
	_T("dstore_3"),
	_T("astore_0"),
	_T("astore_1"),
	_T("astore_2"),
	_T("astore_3"),
	_T("iastore"),
	_T("lastore"),
	_T("fastore"),
	_T("dastore"),
	_T("aastore"),
	_T("bastore"),
	_T("castore"),
	_T("sastore"),
	_T("pop"),
	_T("pop2"),
	_T("dup"),
	_T("dup_x1"),
	_T("dup_x2"),
	_T("dup2"),
	_T("dup2_x1"),
	_T("dup2_x2"),
	_T("swap"),
	_T("iadd"),
	_T("ladd"),
	_T("fadd"),
	_T("dadd"),
	_T("isub"),
	_T("lsub"),
	_T("fsub"),
	_T("dsub"),
	_T("imul"),
	_T("lmul"),
	_T("fmul"),
	_T("dmul"),
	_T("idiv"),
	_T("ldiv"),
	_T("fdiv"),
	_T("ddiv"),
	_T("irem"),
	_T("lrem"),
	_T("frem"),
	_T("drem"),
	_T("ineg"),
	_T("lneg"),
	_T("fneg"),
	_T("dneg"),
	_T("ishl"),
	_T("lshl"),
	_T("ishr"),
	_T("lshr"),
	_T("iushr"),
	_T("lushr"),
	_T("iand"),
	_T("land"),
	_T("ior"),
	_T("lor"),
	_T("ixor"),
	_T("lxor"),
	_T("iinc"),
	_T("i2l"),
	_T("i2f"),
	_T("i2d"),
	_T("l2i"),
	_T("l2f"),
	_T("l2d"),
	_T("f2i"),
	_T("f2l"),
	_T("f2d"),
	_T("d2i"),
	_T("d2l"),
	_T("d2f"),
	_T("i2b"),
	_T("i2c"),
	_T("i2s"),
	_T("lcmp"),
	_T("fcmpl"),
	_T("fcmpg"),
	_T("dcmpl"),
	_T("dcmpg"),
	_T("ifeq"),
	_T("ifne"),
	_T("iflt"),
	_T("ifge"),
	_T("ifgt"),
	_T("ifle"),
	_T("if_icmpeq"),
	_T("if_icmpne"),
	_T("if_icmplt"),
	_T("if_icmpge"),
	_T("if_icmpgt"),
	_T("if_icmple"),
	_T("if_acmpeq"),
	_T("if_acmpne"),
	_T("goto"),
	_T("jsr"),
	_T("ret"),
	_T("tableswitch"),
	_T("lookupswitch"),
	_T("ireturn"),
	_T("lreturn"),
	_T("freturn"),
	_T("dreturn"),
	_T("areturn"),
	_T("return"),
	_T("getstatic"),
	_T("putstatic"),
	_T("getfield"),
	_T("putfield"),
	_T("invokevirtual"),
	_T("invokespecial"),
	_T("invokestatic"),
	_T("invokeinterface"),
	_T("xxxunusedxxx1"),
	_T("new"),
	_T("newarray"),
	_T("anewarray"),
	_T("arraylength"),
	_T("athrow"),
	_T("checkcast"),
	_T("instanceof"),
	_T("monitorenter"),
	_T("monitorexit"),
	_T("wide"),
	_T("multianewarray"),
	_T("ifnull"),
	_T("ifnonnull"),
	_T("goto_w"),
	_T("jsr_w"),
	_T("breakpoint"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("(UNDEFINED)"),
	_T("impdep1"),
	_T("impdep2"),
};

#endif /* DEBUG */

#ifdef DEBUG
/**
 * checkcast命令の統計情報を取得する場合には０以外の値を定義する
 */
#define CHECKCAST_STATISTICS	0

#if CHECKCAST_STATISTICS
 // checkcast命令の統計情報を取る
 static unsigned int g_checkcast_count;
 static unsigned int g_checkcast_hits;
#endif /* CHECKCAST_STATISTICS */

#endif /* DEBUG */

/**
 * OutOfMemoryErrorのインスタンス
 * JVM起動時に確保しておく
 */
static jthrowable g_OutOfMemoryError_instance;

/**
 * StackOverflowErrorのインスタンス
 * JVM起動時に確保しておく
 */
static jthrowable g_StackOverflowError_instance;

// static関数の宣言
static unsigned int find_exception_handler(frame* rt, Code_attribute* code_attr);
static void run_code(frame* rt, Code_attribute* code_attr);
static void invoke_native_method(frame* caller_frame, ClassFile* target_class_file, method_info* target_method_info);
static jarray create_multiarray(frame* current_frame,
								   class_loader* loader,
								   const char* class_name,
								   u1 dimensions,
								   __int32* counts,
								   frame* tmp_frame);
static bool run_code(ClassFile* cfile, java_utf8* name);
static jstring alloc_utf8_string(frame* frame, const java_utf8* utf8, bool permanent);

/**
 * 命令実行関数
 */
static void execute_tableswitch(frame* frame, u1* bytecode);
static void execute_lookupswitch(frame* frame, u1* bytecode);
static void execute_getstatic(frame* frame, u1* bytecode);
static void execute_putstatic(frame* frame, u1* bytecode);
static void execute_arraylength(frame* frame, u1* bytecode);
static void execute_athrow(frame* frame, u1* bytecode);
static void execute_wide(frame* frame, u1* bytecode);
static void execute_multianewarray(frame* frame, u1* bytecode);
static inline void execute_ldc_w(frame* current_frame, u1 index1, u1 index2);
static void execute_jsr(frame* current_frame, __int32 branch);

/**
 * 命令実行関数用ユーティリティ関数
 */
#define GET_PARAMETER(bytecode, index) bytecode[index]

#define MAKE_INT16(index1, index2) \
	((__int16) index1 << 8) | (index2 & 0x00ff)

#define MAKE_U2(index1, index2) \
	(((u2) index1 << 8) | (index2 & 0x00ff))

#define MAKE_INT32(b1, b2, b3, b4) \
	(((__int32) b1 << 24) | ((__int32) b2 << 16) | ((__int32) b3 << 8) | ((__int32) b4))

#define READ_BIG_ENDIAN(value) \
	((value << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | ((value & 0xff000000) >> 24))

#define EXCEPTION_OCCURRED(current_frame)	(current_frame->current_exception)

#define NO_EXCEPTION_HANDLER	(0xffffffff)

/**
 * static変数
 */
static const java_utf8* g_Math_class_name;
static const java_utf8* g_Math_sin_method_name;
static const java_utf8* g_Math_cos_method_name;
static const java_utf8* g_Math_sqrt_method_name;

/**
 * 命令実行環境を初期化する
 */
void init_instruction_settings() {
	// Math関連クラス処理の高速化を行う
	g_Math_class_name = intern_utf8("java/lang/Math");
	g_Math_sin_method_name = intern_utf8("sin");
	g_Math_cos_method_name = intern_utf8("cos");
	g_Math_sqrt_method_name = intern_utf8("sqrt");
}

/**
 * OutOfMemoryErrorのインスタンスを準備しておく
 */
bool prepare_OutOfMemoryError() {
	frame* frm = alloc_root_frame(NULL);
	ClassFile* cfile = find_ClassFile(frm, intern_utf8("java/lang/OutOfMemoryError"));
	if (cfile == NULL) {
		return false;
	}
	// メモリを確保し、オペランドスタック上にリファレンスを積む
	jobject obj;
	if ((obj = alloc_object(cfile, frm)) == NULL) {
		return false;
	}
	DUP_STACK(frm);

	// 引数なしコンストラクタを探す
	method_info* constructor = find_declaring_method_info(cfile, INIT_METHOD_NAME, VOID_NOARG_METHOD_DESCRIPTOR);
	if (constructor == NULL) {
		POP2_DISCARD(frm);
		return false;
	}
	
	// コンストラクタ起動
	invoke_method(frm, obj, cfile, constructor);

	// 生成した参照をグローバル変数に入れておく
	g_OutOfMemoryError_instance = (jthrowable) new_global_reference(obj);
	POP_DISCARD(frm);

	free_root_frame(frm);

	return true;
}

/**
 * StackOverflowErrorのインスタンスを準備しておく
 */
bool prepare_StackOverflowError() {
	frame* frm = alloc_root_frame(NULL);
	ClassFile* cfile = find_ClassFile(frm, intern_utf8("java/lang/StackOverflowError"));
	if (cfile == NULL) {
		return false;
	}
	// メモリを確保し、オペランドスタック上にリファレンスを積む
	jobject obj;
	if ((obj = alloc_object(cfile, frm)) == NULL) {
		return false;
	}
	DUP_STACK(frm);

	// 引数なしコンストラクタを探す
	method_info* constructor = find_declaring_method_info(cfile, INIT_METHOD_NAME, VOID_NOARG_METHOD_DESCRIPTOR);
	if (constructor == NULL) {
		POP2_DISCARD(frm);
		return false;
	}
	
	// コンストラクタ起動
	invoke_method(frm, obj, cfile, constructor);

	// 生成した参照をグローバル変数に入れておく
	g_StackOverflowError_instance = (jthrowable) new_global_reference(obj);
	POP_DISCARD(frm);

	free_root_frame(frm);

	return true;
}

/**
 * 指定されたフレーム内で、pcが示す命令を実行する
 */
static void run_code(frame* current_frame, Code_attribute* code_attr) {
	/**
     * ジャンプ先アドレスを設定するマクロ
	 * 引数 condition が true の場合にのみジャンプする
	 */
	#define SET_JUMP_ADDRESS(current_frame, condition, index1, index2, instruction_length) \
	{ \
		CHECK_EXCEPTION; \
		if (condition) { \
			__int16 offset = MAKE_INT16(index1, index2); \
			bytecode += offset; \
		} else { \
			bytecode += instruction_length; \
		} \
	}

	// 例外チェック用マクロ
	#define CHECK_EXCEPTION	\
	{\
		if (EXCEPTION_OCCURRED(current_frame)) { \
			/* 例外が発生していた場合、例外ハンドラを検索する */ \
			const unsigned int handler_pc = find_exception_handler(current_frame, code_attr); \
			if (handler_pc == NO_EXCEPTION_HANDLER) { \
				/* 例外ハンドラがない場合はリターンする */ \
				goto END; \
			} else { \
				/* 例外ハンドラ有り */ \
				bytecode = code + handler_pc; \
				/* オペランドスタックを空っぽにした後、例外オブジェクトへの参照をpushする */ \
				current_frame->operand_stack_position = 0; \
				PUSH_OBJECT(current_frame, \
							current_frame->current_exception); \
				/* 例外をクリアする */ \
				exception_clear(current_frame); \
				/* ループの先頭に戻る */ \
				continue; \
			} \
		} \
	}

	u1* const code = code_attr->code;
	u1* bytecode = code;
	current_frame->current_bytecode_pointer = &bytecode;

	__int32* const stack_base = current_frame->operand_stack;
	__int32* const local_variables_base = current_frame->local_variables;
	unsigned int* pposition = &current_frame->operand_stack_position;
	
	while (true) {
		switch (*bytecode) {
		default:
			DBG(_T("Unknown instruction "));
			DBG_UINT(*bytecode);
			DBG(_T("\n"));
			assert(false);
			fatal_error(_T("Unknown instruction"));

			// 見通しをよくするため、命令実行関数本体は、別ファイルで定義する
			#include "instruction_inc.h"
			
		}
	}

END:
	;	// 命令実行終了

// この関数内でのみ有効なマクロ
#undef SET_JUMP_ADDRESS
#undef CHECK_EXCEPTION

}

/**
 * ldc/ldc_w命令共通の処理を行う
 */
static inline void execute_ldc_w(frame* current_frame, u1 index1, u1 index2) {
	u2 index = MAKE_U2(index1, index2);
	ClassFile* cfile = current_frame->current_class_file;
	cp_info* cp = cfile->constant_pool[index];

	switch (cfile->constant_pool_tags[index]) {
	case CONSTANT_Integer:
	case CONSTANT_Float:
	{
		// 値をそのままPUSHする
		__int32 value = (__int32) cp;
		PUSH_INT(current_frame, value);
		break;
	}

	case CONSTANT_String:
	{
		jobject string_reference;
		if (IS_SPECIAL_POINTER(cp)) {
			// jstringが入っている
			string_reference = (jstring) MAKE_NORMAL_POINTER(cp);
		} else {
			// java_utf8* が入っている
			java_utf8* utf = (java_utf8*) cp;
			string_reference = intern_string(utf, current_frame);
			if (exception_occurred(current_frame)) {
				return;
			}
			// 値をキャッシュする
			cfile->constant_pool[index] = (cp_info*) MAKE_SPECIAL_POINTER(string_reference);
		}
		PUSH_OBJECT(current_frame, string_reference);

		break;
	}

	case CONSTANT_Class:
	{
		// Java5からldc/ldc_w命令にCONSTANT_Classが許されるようになった

		// 対象となるClassFile構造体へのポインタを得る
		ClassFile* target_class_file = resolve_ClassFile(current_frame,
														 cfile,
														 index);
		if (exception_occurred(current_frame)) {
			// 例外発生
			return;
		}
		// Classオブジェクトを生成する
		jobject clazz = find_class_object(current_frame,
										  get_static_data(target_class_file),
										  NULL);
		PUSH_OBJECT(current_frame, clazz);

		break;
	}

	default:
		// 例外をthrowする
		throw_exception(current_frame, "java/lang/ClassFormatError", "Illegal index in ldc/ldc_w");
		break;
	}
}

/**
 * 指定された次元の配列を作成する
 * 作成中の配列がガベージコレクタによって回収されてしまわないように、引数tmp_stackに
 * 作成中配列への参照をpushする。
 *
 * @param	current_frame	カレントフレーム
 * @param	loader		クラスローダ
 * @param	cname	作成する配列のクラス名
 * @param	dimensions	配列の次元数
 * @param	counts		配列各次元の大きさを格納したint配列へのポインタ
 * @param	tmp_stack	作成中の要素を一時的にpushしておくスタック
 */
static jarray create_multiarray(frame* current_frame,
								   class_loader* loader,
								   const char* cname,
								   u1 dimensions,
								   __int32* counts,
								   frame* tmp_stack) {
	const java_utf8* class_name = intern_utf8(cname);
	ClassFile* cfile = find_ClassFile(current_frame, loader, class_name);
	if (cfile == NULL) {
		return NULL;
	}
	
	int count = *counts;
	jarray obj;
	if ((obj = alloc_array(cfile, count, tmp_stack)) == NULL) {
		return NULL;
	}
	
	if (dimensions > 1 && count > 0) {
		// 要素のクラス名を作成する
		// [[I の場合は [I が要素のクラス名になる
		u4 length = strlen(class_name) - 1;	// 要素名は１つ減る
		char* component_class_name = (char*) malloc(length + 1);
		if (! component_class_name) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		strcpy(component_class_name, class_name + 1);

		int newdimensions = dimensions - 1;
		__int32* newcounts = counts + 1;

		// 要素を再帰的に作成していく
		for (int index = 0; index < count; ++index) {
			jobject component;
			if ((component = create_multiarray(current_frame,
												loader,
												component_class_name,
												newdimensions,
												newcounts,
												tmp_stack)) == NULL) {
				// 再帰処理でエラー発生
				free(component_class_name);
				return NULL;
			}
			store_object_array(obj, index, component);
		}
		free(component_class_name);
	}
	return obj;
}

/**
 * nativeメソッドを実行する
 */
static void invoke_native_method(frame* caller_frame, ClassFile* target_class_file, method_info* target_method_info) {
	void* func_ptr = find_native_proc_ptr(target_class_file, target_method_info);
	if (func_ptr == NULL) {
		// 対応するネイティブ関数が存在しない場合、UnsatisfiedLinkError を throw する
		int len = strlen(target_class_file->this_class_name) + 1 + strlen(target_method_info->name) + 1;
		char* buff = (char*) malloc(len);
		if (buff == NULL) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		strcpy(buff, target_class_file->this_class_name);
		strcat(buff, ".");
		strcat(buff, target_method_info->name);
		throw_exception(caller_frame, "java/lang/UnsatisfiedLinkError", buff);
		free(buff);
		return;
	}
	call_native_function(func_ptr, caller_frame, target_class_file, target_method_info);
}

/**
 * 指定されたメソッドを実行する
 *
 * @param	caller_frame	呼び出し側のフレーム。
 * @param	ref				オブジェクトの参照。
 *                          インスタンスメソッドの場合はインスタンスの参照を指定する。
 *							（スタティックメソッドの場合は無視される）
 * @param	declaring_class_file		該当メソッドを定義しているClassFile構造体
 * @param	minfo			呼び出し対象メソッド
 */
void invoke_method(frame* caller_frame,
				   jobject ref,
				   ClassFile* declaring_file,
				   method_info* minfo) {

	// AbstractMethodの場合は、例外を発生させる
	if (is_abstract(minfo->access_flags)) {
		throw_exception(caller_frame, "java/lang/AbstractMethodError", minfo->name);
		return;
	}

	bool synchronized = is_synchronized(minfo->access_flags);
	
	// synchronized メソッドの場合、オブジェクトのロックを取得する
	jobject lock = NULL;
	if (synchronized) {
		if (is_static(minfo->access_flags)) {
			// staticオブジェクトの場合、クラスオブジェクトをロックする
			jobject static_data = get_static_data(declaring_file);
			lock = find_class_object(caller_frame, static_data, NULL);
		} else {
			lock = ref;
		}
	}
	if (lock != NULL) {
		monitor_enter(lock);
	}
	
	// nativeメソッドかを判断する
	if (is_native(minfo->access_flags)) {
		// nativeメソッドを起動する
		invoke_native_method(caller_frame, declaring_file, minfo);
	} else {
		// フレームを作る
		frame new_frame;
		if (enter_method(caller_frame, &new_frame, declaring_file, minfo)) {
			// コードを実行する
			Code_attribute* code_attr = get_Code_attribute(declaring_file, minfo);

			run_code(&new_frame, code_attr);
		
			// フレームを元に戻す
			leave_method(&new_frame);
		} else {
			assert(false);
		}
	}

	// ロックを解放する
	if (lock) {
		monitor_exit(lock);
	}
}

/**
 * 例外ハンドラを検索し、該当するpc値を返す
 * 対応するハンドラが見つからない場合、NO_EXCEPTION_HANDLER を返す
 *
 */
static unsigned int find_exception_handler(frame* rt, Code_attribute* code_attr) {
	u2 exception_table_length = code_attr->exception_table_length;
	unsigned int pc = *rt->current_bytecode_pointer - code_attr->code;
	unsigned int handler_pc = NO_EXCEPTION_HANDLER;

	// Javaのメソッド呼び出し処理が入るため、一時的に例外をクリアしておく
	jthrowable current_exception = rt->current_exception;
	jthrowable global_throwable = NULL;
	if (current_exception) {
		// ガベージコレクトされないようにグローバル参照として保持しておく
		global_throwable = (jthrowable) new_global_reference(current_exception);
	}
	rt->current_exception = NULL;

	for (u2 i = 0; i < exception_table_length; ++i) {
		exception_handler_info* handler = code_attr->exception_table + i;
		if (pc >= handler->start_pc && pc < handler->end_pc) {
			if (handler->catch_type == 0) {
				// 全ての例外に一致
				DBG(_T("Exception handler found at "));
				DBG_UTF8(rt->current_class_file->this_class_name);
				DBG(_T("."));
				DBG_UTF8(rt->current_method_info->name);
				DBG(_T(" (all exceptions)\n"));
				handler_pc = handler->handler_pc;
				break;
			} else {
				// 例外を調べる
				const java_utf8* cname = get_CONSTANT_Class_info(rt->current_class_file, handler->catch_type);
				// catchハンドラのClassFileをロードする
				ClassFile* catch_cfile = find_ClassFile(rt,
														get_defining_loader(rt->current_class_file),
														cname);
				if (catch_cfile) {
					if (is_assignable_from(rt, get_ClassFile(current_exception), catch_cfile)) {
						DBG(_T("Exception handler found at "));
						DBG_UTF8(rt->current_class_file->this_class_name);
						DBG(_T("."));
						DBG_UTF8(rt->current_method_info->name);
						DBG(_T(" Exception name="));
						DBG_UTF8(cname);
						DBG(_T("\n"));
						handler_pc = handler->handler_pc;
						break;
					}
				} else {
					// クラスのロードに失敗
					DBG(_T("Failed to load "));
					DBG_UTF8(cname);
					DBG(_T("\n"));
					break;
				}
			}
		}
	}

	// 退避しておいた例外を元に戻す
	if (current_exception) {
		rt->current_exception = current_exception;
		delete_global_reference(global_throwable);
	}

	return handler_pc;
}

/**
 * 指定された例外をthrowされた状態にする。
 * 指定されたクラスが java.lang.Throwable の派生クラスであるかはチェックしない
 *
 * @param	current_frame	カレントフレーム
 * @param	cfile			クラスファイル
 * @param	message			メッセージ
 * @return	処理が成功した場合true。
 */
bool throw_exception(frame* current_frame, ClassFile* cfile, const java_utf8* message) {
	static java_utf8* constructor_descriptor = intern_utf8("(Ljava/lang/String;)V");

	// メモリを確保し、オペランドスタック上にリファレンスを積む
	jobject obj;
	if ((obj = alloc_object(cfile, current_frame)) == NULL) {
		// どうしようもないので、OutOfMemoryErrorを投げて終了
		throw_OutOfMemoryError(current_frame);
		return false;
	}
	DUP_STACK(current_frame);

	// 引数にStringをとるコンストラクタを実行する
	method_info* constructor = find_declaring_method_info(cfile, INIT_METHOD_NAME, constructor_descriptor);
	if (constructor != NULL) {
		// Stringオブジェクトを作成する（同時にスタックにpushされる）
		if (message != NULL) {
			alloc_utf8_string(current_frame, message, false);
		} else {
			PUSH_OBJECT(current_frame, NULL);
		}
	} else {
		// Stringを引数にとるコンストラクタが見つからない場合、引数なしコンストラクタを探す
		constructor = find_declaring_method_info(cfile, INIT_METHOD_NAME, VOID_NOARG_METHOD_DESCRIPTOR);
		if (constructor == NULL) {
			POP2_DISCARD(current_frame);
			return false;
		}
	}
	
	// コンストラクタ起動
	invoke_method(current_frame, obj, cfile, constructor);

	// 生成した参照をカレントフレームに入れる
	bool result = throw_exception(current_frame, (jthrowable) obj);
	POP_DISCARD(current_frame);

	return result;
}

/**
 * 指定された例外をthrowされた状態にする。
 * 指定されたクラスが java.lang.Throwable の派生クラスであるかはチェックしない
 *
 * @param	current_frame	カレントフレーム
 * @param	cfile			クラスファイル
 * @param	param			パラメータ
 * @return	処理が成功した場合true。
 */
bool throw_exception(frame* current_frame, ClassFile* cfile, jint param) {
	// メモリを確保し、オペランドスタック上にリファレンスを積む
	jobject obj;
	if ((obj = alloc_object(cfile, current_frame)) == NULL) {
		// どうしようもないので、OutOfMemoryErrorを投げて終了
		throw_OutOfMemoryError(current_frame);
		return false;
	}
	DUP_STACK(current_frame);

	// 引数にintをとるコンストラクタを実行する
	static method_info* constructor = find_declaring_method_info(cfile, INIT_METHOD_NAME, intern_utf8("(I)V"));
	if (constructor != NULL) {
		// パラメータを積む
		PUSH_INT(current_frame, param);
	} else {
		// intを引数にとるコンストラクタが見つからない場合、引数なしコンストラクタを探す
		constructor = find_declaring_method_info(cfile, INIT_METHOD_NAME, VOID_NOARG_METHOD_DESCRIPTOR);
		if (constructor == NULL) {
			// 引数なしコンストラクタすらない場合は、エラーリターン
			POP2_DISCARD(current_frame);
			return false;
		}
	}
	
	// コンストラクタ起動
	invoke_method(current_frame, obj, cfile, constructor);

	// 生成した参照をカレントフレームに入れる
	bool result = throw_exception(current_frame, (jthrowable) obj);
	POP_DISCARD(current_frame);

	return result;
}

bool throw_exception(frame* current_frame, jthrowable t) {
	current_frame->current_exception = t;
	return true;
}

/**
 * 指定された名前の例外をthrowされた状態にする。
 * 指定された名前のクラスが java.lang.Throwable の派生クラスであるかはチェックしないため、
 * 呼び出しもとの責任になる。
 *
 * @param	current_frame	カレントフレーム
 * @param	throwする例外のクラス名
 * @return	処理が成功した場合true。
 */
bool throw_exception(frame* current_frame, const char* classname) {
	bool result = throw_exception(current_frame, classname, (java_utf8*) NULL);
	return result;
}

bool throw_exception(frame* current_frame, const char* classname, const char* message) {
	// 既に例外が発生している場合に備え、例外をクリアする
	exception_clear(current_frame);

	DBG(_T("JVM throws "));
	DBG_UTF8(classname);
	DBG(_T("\n"));
	
	class_loader* loader = get_defining_loader(current_frame->current_class_file);
	ClassFile* cfile = find_ClassFile(current_frame, loader, intern_utf8(classname));
	
	if (cfile == NULL) {
		// クラスファイルをロードできない。ギブアップ
		DBG(_T("Cannot throw "));
		DBG_UTF8(classname);
		DBG(_T("\n"));
		assert(false);

		return false;
	}
	return throw_exception(current_frame, cfile, message);
}

bool throw_exception(frame* current_frame, const char* classname, jint param) {
	// 既に例外が発生している場合に備え、例外をクリアする
	exception_clear(current_frame);

	DBG(_T("JVM throws "));
	DBG_UTF8(classname);
	DBG(_T("\n"));
	
	class_loader* loader = get_defining_loader(current_frame->current_class_file);
	ClassFile* cfile = find_ClassFile(current_frame, loader, intern_utf8(classname));
	
	if (cfile == NULL) {
		// クラスファイルをロードできない。ギブアップ
		DBG(_T("Cannot throw "));
		DBG_UTF8(classname);
		DBG(_T("\n"));
		assert(false);

		return false;
	}
	return throw_exception(current_frame, cfile, param);
}

/**
 * OutOfMemoryErrorをthrowする
 */
bool throw_OutOfMemoryError(frame* current_frame) {
	DBG(_T("JVM throws OutOfMemoryError\n"));
	return throw_exception(current_frame, g_OutOfMemoryError_instance);
}

/**
 * StackOverflowErrorをthrowする
 */
bool throw_StackOverflowError(frame* current_frame) {
	DBG(_T("JVM throws StackOverflowError\n"));
	return throw_exception(current_frame, g_StackOverflowError_instance);
}

/**
 * 現在例外が発生しているかを返す
 *
 * @param	current_frame	カレント・フレーム
 * @return	例外が発生している場合はその例外。発生していない場合はNULL.
 */
jthrowable exception_occurred(frame* current_frame) {
	return current_frame->current_exception;
}

/**
 * 例外をクリアする
 *
 * @param	current_frame	カレント・フレーム
 */
void exception_clear(frame* current_frame) {
	current_frame->current_exception = NULL;
}

/**
 * s から t に代入可能かを調べる
 * （JNIのIsAssignableFrom関数と同等の意味になる）
 *
 * @param	s	対象となるクラス
 * @param	t	代入先クラス
 * @return	cfile instanceof name の場合 true
 */
bool is_assignable_from(frame* current_frame, ClassFile* s, ClassFile* t) {
	if (s == t) {
		// 同一クラス
		return true;
	}
	if (! is_interface(s->access_flags)) {
		// Sがクラス
		if (! is_array_class(s)) {
			// Sが非配列クラス
			if (! is_interface(t->access_flags)) {
				// T がクラスである場合、S は T または Tのサブクラスでなければならない
				if (s->this_class_name == t->this_class_name) {
					return true;
				}
				ClassFile* super_cfile = get_superclass_ClassFile(s);
				if (! super_cfile) {
					return false;
				}
				// 再帰的に調べる
				return is_assignable_from(current_frame, super_cfile, t);
			} else {
				// T がインタフェースの場合、SはTをimplementsしていなければならない
				u2 interfaces_count = s->interfaces_count;
				class_loader* loader = get_defining_loader(s);
				for (u2 i = 0; i < interfaces_count; ++i) {
					const java_utf8* interface_name = get_CONSTANT_Class_info(s, s->interfaces[i]);
					ClassFile* interface_class_file = get_interface_ClassFile(s, i);
					
					// インタフェース名が T と一致するか調べる
					if (interface_class_file->this_class_name == t->this_class_name) {
						return true;
					}

					// 再帰的に調べる
					if (is_assignable_from(current_frame, interface_class_file, t)) {
						return true;
					}
				}
				// スーパークラスで実装しているインタフェースを比較していく
				const java_utf8* super_class_name = s->super_class_name;
				if (super_class_name == NULL) {
					return false;
				}
				// 再帰的に調べる
				return is_assignable_from(current_frame, get_superclass_ClassFile(s), t);
			}
		} else {
			// Sが配列クラス
			if (! is_interface(t->access_flags)) {
				// Tがクラス
				if (! is_array_class(t)) {
					// Tが非配列クラス型の場合、Tは java/lang/Object でなければならない
					if (JAVA_LANG_OBJECT_CLASS_NAME == t->this_class_name) {
						return true;
					} else {
						return false;
					}
				} else {
					// Tが配列型 TC[] の場合、以下のいずれかの条件を満たさなければならない
					// ・TC と SC が同じプリミティブ型である。
					// ・TCとSCが参照型であり、SCをTCにキャスト可能である
					// （上記に加えて「SとTとが同一次元である」という前提条件も必要？）
					
					// クラス名が一致するかを調べる
					if (s->this_class_name == t->this_class_name) {
						// 両者が同一型であることが保障される
						return true;
					} else {
						if (is_primitive_array_class(s) || is_primitive_array_class(t)) {
							return false;
						}
					}

					// 配列要素を再帰的に調べていく
					// ロード時には、配列クラスの定義ローダを用いなければならない点に注意が必要
					java_utf8* tc_name = get_array_component_name(t->this_class_name);
					java_utf8* sc_name = get_array_component_name(s->this_class_name);
					ClassFile* tc = find_ClassFile(current_frame, get_defining_loader(t), tc_name);
					ClassFile* sc = find_ClassFile(current_frame, get_defining_loader(s), sc_name);
					return is_assignable_from(current_frame, sc, tc);
				}
			} else {
				// Tがインタフェース型である場合
				// 配列クラスが実装しているインタフェースがTと一致するかを調べる
				u2 interfaces_count = s->interfaces_count;
				class_loader* loader = get_defining_loader(s);
				for (u2 i = 0; i < interfaces_count; ++i) {
					const java_utf8* interface_name = get_CONSTANT_Class_info(s, s->interfaces[i]);
					// インタフェース名が T と一致するか調べる
					if (interface_name == t->this_class_name) {
						return true;
					}
				}
				return false;
			}
		}
	} else {
		// Sがインタフェースの場合
		if (! is_interface(t->access_flags)) {
			// T がクラス型の場合、Tは "java/lang/Object" でなければならない
			if (JAVA_LANG_OBJECT_CLASS_NAME == t->this_class_name) {
				return true;
			} else {
				return false;
			}
		} else {
			// Tがインタフェース型の場合、
			// TはSと同じインタフェースか
			if (s->this_class_name == t->this_class_name) {
				return true;
			}
			// TはSのスーパーインタフェースか
			u2 interfaces_count = s->interfaces_count;
			class_loader* loader = get_defining_loader(s);
			for (u2 i = 0; i < interfaces_count; ++i) {
				const java_utf8* interface_name = get_CONSTANT_Class_info(s, s->interfaces[i]);
				ClassFile* interface_class_file = get_interface_ClassFile(s, i);
				
				// インタフェース名が T と一致するか調べる
				if (interface_class_file->this_class_name == t->this_class_name) {
					return true;
				}

				// 再帰的に調べる
				if (is_assignable_from(current_frame, interface_class_file, t)) {
					return true;
				}
			}
			return false;
		}
	}
}

/**
 * 指定されたUTF-8文字列の内容を保持するStringオブジェクトを作成する。
 * 作成したStringオブジェクトの参照は、引数 frame で指定されたフレームに
 * pushされる。
 *
 * @param	frame	フレーム。作成したStringの参照がpushされる。
 * @param	utf8	UTF-8文字列
 */
jstring new_utf8_string(frame* frame, java_utf8* utf8) {
	return alloc_utf8_string(frame, utf8, false);
}

/**
 * 指定されたUTF-8文字列の内容を保持するStringオブジェクトを作成する。
 * 作成したStringオブジェクトの参照は、引数 frame で指定されたフレームに
 * pushされる。
 * この関数を使用して作成した文字列は、ガベージコレクションの対象とならない。
 *
 * @param	frame	フレーム。作成したStringの参照がpushされる。
 * @param	utf8	UTF-8文字列
 */
jstring permanent_utf8_string(frame* frame, const java_utf8* utf8) {
	return alloc_utf8_string(frame, utf8, true);
}

/**
 * 指定されたUTF-8文字列の内容を保持するStringオブジェクトを作成する。
 * 作成したStringオブジェクトの参照は、引数 frame で指定されたフレームに
 * pushされる。
 * 引数permanentがtrueの場合、ガベージコレクションの対象とならない。
 *
 * @param	frame	フレーム。作成したStringの参照がpushされる。
 * @param	utf8	UTF-8文字列
 * @param	permanent	
 */
static jstring alloc_utf8_string(frame* frame, const char* utf8, bool permanent) {
	static ClassFile* string_class_file;
	static ClassFile* char_array_class_file;
	static method_info* string_constructor_minfo;

	// StringのClassFileをロードする
	if (! string_class_file) {
		string_class_file = find_ClassFile(frame, STRING_CLASS_NAME);
		if (! string_class_file) {
			return NULL;
		}
	}

	// メモリを割り当てる
	jstring string_reference = (jstring) alloc_object(string_class_file, frame);
	if (string_reference == NULL) {
		return NULL;
	}
	if (permanent) {
		// ガベージコレクション対象外とする
		new_global_reference(string_reference);
	}

	DUP_STACK(frame);	// Stringの参照を複製する

	// char配列を作成する
	// char[]を示すクラスファイルを取得する
	if (! char_array_class_file) {
		char_array_class_file = find_ClassFile(frame, CHAR_ARRAY_CLASS_NAME);
		if (! char_array_class_file) {
			return NULL;
		}
	}

	jobject char_array_reference = alloc_char_array(char_array_class_file, utf8, frame);
	if (char_array_reference == NULL) {
		return NULL;
	}
	
	// コンストラクタを呼び出す(String(char[]))
	if (! string_constructor_minfo) {
		static java_utf8* constructor_descriptor = intern_utf8("([C)V");
		string_constructor_minfo
				= find_declaring_method_info(string_class_file,
								  INIT_METHOD_NAME,
								  constructor_descriptor);
	}
	invoke_method(frame,
				  string_reference,
				  string_class_file,
				  string_constructor_minfo);
	
	// dup()しているので、スタックトップに String の参照が乗っている

	return string_reference;	
}

/**
 * 指定されたクラス from から、クラス target の access_flags を持つ
 * クラス／メソッド／フィールドにアクセス可能かを判定する
 */
static bool is_accessible(frame* current_frame, ClassFile* from, ClassFile* target, u2 access_flags) {
	if (from == target) {
		// 自分自身のフィールド／メソッドには必ずアクセス可能
		return true;
	}

	if (is_public(access_flags)) {
		// publicの場合は常にアクセス可能
		return true;
	}
	if (is_protected(access_flags)) {
		// protectedの場合には以下のいずれかの条件を満たしていなければならない
		// ・同一パッケージ
		// ・from が targetの派生クラスである必要がある
		class_loader* loader = get_defining_loader(from);
		if (is_same_package(loader, from, target)) {
			return true;
		}
	
		while ((from = get_superclass_ClassFile(from)) != NULL) {
			if (from == target) {
				return true;
			}
		} 
		return false;
	}
	if (is_private(access_flags)) {
		// privateの場合には自分自身でなければならない
		return from == target;
	}
	
	// パッケージプライベートの場合
	class_loader* loader = get_defining_loader(from);
	return is_same_package(loader, from, target);
}

/**
 * jsr命令を実行する（ユーティリティ）
 */
static inline void execute_jsr(frame* current_frame, __int32 branch) {
	// jsr命令の長さは3なので、pcに3を加算した値をPUSHする
	// returnAddress型であることを示すため最上位ビットを立てておく
	// （ret命令実行時にこのビットはクリアされる）
	PUSH_INT(current_frame,
	     	(unsigned int) (*current_frame->current_bytecode_pointer + 3) | 0x80000000);
	*current_frame->current_bytecode_pointer += branch;
}

// これ以降は EXECUTOR関数

/**
 * tableswitch命令を実行する
 */
static void execute_tableswitch(frame* current_frame, u1* bytecode) {
	// プリロードする
	// _PreLoad((unsigned long*) bytecode);

	// ４バイト境界からデータを読み込む
	unsigned int padding = (unsigned int) (*current_frame->current_bytecode_pointer + 1) % 4;
	if (padding != 0) {
		padding = 4 - padding;
	}
	const unsigned int startpos = padding + 1;
	const __int32* data = (__int32*) &bytecode[startpos];

	const __int32 defaultoffset = READ_BIG_ENDIAN(data[0]);
	const __int32 lowvalue      = READ_BIG_ENDIAN(data[1]);
	const __int32 highvalue     = READ_BIG_ENDIAN(data[2]);
	const __int32* jump_table   = &data[3];

	__int32 index;
	POP_INT(current_frame, index);
	if (index < lowvalue || index > highvalue) {
		*current_frame->current_bytecode_pointer += defaultoffset;
	} else {
		__int32 offset = jump_table[index - lowvalue];
		offset = READ_BIG_ENDIAN(offset);
		*current_frame->current_bytecode_pointer += offset;
	}
}

/**
 * lookupswitch命令を実行する
 */
static void execute_lookupswitch(frame* current_frame, u1* bytecode) {
	// プリロードする
	// _PreLoad((unsigned long*) bytecode);

	// ４バイト境界からデータを読み込む
	unsigned int padding = (unsigned int) (*current_frame->current_bytecode_pointer + 1) % 4;
	if (padding != 0) {
		padding = 4 - padding;
	}
	const unsigned int startpos = padding + 1;
	const __int32* data = (__int32*) &bytecode[startpos];
	
	const __int32 defaultoffset = READ_BIG_ENDIAN(data[0]);
	const __int32 npairs = READ_BIG_ENDIAN(data[1]);
	const __int32* match_offset = (__int32*) &data[2];
	
	__int32 key;
	POP_INT(current_frame, key);

	// キーに一致する値を検索する
	// ToDo:二分検索処理を行う
	bool found = false;
	for (int i = 0; i < npairs; ++i) {
		__int32 match = READ_BIG_ENDIAN(match_offset[i * 2]);
		if (key == match) {
			__int32 offset = READ_BIG_ENDIAN(match_offset[i * 2 + 1]);
			*current_frame->current_bytecode_pointer += offset;
			found = true;
			break;
		}
	}
	// match-offsetに一致しなかった場合
	if (! found) {
		*current_frame->current_bytecode_pointer += defaultoffset;
	}
}

/**
 * getstatic命令を実行する
 */
static void execute_getstatic(frame* current_frame, u1* bytecode) {
	u1 index1 = GET_PARAMETER(bytecode, 1);
	u1 index2 = GET_PARAMETER(bytecode, 2);
	u2 index = MAKE_U2(index1, index2);

	// インデックスで指定された CONSTANT_Fieldref_info 構造体へのポインタを取得する
	CONSTANT_Fieldref_info* fieldref = get_CONSTANT_Fieldref_info(current_frame->current_class_file, index);
	
	// Fieldrefで指定されたクラスを取得する 
	ClassFile* target_class_file = fieldref->class_file;
	if (target_class_file == NULL) {
		class_loader* loader = get_defining_loader(current_frame->current_class_file);
		target_class_file = find_ClassFile(current_frame, loader, fieldref->class_name);
		if (! target_class_file) {
			// 初期化に失敗
			return;
		}
		fieldref->class_file = target_class_file;
	}

	// フィールドIDを取得する
	jfieldID fid = get_static_field_id(target_class_file, fieldref->field_name, fieldref->field_descriptor);
	if (fid == NULL) {
		// ToDo: 例外を設定する
		return;
	}

	// ロード制約を追加する
	field_info* finfo = get_static_field_info(target_class_file, fid);
	if (! add_loading_constraints_of_field(current_frame,
										   target_class_file,
										   finfo->descriptor)) {
		return;
	}
	// アクセス権限をチェックする
	if (! is_accessible(current_frame,
					    current_frame->current_class_file,
						target_class_file,
						finfo->access_flags)) {
		throw_exception(current_frame, "java/lang/IllegalAccessError", finfo->name);
		return;
	}

	// スタティックフィールドを取得する
	jobject static_instance = get_static_data(target_class_file);
	get_static_field(current_frame, static_instance, fid);

}

/**
 * putstatic命令を実行する
 */
static void execute_putstatic(frame* current_frame, u1* bytecode) {
	u1 index1 = GET_PARAMETER(bytecode, 1);
	u1 index2 = GET_PARAMETER(bytecode, 2);
	u2 index = MAKE_U2(index1, index2);

	// インデックスで指定された CONSTANT_Fieldref_info 構造体へのポインタを取得する
	CONSTANT_Fieldref_info* fieldref = get_CONSTANT_Fieldref_info(current_frame->current_class_file, index);
	
	// Fieldrefで指定されたクラスを取得する
	ClassFile* target_class_file = fieldref->class_file;
	if (target_class_file == NULL) {
		class_loader* loader = get_defining_loader(current_frame->current_class_file);
		target_class_file = find_ClassFile(current_frame, loader, fieldref->class_name);
		if (! target_class_file) {
			// 初期化に失敗
			return;
		}
		fieldref->class_file = target_class_file;
	}
	
	// フィールドIDを取得する
	jfieldID fid = get_static_field_id(target_class_file, fieldref->field_name, fieldref->field_descriptor);
	if (fid == NULL) {
		// 例外を設定する
		return;
	}

	// ロード制約を追加する
	field_info* finfo = get_static_field_info(target_class_file, fid);
	if (! add_loading_constraints_of_field(current_frame,
										   target_class_file,
										   finfo->descriptor)) {
		return;
	}

	// アクセス権限をチェックする
	if (! is_accessible(current_frame,
						current_frame->current_class_file,
						target_class_file,
						finfo->access_flags)) {
		throw_exception(current_frame, "java/lang/IllegalAccessError", finfo->name);
		return;
	}

	jobject static_instance = get_static_data(target_class_file);
	put_static_field(current_frame, static_instance, fid);
}

/**
 * wide命令を実行する
 */
static void execute_wide(frame* current_frame, u1* bytecode) {
	u1 opcode = GET_PARAMETER(bytecode, 1);
	u1 indexbyte1 = GET_PARAMETER(bytecode, 2);
	u1 indexbyte2 = GET_PARAMETER(bytecode, 3);
	u2 index = MAKE_U2(indexbyte1, indexbyte2);

	switch (opcode) {
	case 21:	// iload
		LOAD(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 22:	// lload
		LOAD2(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 23:	// fload
		LOAD(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 24:	// dload
		LOAD2(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 25:	// aload
		LOAD(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 54:	// istore
		STORE(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 55:	// lstore
		STORE2(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 56:	// fstore
		STORE(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 57:	// dstore
		STORE2(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 58:	// astore
		STORE(current_frame, index);
		*current_frame->current_bytecode_pointer += 4;
		break;

	case 132:	// iinc
		{
			u1 constbyte1 = GET_PARAMETER(bytecode, 4);
			u1 constbyte2 = GET_PARAMETER(bytecode, 5);
			__int16 count = MAKE_INT16(constbyte1, constbyte2);
			IINC_LOCAL(current_frame, index, count);
			*current_frame->current_bytecode_pointer += 6;
		}
		break;

	}
}

/**
 * multianewarray命令を処理する
 */
static void execute_multianewarray(frame* current_frame, u1* bytecode) {
	u1 indexbyte1 = GET_PARAMETER(bytecode, 1);
	u1 indexbyte2 = GET_PARAMETER(bytecode, 2);
	u1 dimensions = GET_PARAMETER(bytecode, 3);
	u2 index = MAKE_U2(indexbyte1, indexbyte2);
	
	// 各次元の要素数をオペランドスタックから取得する
	jint* counts = (__int32*) alloca(sizeof(int) * dimensions);
	for (int i = dimensions - 1; i >= 0; --i) {
		POP_INT(current_frame, counts[i]);
	}
	// 全要素の合計数を計算する
	jint tmp_stack_size = counts[0];
	if (tmp_stack_size == 0) {
		// 要素数が０の場合に備える
		tmp_stack_size = 1;
	} else if (tmp_stack_size < 0) {
		// NegativeArraySizeException を throw する
		throw_exception(current_frame, "java/lang/NegativeArraySizeException", tmp_stack_size);
		return;
	}

	for (int i = 1; i < dimensions; ++i) {
		unsigned int count = counts[i];
		if (count < 0) {
			// NegativeArraySizeException を throw する
			throw_exception(current_frame, "java/lang/NegativeArraySizeException", tmp_stack_size);
			return;
		}
		tmp_stack_size *= count;	
	}
	
	// 次元数分加算する（各要素を入れる"親"の配列分）
	tmp_stack_size += dimensions;

	// 生成するクラスの情報を取得する
	ClassFile* cfile = current_frame->current_class_file;
	const java_utf8* cname = get_CONSTANT_Class_info(cfile, index);
	class_loader* loader = get_defining_loader(current_frame->current_class_file);
	
	// テンポラリのスタックを作成する（作成中の配列がガベージコレクトされないようにするため）
	frame* tmp_stack = alloc_root_frame(current_frame->current_class_file, tmp_stack_size + 1);
	if (tmp_stack == NULL) {
		throw_OutOfMemoryError(current_frame);
		return;
	}

	jobject arrayref = create_multiarray(current_frame, loader, cname, dimensions, counts, tmp_stack);

	PUSH_OBJECT(current_frame, arrayref);

	// テンポラリスタックを削除する
	free_root_frame(tmp_stack);
}
