/* verifier.cpp - Class file verifier 
   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 "java_utf8.h"
#include "console.h"
#include "ClassFile.h"
#include "class_loader.h"
#include "frame.h"
#include "instruction.h"
#include "common_funcs.h"
#include "bytecode.h"

/**
 * ベリファイヤ
 */
struct verifier {
	/**
	 * カレントフレーム
	 */
	frame* current_frame;

	/**
	 * ベリファイ中のクラスファイル
	 */
	ClassFile* cfile;

	/**
	 * ベリファイ中のフィールド
	 */
	field_info* finfo;

	/**
	 * ベリファイ中のメソッド
	 */
	method_info* minfo;
};

/**
 * ２つ以上のクラスがマージした状態
 */
struct merged_class_names {
	/**
	 * マージされたクラス数。常に２よりも大きな数になる。
	 */
	unsigned int count;

	/**
	 * クラス名のポインタの配列。count数分の要素を持つ。
	 */
	const java_utf8** class_names;

	/**
	 * リンクリストとして管理する（最後に開放するため）
	 */
	merged_class_names* next;
};

/**
 * データモデルの状態定数
 */
enum types {
	// 無効
	ITEM_Invalid,
	
	// 整数（カテゴリ1）
	ITEM_Integer,

	// float
	ITEM_Float,

	// double
	ITEM_Double,

	// long
	ITEM_Long,

	// null
	ITEM_Null,

	// 未初期化this(<init>メソッド内)
	ITEM_UninitializedThis,

	// Object
	ITEM_Object,

	// 未初期化オブジェクト
	ITEM_Uninitialized,

	// リターンアドレス
	ITEM_ReturnAddress,

	// マージされたクラス
	ITEM_MergedObjects,
};

// 型判定用マクロ

#define IS_OBJECT(item) \
((item)->type == ITEM_Null \
  || (item)->type == ITEM_Object \
  || (item)->type == ITEM_MergedObjects \
  || (item)->type == ITEM_Uninitialized \
  || (item)->type == ITEM_UninitializedThis)

#define IS_RETURN_ADDRESS(item) \
 ((item)->type == ITEM_ReturnAddress)

#define IS_BOOLEAN_ARRAY(item) \
	((item)->type == ITEM_Null \
	 || ((item)->type == ITEM_Object && (item)->class_name == BOOLEAN_ARRAY_CLASS_NAME))

#define IS_BYTE_ARRAY(item) \
	((item)->type == ITEM_Null \
	 || ((item)->type == ITEM_Object && (item)->class_name == BYTE_ARRAY_CLASS_NAME))

#define IS_CHAR_ARRAY(item) \
	((item)->type == ITEM_Null \
	 || ((item)->type == ITEM_Object && (item)->class_name == CHAR_ARRAY_CLASS_NAME))

#define IS_SHORT_ARRAY(item) \
	((item)->type == ITEM_Null \
	 || ((item)->type == ITEM_Object && (item)->class_name == SHORT_ARRAY_CLASS_NAME))

#define IS_INT_ARRAY(item) \
	((item)->type == ITEM_Null \
	 || ((item)->type == ITEM_Object && (item)->class_name == INT_ARRAY_CLASS_NAME))

#define IS_LONG_ARRAY(item) \
	((item)->type == ITEM_Null \
	 || ((item)->type == ITEM_Object && (item)->class_name == LONG_ARRAY_CLASS_NAME))

#define IS_FLOAT_ARRAY(item) \
	((item)->type == ITEM_Null \
	 || ((item)->type == ITEM_Object && (item)->class_name == FLOAT_ARRAY_CLASS_NAME))

#define IS_DOUBLE_ARRAY(item) \
	((item)->type == ITEM_Null \
	 || ((item)->type == ITEM_Object && (item)->class_name == DOUBLE_ARRAY_CLASS_NAME))

#define IS_CATEGORY2_TYPE(item) \
	((item)->type == ITEM_Double || (item)->type == ITEM_Long)

/**
 * ローカル変数およびスタック上のデータ
 */
struct data_item {
	/**
	 * このデータアイテムのタイプ
	 */
	int type;

	union {
		/**
		 * クラス名
		 * ITEM_Object の場合にのみ有効
		 */
		const java_utf8* class_name;

		/**
		 * マージされたクラス
		 * ITEM_MergedObjects の場合にのみ有効
		 */
		const merged_class_names* merged_classes;

		/**
		 * new命令のpc.
		 * ITEM_Uninitialized の場合にのみ有効
		 */
		u2 pc;
	};
};

/**
 * ベリファイ対象ユニット
 */
struct verified_unit {
	struct {
		/**
		 * 変更フラグ
		 */
		int changed : 1;

		/**
		 * retリスト変更
		 */
		int ret_list_changed : 1;

		/**
		 * 例外ハンドラの入り口
		 */
		int exception_handler : 1;

		/**
		 * 他のベリファイユニット内容をコピー済み
		 */
		int copied : 1;
	};

	/**
	 * 開始命令位置
	 */
	u2 start_pc;

	/**
	 * 終了命令位置
	 */
	u2 end_pc;

	/**
	 * オペランド・スタック位置
	 */
	u2 operand_stack_position;

	/**
	 * ret命令の戻り先数
	 */
	u2 ret_target_count;

	union {
		/**
		 * ret命令の戻り先
		 * ret_target_count = 1 の場合に有効
		 */
		u2 ret_target_pc;

		/**
		 * ret命令の戻り先
		 * ret_target_count > 1 の場合に有効
		 */
		u2* ret_target_pc_list;
	};

	/**
	 * ローカル変数
	 */
	data_item* local_variables;

	/**
	 * オペランド・スタック
	 */
	data_item* operand_stack;
};

/**
 * データフローアナライザが使用する構造体
 */
struct data_flow_analyzer : verifier {
	/**
	 * コードフラグ
	 * minfo->code_attribute->code_length分の要素を持ち
	 * フラグを保持する。
	 */
	const u1* code_flags;

	/**
	 * verified_unitの配列。
	 * verified_unit_count数分存在する。
	 * 各 verified_unit 構造体は、start_pc の昇順に配置される。
	 */
	verified_unit* verified_units;

	/**
	 * ベリファイユニットの数
	 */
	u2 verified_unit_count;

	/**
	 * 「マージされたクラス型」のルート
	 * リンクリストとして管理する
	 */
	merged_class_names* merged_classes_root;

	/**
	 * 作業用のユニット
	 */
	verified_unit work_unit;
};

/**
 * data_flow_analyzer.code_flagsに設定される値
 */

// 命令の先頭
#define CF_INSTRUCTION						(1)
// ベリファイユニットの先頭
#define CF_START_OF_VERIFIED_UNIT			(1 << 1)
// ベリファイユニットの末尾
#define CF_END_OF_VERIFIED_UNIT				(1 << 2)
// 例外ハンドラの先頭
#define CF_START_OF_EXCEPTION_HANDLER		(1 << 3)
// 例外ハンドラによって保護が開始される位置
#define CF_START_OF_PROTECTED_CODE			(1 << 4)

/**
 * 指定された値をを4バイト境界に切り上げる
 */
#define ALIGN_UP4(pc) ((pc) + (4 - ((pc) % 4)))

/**
 * ２つのバイトを結合し符号なし16ビット値に変換する
 */
#define MAKE_U2(index1, index2) \
	(u2)(((u2) index1 << 8) | (index2 & 0x00ff))

/**
 * ２つのバイトを結合し符号付16ビット値に変換する
 */
#define MAKE_INT16(index1, index2) \
	((__int16) index1 << 8) | (index2 & 0x00ff)

/**
 * ２つのバイトを結合し、符号付32ビット値に変換する
 */
#define MAKE_INT32(b1, b2, b3, b4) \
	(((__int32) b1 << 24) | ((__int32) b2 << 16) | ((__int32) b3 << 8) | ((__int32) b4))

/**
 * VerifyErrorをthrowする
 */
#define THROW_VERIFY_ERROR(current_frame, msg) \
{ \
	assert(false); \
    throw_exception((current_frame), "java/lang/VerifyError", msg); \
}

/**
 * VerifyErrorをthrowする
 */
static void
throw_verify_error(verifier* v, const char* msg) {
	char detail[256];
	if (v->finfo) {
		_snprintf(detail,
				  sizeof(detail) - 1,
				  "%s.%s:(%s)",
				  v->cfile->this_class_name,
				  v->finfo->name,
				  msg);
	} else if (v->minfo) {
		_snprintf(detail,
				  sizeof(detail) - 1,
				  "%s.%s:(%s)",
				  v->cfile->this_class_name,
				  v->minfo->name,
				  msg);
	} else {
		_snprintf(detail,
				  sizeof(detail) - 1,
				  "%s:(%s)",
				  v->cfile->this_class_name,
				  msg);
	}
	assert(false);
	throw_exception(v->current_frame, "java/lang/VerifyError", detail);
}

/**
 * VerifyErrorをthrowする
 */
static void
throw_verify_error(ClassFile* cfile, field_info* finfo, const char* msg) {
	verifier v;
	v.cfile = cfile;
	v.finfo = finfo;
	throw_verify_error(&v, msg);
}

/**
 * VerifyErrorをthrowする
 */
static void
throw_verify_error(ClassFile* cfile, method_info* minfo, const char* msg) {
	verifier v;
	v.cfile = cfile;
	v.minfo = minfo;
	throw_verify_error(&v, msg);
}

/**
 * ベリファイヤがパニック状態になったことを通知する
 */
static void verifier_panic(verifier* v, const char* detail) {
	char msg[256];
	_snprintf(msg,
			  sizeof(msg) - 1,
			  "Verifier panic : %s",
			  detail);
	msg[sizeof(msg) - 1] = '\0';
	throw_verify_error(v, msg);
}

/**
 * u2(符号なし16ビット）を読み込む
 */
static inline u2 read_u2(const u1* code) {
	return ((u2) code[0] << 8) | (code[1] & 0xff);
}

/**
 * コードのオーバーランをチェックする
 */
static inline bool check_code_overrun(data_flow_analyzer* analyzer,
									  u4 code_length,
									  u4 pc) {
	if (code_length <= pc) {
		throw_verify_error(analyzer, "Code overrun");
		return false;
	}
	return true;
}

/**
 * ローカル変数のインデックスをチェックする
 */
static inline bool check_local_index(data_flow_analyzer* analyzer,
									 u2 max_locals,
									 u2 index) {
	if (max_locals <= index) {
		throw_verify_error(analyzer, "Invalid local index");
		return false;
	}
	return true;
}


/**
 * スタックがオーバーフローしないか調べる
 */
static inline bool check_stack_overflow(data_flow_analyzer* analyzer,
										verified_unit* unit,
										u2 max_stack,
										u4 data_size) {
	if ((u4) max_stack - (u4) unit->operand_stack_position < data_size) {
		throw_verify_error(analyzer, "Stack overflow");
		return false;
	}
	return true;
}

/**
 * スタックがアンダーフローしないかを調べる
 */
static inline bool check_stack_underflow(data_flow_analyzer* analyzer,
										 verified_unit* unit,
										 u2 data_size) {
	if ((u4) unit->operand_stack_position < (u4) data_size) {
		throw_verify_error(analyzer, "Stack underflow");
		return false;
	}
	return true;
}

/**
 * オブジェクト配列かを調べる
 */
static inline bool
is_object_array(data_flow_analyzer* analyzer, data_item* item) {
	bool error = false;
	switch (item->type) {
	case ITEM_Null:
		break;

	case ITEM_Object:
		{
			const java_utf8* class_name = item->class_name;
			int len = strlen(class_name);
			if (len < 3) {
				error = true;
				break;
			} else if (class_name[1] != 'L' && class_name[1] != '[') {
				error = true;
				break;
			}
		}
		break;

	case ITEM_MergedObjects:
		{
			const merged_class_names* merged = item->merged_classes;
			for (unsigned int i = 0; i < merged->count; ++i) {
				const java_utf8* class_name = merged->class_names[i];
				int len = strlen(class_name);
				if (len < 3) {
					error = true;
					break;
				} else if (class_name[1] != 'L' && class_name[1] != '[') {
					error = true;
					break;
				}
			}
		}

	default:
		error = true;
		break;
	}

	if (error) {
		throw_verify_error(analyzer, "Must be Object array class");
		return false;
	}

	return true;
}

/**
 * static関数
 */
static bool check_visibility_flags(verifier* v, u2 access_flags);
static bool check_access_flags(verifier* v, ClassFile* cfile);
static bool check_fields(verifier* v, ClassFile* cfile);
static bool check_methods(verifier* v, ClassFile* cfile);
static bool check_class_inheritance(verifier* v, ClassFile* cfile, ClassFile* super_cfile, ClassFile* interfaces_cfiles);
static bool check_fieldref_and_methodref(frame* current_frame, ClassFile* cfile);
static bool check_fieldref(frame* current_frame, CONSTANT_Fieldref_info* fieldref);
static bool check_methodref(frame* current_frame, CONSTANT_Methodref_info* methodref);

/**
 * 各命令を区切る
 */
static bool split_instructions(data_flow_analyzer* analyzer,
							   data_item* return_type);

/**
 * 変更されたベリファイユニットへのポインタを返す
 * 存在しない場合はNULLを返す。
 */
static verified_unit* get_changed_unit(data_flow_analyzer* analyzer);

/**
 * データフローアナライザ
 */
static bool analyze(data_flow_analyzer* analyzer);

/**
 * 指定されたベリファイユニットのアナライズを行う。
 */
static bool analyze(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * verified_unitにメモリを割り当てる
 */
static bool alloc_verified_unit(data_flow_analyzer* analyzer,
								verified_unit* unit,
								u2 locals,
								u2 stack);

/**
 * verified_unitに割り当てたメモリをすべて開放する
 */
static void free_verified_units(data_flow_analyzer* analyzer);


/**
 * jsr命令に関する処理を行う
 */
static bool process_jsr(frame* current_frame,
						data_flow_analyzer* analyzer);

/**
 * 新しいデータフローアナライザを作成する
 */
static data_flow_analyzer* create_data_flow_analyzer(frame* current_frame,
													 ClassFile* cfile,
													 method_info* minfo);

/**
 * データフローアナライザに割り当てたメモリを解放する
 */
static bool delete_data_flow_analyzer(data_flow_analyzer* analyzer);

/**
 * ローカル変数の指定されたインデックス位置に、データを格納する。
 */
static bool set_local(data_flow_analyzer* analyzer,
					  verified_unit* unit,
					  u2 index,
					  data_item* item);

/**
 * 指定された配列クラス名から、配列要素のクラス名を取り出す
 */
static const java_utf8* get_component_of_object_array(const java_utf8* object_array_name);

/**
 * ローカル変数から、指定された型を取得する
 */
static bool get_int(data_flow_analyzer* analyzer, verified_unit* unit, u2 index);
static bool get_long(data_flow_analyzer* analyzer, verified_unit* unit, u2 index);
static bool get_float(data_flow_analyzer* analyzer, verified_unit* unit, u2 index);
static bool get_double(data_flow_analyzer* analyzer, verified_unit* unit, u2 index);
static bool get_object(data_flow_analyzer* analyzer, verified_unit* unit, u2 index);
static bool get_object(data_flow_analyzer* analyzer, verified_unit* unit, u2 index, data_item* item);

/**
 * スタックに、指定された型をpushする。
 */
static bool push(data_flow_analyzer* analyzer, verified_unit* unit, data_item* item);

/**
 * 整数（カテゴリ１）をPUSHする
 */
static bool push_int(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * floatをPUSHする
 */
static bool push_float(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * longをPUSHする
 */
static bool push_long(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * doubleをPUSHする
 */
static bool push_double(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * Objectをpushする
 */
static bool push_object(data_flow_analyzer* analyzer, verified_unit* unit, const java_utf8* class_name);

/**
 * スタックトップの要素をpopする。
 */
static bool pop(data_flow_analyzer* analyzer,
				verified_unit* unit,
				data_item* item);

/**
 * intをpopする
 */
static bool pop_int(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * longをpopする
 */
static bool pop_long(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * floatをpopする
 */
static bool pop_float(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * doubleをpopする
 */
static bool pop_double(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * Objectをpopする
 */
static bool pop_object(data_flow_analyzer* analyzer, verified_unit* unit, data_item* item);
static bool pop_object(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * スタックからPOPする
 */
static bool pop(data_flow_analyzer* analyzer, verified_unit* unit);
static bool pop2(data_flow_analyzer* analyzer, verified_unit* unit);

/**
 * 指定されたフィールドの型を取得して返す。
 *
 * @param	current_frame
 * @param	field_descriptor	フィールドのディスクリプタ
 * @param	ptype				item_typeへのポインタ。
 */
static bool get_field_type(data_flow_analyzer* analyzer,
						   const java_utf8* field_descriptor,
						   data_item* ptype);

/**
 * メソッドのディスクリプタをパースし、data_itemに入れる
 */
static int parse_method_descriptor(data_flow_analyzer* analyzer,
								   const java_utf8* method_descriptor,
                                   data_item* params,
								   data_item* rettype);

/**
 * ベリファイヤがパニック状態になったことを通知する
 */
static void verifier_panic(verifier* v, const char* msg);

/**
 * 指定された位置をstart_pcとするベリファイユニットを返す。
 * 見つからない場合はNULLを返す。
 */
static verified_unit* get_verified_unit(data_flow_analyzer* analyzer,
										u2 start_pc);

/**
 * 指定されたモデルを、指定されたpc位置にあるモデルにマージする。
 */
static bool merge(data_flow_analyzer* analyzer,
				  verified_unit* unit,
				  u2 current_pc,
				  u2 target_pc);

/**
 * 指定されたデータアイテムをマージする
 */
static bool
merge_items(data_flow_analyzer* analyzer, 
		  data_item* from_items,
		  data_item* to_items,
		  int count,
		  bool error_on_invalidate,
		  bool* changed_flag);

/**
 * 指定されたオブジェクトに対するマージ処理を行う
 */
static bool
merge_object(data_flow_analyzer* analyzer,
			 data_item* from_object,
			 data_item* to_object);

/**
 * 指定されたdata_itemの配列内容が、もう一方の配列内容に代入可能か
 * 調べる。
 */
static bool
check_assignable(data_flow_analyzer* analyzer,
				 data_item* from_items,
				 data_item* to_items,
				 unsigned int count);

/**
 * merged_class_names構造体を作成する
 */
static merged_class_names*
create_merged_class_names(data_flow_analyzer* analyzer,
						 u2 class_count);

/**
 * merged_class_names構造体を開放する
 */
static void
delete_merged_class_names(data_flow_analyzer* analyzer);

/**
 * 指定された２つのクラス名を保持する構造体を返す。
 */
static const merged_class_names*
create_merged_class_names(data_flow_analyzer* analyzer,
			  const java_utf8* class_name1,
			  const java_utf8* class_name2);
/**
 * 指定されたクラス名を保持する構造体を返す。
 */
static const merged_class_names*
create_merged_class_names(data_flow_analyzer* analyzer,
			  const merged_class_names* merged,
			  const java_utf8* class_name);

/**
 * 指定されたクラス名を保持する構造体を返す。
 */
static const merged_class_names*
create_merged_class_names(data_flow_analyzer* analyzer,
			  const merged_class_names* merged1,
			  const merged_class_names* merged2);

/**
 * 命令の長さ（バイト数）
 */
static u2 g_instruction_length[256] = {
	1,	//00 (0x00) nop
	1,	//01 (0x01) aconst_null
	1,	//02 (0x02) iconst_m1
	1,	//03 (0x03) iconst_0
	1,	//04 (0x04) iconst_1
	1,	//05 (0x05) iconst_2
	1,	//06 (0x06) iconst_3
	1,	//07 (0x07) iconst_4
	1,	//08 (0x08) iconst_5
	1,	//09 (0x09) lconst_0
	1,	//10 (0x0a) lconst_1
	1,	//11 (0x0b) fconst_0
	1,	//12 (0x0c) fconst_1
	1,	//13 (0x0d) fconst_2
	1,	//14 (0x0e) dconst_0
	1,	//15 (0x0f) dconst_1
	2,	//16 (0x10) bipush
	3,	//17 (0x11) sipush
	2,	//18 (0x12) ldc
	3,	//19 (0x13) ldc_w
	3,	//20 (0x14) ldc2_w
	2,	//21 (0x15) iload
	2,	//22 (0x16) lload
	2,	//23 (0x17) fload
	2,	//24 (0x18) dload
	2,	//25 (0x19) aload
	1,	//26 (0x1a) iload_0
	1,	//27 (0x1b) iload_1
	1,	//28 (0x1c) iload_2
	1,	//29 (0x1d) iload_3
	1,	//30 (0x1e) lload_0
	1,	//31 (0x1f) lload_1
	1,	//32 (0x20) lload_2
	1,	//33 (0x21) lload_3
	1,	//34 (0x22) fload_0
	1,	//35 (0x23) fload_1
	1,	//36 (0x24) fload_2
	1,	//37 (0x25) fload_3
	1,	//38 (0x26) dload_0
	1,	//39 (0x27) dload_1
	1,	//40 (0x28) dload_2
	1,	//41 (0x29) dload_3
	1,	//42 (0x2a) aload_0
	1,	//43 (0x2b) aload_1
	1,	//44 (0x2c) aload_2
	1,	//45 (0x2d) aload_3
	1,	//46 (0x2e) iaload
	1,	//47 (0x2f) laload
	1,	//48 (0x30) faload
	1,	//49 (0x31) daload
	1,	//50 (0x32) aaload
	1,	//51 (0x33) baload
	1,	//52 (0x34) caload
	1,	//53 (0x35) saload
	2,	//54 (0x36) istore
	2,	//55 (0x37) lstore
	2,	//56 (0x38) fstore
	2,	//57 (0x39) dstore
	2,	//58 (0x3a) astore
	1,	//59 (0x3b) istore_0
	1,	//60 (0x3c) istore_1
	1,	//61 (0x3d) istore_2
	1,	//62 (0x3e) istore_3
	1,	//63 (0x3f) lstore_0
	1,	//64 (0x40) lstore_1
	1,	//65 (0x41) lstore_2
	1,	//66 (0x42) lstore_3
	1,	//67 (0x43) fstore_0
	1,	//68 (0x44) fstore_1
	1,	//69 (0x45) fstore_2
	1,	//70 (0x46) fstore_3
	1,	//71 (0x47) dstore_0
	1,	//72 (0x48) dstore_1
	1,	//73 (0x49) dstore_2
	1,	//74 (0x4a) dstore_3
	1,	//75 (0x4b) astore_0
	1,	//76 (0x4c) astore_1
	1,	//77 (0x4d) astore_2
	1,	//78 (0x4e) astore_3
	1,	//79 (0x4f) iastore
	1,	//80 (0x50) lastore
	1,	//81 (0x51) fastore
	1,	//82 (0x52) dastore
	1,	//83 (0x53) aastore
	1,	//84 (0x54) bastore
	1,	//85 (0x55) castore
	1,	//86 (0x56) sastore
	1,	//87 (0x57) pop
	1,	//88 (0x58) pop2
	1,	//089 (0x59) dup
	1,	//090 (0x5a) dup_x1
	1,	//091 (0x5b) dup_x2
	1,	//092 (0x5c) dup2
	1,	//093 (0x5d) dup2_x1
	1,	//094 (0x5e) dup2_x2
	1,	//095 (0x5f) swap
	1,	//096 (0x60) iadd
	1,	//097 (0x61) ladd
	1,	//098 (0x62) fadd
	1,	//099 (0x63) dadd
	1,	//100 (0x64) isub
	1,	//101 (0x65) lsub
	1,	//102 (0x66) fsub
	1,	//103 (0x67) dsub
	1,	//104 (0x68) imul
	1,	//105 (0x69) lmul
	1,	//106 (0x6a) fmul
	1,	//107 (0x6b) dmul
	1,	//108 (0x6c) idiv
	1,	//109 (0x6d) ldiv
	1,	//110 (0x6e) fdiv
	1,	//111 (0x6f) ddiv
	1,	//112 (0x70) irem
	1,	//113 (0x71) lrem
	1,	//114 (0x72) frem
	1,	//115 (0x73) drem
	1,	//116 (0x74) ineg
	1,	//117 (0x75) lneg
	1,	//118 (0x76) fneg
	1,	//119 (0x77) dneg
	1,	//120 (0x78) ishl
	1,	//121 (0x79) lshl
	1,	//122 (0x7a) ishr
	1,	//123 (0x7b) lshr
	1,	//124 (0x7c) iushr
	1,	//125 (0x7d) lushr
	1,	//126 (0x7e) iand
	1,	//127 (0x7f) land
	1,	//128 (0x80) ior
	1,	//129 (0x81) lor
	1,	//130 (0x82) ixor
	1,	//131 (0x83) lxor
	3,	//132 (0x84) iinc
	1,	//133 (0x85) i2l
	1,	//134 (0x86) i2f
	1,	//135 (0x87) i2d
	1,	//136 (0x88) l2i
	1,	//137 (0x89) l2f
	1,	//138 (0x8a) l2d
	1,	//139 (0x8b) f2i
	1,	//140 (0x8c) f2l
	1,	//141 (0x8d) f2d
	1,	//142 (0x8e) d2i
	1,	//143 (0x8f) d2l
	1,	//144 (0x90) d2f
	1,	//145 (0x91) i2b
	1,	//146 (0x92) i2c
	1,	//147 (0x93) i2s
	1,	//148 (0x94) lcmp
	1,	//149 (0x95) fcmpl
	1,	//150 (0x96) fcmpg
	1,	//151 (0x97) dcmpl
	1,	//152 (0x98) dcmpg
	3,	//153 (0x99) ifeq
	3,	//154 (0x9a) ifne
	3,	//155 (0x9b) iflt
	3,	//156 (0x9c) ifge
	3,	//157 (0x9d) ifgt
	3,	//158 (0x9e) ifle
	3,	//159 (0x9f) if_icmpeq
	3,	//160 (0xa0) if_icmpne
	3,	//161 (0xa1) if_icmplt
	3,	//162 (0xa2) if_icmpge
	3,	//163 (0xa3) if_icmpgt
	3,	//164 (0xa4) if_icmple
	3,	//165 (0xa5) if_acmpeq
	3,	//166 (0xa6) if_acmpne
	3,	//167 (0xa7) goto 
	3,	//168 (0xa8) jsr
	2,	//169 (0xa9) ret
	0,	//170 (0xaa) tableswitch
	0,	//171 (0xab) lookupswitch
	1,	//172 (0xac) ireturn
	1,	//173 (0xad) lreturn
	1,	//174 (0xae) freturn
	1,	//175 (0xaf) dreturn
	1,	//176 (0xb0) areturn
	1,	//177 (0xb1) return
	3,	//178 (0xb2) getstatic
	3,	//179 (0xb3) putstatic
	3,	//180 (0xb4) getfield
	3,	//181 (0xb5) putfield
	3,	//182 (0xb6) invokevirtual
	3,	//183 (0xb7) invokespecial
	3,	//184 (0xb8) invokestatic
	5,	//185 (0xb9) invokeinterface
	0,	//186 (0xba) xxxunusedxxx1
	3,	//187 (0xbb) new
	2,	//188 (0xbc) newarray
	3,	//189 (0xbd) anewarray
	1,	//190 (0xbe) arraylength
	1,	//191 (0xbf) athrow
	3,	//192 (0xc0) checkcast
	3,	//193 (0xc1) instanceof
	1,	//194 (0xc2) monitorenter
	1,	//195 (0xc3) monitorexit
	0,	//196 (0xc4) wide
	4,	//197 (0xc5) multianewarray
	3,	//198 (0xc6) ifnull
	3,	//199 (0xc7) ifnonnull
	5,	//200 (0xc8) goto_w
	5,	//201 (0xc9) jsr_w
	1,	//202 (0xca) breakpoint
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//
	0,	//254 (0xfe) impdep1
	0,	//255 (0xff) impdep2
};

/**
 * public, protected, privateフラグのチェックを実施する
 */
static bool check_visibility_flags(verifier* v, u2 access_flags) {
	// アクセス権フラグをチェックする
	const char* msg = NULL;
	if (is_public(access_flags)) {
		// ACC_PUBLIC以外に、ACC_PROTECTED または ACC_PRIVATE が同時に設定されている場合
		if (is_protected(access_flags)) {
			msg = "Both ACC_PUBLIC and ACC_PROTECTED is set";
		} else if (is_private(access_flags)) {
			msg = "Both ACC_PUBLIC and ACC_PRIVATE is set";
		}
	} else if (is_protected(access_flags)) {
		if (is_private(access_flags)) {
			// ACC_PROTECTED と ACC_PRIVATE が同時に設定されている場合
			msg = "Both ACC_PROTECTED and ACC_PRIVATE is set";
		}
	}

	if (msg) {
		throw_verify_error(v, msg);
	}

	return msg == NULL;
}

/**
 * 指定されたClassFileのaccess_flagsをチェックする
 */
static bool check_access_flags(verifier* v, ClassFile* cfile) {
	const u2 access_flags = cfile->access_flags;
	const char* msg = NULL;
	
	v->finfo = NULL;
	v->minfo = NULL;

	if (! check_visibility_flags(v, access_flags)) {
		return false;
	}

	if (! msg) {
		if (is_final(access_flags) && is_abstract(access_flags)) {
			// ACC_FINAL と ACC_ABSTRACT が同時にセットされている場合
			msg = "Both ACC_FINAL and ACC_ABSTRACT is set";
		}
	}

	// このチェックを通過できないクラスファイルがあるため、
	// チェックしないようにしている
//	if (is_interface(access_flags) && ! is_abstract(access_flags)) {
//		msg = "Interface should have ACC_ABSTRACT flag";
//	}

	if (msg) {
		throw_verify_error(v, msg);
	}

	return msg == NULL;
}

/**
 * 全フィールドをチェックする
 */
static bool check_fields(verifier* v, ClassFile* cfile) {
	const u2 access_flags = cfile->access_flags;
	const char* msg = NULL;
	v->minfo = NULL;
	for (u2 i = 0; i < cfile->fields_count; ++i) {
		field_info* finfo = &cfile->fields[i];
		v->finfo = finfo;

		// アクセスフラグをチェックする
		if (! check_visibility_flags(v, finfo->access_flags)) {
			return false;
		}

		if (is_final(access_flags) && is_volatile(access_flags)) {
			// ACC_FINAL と ACC_VOLATILE が同時にセットされている場合
			msg = "Both ACC_FINAL and ACC_VOLATILE is set";
			break;
		} else if (is_interface(cfile->access_flags)) {
			if (! (is_public(finfo->access_flags)
					&& is_static(finfo->access_flags)
					&& is_final(finfo->access_flags))) {
				msg = "Invalid interface field flags";
				break;
			}
		}

		// --- アクセスフラグのチェックここまで
	
		// 同一のフィールドが複数ないか調べる
		for (u2 j = i + 1; j < cfile->fields_count; ++j) {
			field_info* finfo2 = &cfile->fields[j];
			if (finfo->name == finfo2->name
					&& finfo->descriptor == finfo2->descriptor) {
				msg = "Same field declared more than once";
				break;
			}
		}

		if (msg) {
			break;
		}
	}

	if (msg) {
		throw_verify_error(v, msg);
	}

	return msg == NULL;
}

/**
 * 全メソッドをチェックする
 */
static bool check_methods(verifier* v, ClassFile* cfile) {
	v->finfo = NULL;
	for (u2 i = 0; i < cfile->methods_count; ++i) {
		method_info* minfo = &cfile->methods[i];
		v->minfo = minfo;

		const u2 access_flags = minfo->access_flags;
		// public/protected/privateフラグをチェックする
		if (! check_visibility_flags(v, access_flags)) {
			return false;
		}
		
		// インタフェースの場合
		if (is_interface(cfile->access_flags) && minfo->name != CLINIT_METHOD_NAME) {
			// ACC_PUBLIC と ACC_ABSTRACT が設定されていなければいけない
			if (! (is_abstract(access_flags) && is_public(access_flags))) {
				throw_verify_error(v, "ACC_PUBLIC and ACC_ABSTRACT flag must be set");
				return false;
			}
		}

		if (INIT_METHOD_NAME == minfo->name) {
			// インスタンス初期化メソッド
			if (is_static(access_flags)
					|| is_final(access_flags)
					|| is_synchronized(access_flags)
					|| is_native(access_flags)
					|| is_abstract(access_flags)) {
				throw_verify_error(v, "Method has invalid access flags");
				return false;
			}
		}

		// メソッドの名前の先頭が '<' である場合、"<init>" または "<clinit>"でなければ
		// ならない
		if (minfo->name[0] == '<') {
			if (minfo->name != CLINIT_METHOD_NAME && minfo->name != INIT_METHOD_NAME) {
				throw_verify_error(v, "Illegal method name");
				return false;
			}
		}

		// Codeアトリビュートをチェックする
		Code_attribute* codeattr = minfo->code_attribute;
		if (codeattr) {
			if (is_native(access_flags) || is_abstract(access_flags)) {
				// nativeメソッド/abstractメソッドはCodeアトリビュートを
				// 持つことができない
				throw_verify_error(v, "Native or abstract method cannot have code attribute");
				return false;
			}

			if (codeattr->code_length == 0 || codeattr->code_length > 65536) {
				// コード長がおかしい
				throw_verify_error(v, "Illegal code length");
				return false;
			}

			// 例外テーブルのチェックは、パス３で行う
			// これは、「有効な命令の先頭をさしているか」というチェックが
			// 必要になるためである
		
			// 同一のメソッドが２回以上宣言されていないか調べる
			for (u2 j = i + 1; j < cfile->methods_count; ++j) {
				method_info* minfo2 = &cfile->methods[j];
				if (minfo->name == minfo2->name
						&& minfo->descriptor == minfo2->descriptor) {
					throw_verify_error(cfile, minfo, "Same method declared more than once");
					return false;
				}
			}
		}
	}
	return true;
}

/**
 * 基底クラスが final クラスではないか、等を調べる。
 */
static bool check_class_inheritance(verifier* v,
									ClassFile* cfile,
									ClassFile* super_cfile,
									ClassFile** interfaces_cfiles) {
	char* msg = NULL;

	v->finfo = NULL;
	v->minfo = NULL;
	// 自分自身のスーパークラスを調べる
	if (super_cfile) {
		if (is_interface(cfile->access_flags)) {
			// interfaceのスーパークラスは常に java.lang.Objectでなければ
			// ならない
			if (super_cfile->this_class_name != JAVA_LANG_OBJECT_CLASS_NAME) {
				throw_verify_error(v, "Superclass of interface must be java.lang.Object");
				return false;
			}
		}

		if (is_final(super_cfile->access_flags)) {
			// スーパクラスがfinal
			throw_verify_error(v, "Super class is final");
			return false;
		}
		if (is_interface(super_cfile->access_flags)) {
			// スーパークラスが実はinterface
			throw_verify_error(v, "Super class is an interface");
			return false;
		}
	}

	// すべての実装インタフェースが、ACC_INTERFACE フラグを持っているか調べる
	for (u2 i = 0; i < cfile->interfaces_count; ++i) {
		ClassFile* ifile = interfaces_cfiles[i];
		if (! is_interface(ifile->access_flags)) {
			throw_verify_error(v, "All interfaces must have ACC_INTERFACE flag");
			return false;
		}
	}
	return true;
}

/**
 * Check fieldref and methodref
 */
static bool check_fieldref_and_methodref(frame* current_frame, ClassFile* cfile) {
	for (int i =  0; i < cfile->constant_pool_count; ++i) {
		u2 tag = cfile->constant_pool_tags[i];
		switch (tag) {
		case CONSTANT_Fieldref:
			if (! check_fieldref(current_frame,
								(CONSTANT_Fieldref_info*) cfile->constant_pool[i])) {
				return false;
			}
			break;

		case CONSTANT_Methodref:
			if (! check_methodref(current_frame,
								(CONSTANT_Methodref_info*) cfile->constant_pool[i])) {
				return false;
			}
			break;

		case CONSTANT_Double:
		case CONSTANT_Long:
			i++;
			break;
		}
	}
	return true;
}

/**
 * Check fieldref has valid name and descriptor
 */
static bool check_fieldref(frame* current_frame, CONSTANT_Fieldref_info* fieldref) {
	// Check signature only
	bool failed = false;
	const java_utf8* desc = fieldref->field_descriptor;
	while (*desc && ! failed) {
		java_utf8 c = *desc;
		switch (c) {
		case 'Z':	// boolean
		case 'B':	// byte
		case 'C':	// char
		case 'S':	// short
		case 'I':	// int
		case 'J':	// long
		case 'F':	// float
		case 'D':	// double
			if (*(desc + 1)) {
				failed = true;
				break;
			}
			break;
		
		case 'L':	// Object
			while (*desc && *desc != ';') {
				desc++;
			}
			if (*desc != ';') {
				failed = true;
			} else if (*(desc + 1)) {
				failed = true;
			}
			break;

		case '[':	// Array
			break;

		default:
			failed = true;
			break;
		}
		desc++;
	}

	if (failed) {
		char* msg = (char*) malloc(128);
		if (! msg) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		_snprintf(msg,
				  128 - 1,
				  "Invalid field signature %s",
				  desc);
		msg[127] = '\0';
		throw_exception(current_frame,
						"java/lang/VerifyError",
						msg);
		free(msg);
	}
	return ! failed;
}

/**
 * Check methodref has valid name and descriptor.
 */
static bool check_methodref(frame* current_frame, CONSTANT_Methodref_info* methodref) {
	// Only checks descriptor
	const java_utf8* desc = methodref->method_descriptor;
	bool valid = false;
	if (*desc == '(') {
		// Simply checks well-formed
		// "\(.*\).*"
		while (*desc && *desc != ')') {
			desc++;
		}
		if (*desc == ')' && *(desc + 1)) {
			valid = true;
		}
	}
	return valid;
}


/**
 * Verify class file (pass 2)
 */
bool verify_pass2(frame* current_frame,
				  ClassFile* cfile,
				  ClassFile* super_cfile,
				  ClassFile** interfaces_cfiles) {
	if (! cfile || cfile->status >= CLASS_FILE_VERIFIED) {
		return true;
	}
	DBG(_T("Verifying (pass2) class="));
	DBG_UTF8(cfile->this_class_name);
	DBG(_T("\n"));

	// update status
	cfile->status = CLASS_FILE_VERIFYING;

	verifier v;
	v.cfile = cfile;
	v.current_frame = current_frame;

	// Check access_flags
	if (! check_access_flags(&v, cfile)) {
		return false;
	}

	// Every class has superclass except java/lang/Object.
	if (! super_cfile && (JAVA_LANG_OBJECT_CLASS_NAME != cfile->this_class_name)) {
		throw_exception(current_frame,
						"java/lang/VerifyError",
						"Superclass is null");
		return false;
	}

	// Check inheritance. (Super class is not final)
	if (! check_class_inheritance(&v, cfile, super_cfile, interfaces_cfiles)) {
		return false;
	}

	// Check fields.
	if (! check_fields(&v, cfile)) {
		return false;
	}

	// Check methods
	if (! check_methods(&v, cfile)) {
	   return false;
	}

	// Check final methods are not overridden.
	
	//
	// Constant pool consistency is already checked in parse_ClassFile()
	//
	
	// Check field references and method references have valid names.
	if (! check_fieldref_and_methodref(current_frame, cfile)) {
		return false;
	}

	DBG(_T("Verified (pass2) class="));
	DBG_UTF8(cfile->this_class_name);
	DBG(_T("\n"));

	return true;
}

/**
 * Verify pass 3 (Bytecode verifier)
 */
bool verify_pass3(frame* current_frame,
				  ClassFile* cfile) {

	if (! cfile || cfile->status >= CLASS_FILE_VERIFIED) {
		return true;
	}
	DBG(_T("Verifying (pass3) class="));
	DBG_UTF8(cfile->this_class_name);
	DBG(_T("\n"));

	bool result = true;
	
	// 全メソッドをチェックする
	for (u2 i = 0; i < cfile->methods_count; ++i) {
		method_info* minfo = &cfile->methods[i];
		Code_attribute* cattr = minfo->code_attribute;
		if (! cattr) {
			// abstract/nativeメソッドはベリファイできない
			continue;
		}
		
		DBG(_T("Verifying "));
		DBG_UTF8(cfile->this_class_name);
		DBG(_T("."));
		DBG_UTF8(minfo->name);
		DBG_UTF8(minfo->descriptor);
		DBG(_T("\n"));

		// データフローアナライザを初期化する
		data_flow_analyzer* analyzer = create_data_flow_analyzer(current_frame,
																 cfile,
																 minfo);
		if (! analyzer) {
			result = false;
			break;
		}

		// データ・フロー・アナライザを開始する
		result =  analyze(analyzer);

		// data_flow_analyzerに割り当てたメモリを開放する
		delete_data_flow_analyzer(analyzer);

		if (! result) {
			// ベリファイ失敗
			break;
		}
	}

	DBG(_T("Verified (pass 3) class="));
	DBG_UTF8(cfile->this_class_name);
	if (result) {
		cfile->status = CLASS_FILE_VERIFIED;
		DBG(_T(" passed\n"));
	} else {
		DBG(_T(" failed\n"));
	}
	return result;
}

/**
 * 各命令を区切る。
 * 同時に、データモデルなしで可能なチェックも実施する
 */
static bool split_instructions(data_flow_analyzer* analyzer,
							   data_item* return_type) {
	// この関数内でのみ使用するマクロ
#define SET_CODE_FLAG(pc, flag) \
	{ \
		if (! check_code_overrun(analyzer, \
								 code_length, \
								 pc)) { \
			ERROR_RETURN(); \
		} \
		code_flags[(pc)] |= (flag); \
	}

#define ERROR_RETURN() { free(code_flags); return false; }

	frame* current_frame = analyzer->current_frame;
	const Code_attribute* cattr = analyzer->minfo->code_attribute;
	const u1* code = cattr->code;
	const u4 code_length = cattr->code_length;
	const u2 max_locals = analyzer->minfo->code_attribute->max_locals;
	u1* code_flags = (u1*) calloc(sizeof(u1) * code_length, 1);

	// 命令を区切る
	{
		u4 pc = 0;
		bool wide = false;
		bool done = false;
		SET_CODE_FLAG(0, (CF_INSTRUCTION | CF_START_OF_VERIFIED_UNIT));
		while (! done) {
			// 命令の開始位置フラグ
			if (! wide) {
				SET_CODE_FLAG(pc, CF_INSTRUCTION);
			}
			const u1 instruction = code[(u2) pc];
			switch (instruction) {
			case NOP:
			case ACONST_NULL:
			case ICONST_M1:
			case ICONST_0:
			case ICONST_1:
			case ICONST_2:
			case ICONST_3:
			case ICONST_4:
			case ICONST_5:
			case LCONST_0:
			case LCONST_1:
			case FCONST_0:
			case FCONST_1:
			case FCONST_2:
			case DCONST_0:
			case DCONST_1:
				pc++;
				break;
			
			case BIPUSH:
				pc += 2;
				break;

			case SIPUSH:
				pc += 3;
				break;
			
			case LDC:
				{
					if (! check_code_overrun(analyzer, code_length, pc + 1)) {
						// 命令が途中で終わっている
						ERROR_RETURN();
					}
					u1 index = code[pc + 1];
					if (index >= analyzer->cfile->constant_pool_count) {
						throw_verify_error(analyzer, "Invalid index in ldc instruction");
						ERROR_RETURN();
					}
					switch (analyzer->cfile->constant_pool_tags[index]) {
					case CONSTANT_Integer:
					case CONSTANT_Float:
					case CONSTANT_String:
						break;
					default:
						throw_verify_error(analyzer, "ldc instruction refers invalid constant pool type");
						ERROR_RETURN();
					}
					pc += 2;
				}
				break;

			case LDC_W:
				{
					if (! check_code_overrun(analyzer, code_length, pc + 2)) {
						// 命令が途中で終わっている
						ERROR_RETURN();
					}
					u2 index = MAKE_U2(code[pc + 1], code[pc + 2]);
					if (index >= analyzer->cfile->constant_pool_count) {
						throw_verify_error(analyzer, "Invalid index in ldc_w instruction");
						ERROR_RETURN();
					}

					// integer, float, String のみロード可能
					switch (analyzer->cfile->constant_pool_tags[index]) {
					case CONSTANT_Integer:
					case CONSTANT_Float:
					case CONSTANT_String:
						break;

					default:
						throw_verify_error(analyzer, "ldc_w instruction refers invalid constant pool type");
						ERROR_RETURN();
					}
					pc += 3;
				}
				break;

			case LDC2_W:
				{
					if (! check_code_overrun(analyzer, code_length, pc + 2)) {
						// 命令が途中で終わっている
						ERROR_RETURN();
					}
					u2 index = MAKE_U2(code[pc + 1], code[pc + 2]);
					if (index >= analyzer->cfile->constant_pool_count) {
						throw_verify_error(analyzer, "Invalid index in ldc2_w instruction");
						ERROR_RETURN();
					}

					// long, double のみロード可能
					switch (analyzer->cfile->constant_pool_tags[index]) {
					case CONSTANT_Long:
					case CONSTANT_Double:
						break;
					default:
						throw_verify_error(analyzer, "ldc2_w instruction refers invalid constant pool type");
						ERROR_RETURN();
					}
					pc += 3;
				}
				break;

			case ILOAD:
			case FLOAD:
			case LLOAD:
			case DLOAD:
			case ALOAD:
			case ISTORE:
			case FSTORE:
			case LSTORE:
			case DSTORE:
			case ASTORE:
				{
					int param_len;
					if (wide) {
						param_len = 2;
					} else {
						param_len = 1;
					}
					if (! check_code_overrun(analyzer, code_length, pc + param_len)) {
						// 命令が途中で終わっている
						ERROR_RETURN();
					}
					u2 index;
					if (wide) {
						index = MAKE_U2(code[pc + 1], code[pc + 2]);
					} else {
						index = code[pc + 1];
					}
					if (instruction == LSTORE || instruction == DSTORE) {
						// もう１つローカル変数が必要
						index++;
					}
					if (! check_local_index(analyzer, max_locals, index)) {
						ERROR_RETURN();
					}
					if (wide) {
						pc += 3;
					} else {
						pc += 2;
					}
					wide = false;
				}
				break;

			case ILOAD_0:
			case FLOAD_0:
			case LLOAD_0:
			case DLOAD_0:
			case ALOAD_0:
			case ISTORE_0:
			case FSTORE_0:
			case LSTORE_0:
			case DSTORE_0:
			case ASTORE_0:
				{
					if (! check_local_index(analyzer, max_locals, 0)) {
						ERROR_RETURN();
					}
					pc++;
				}
				break;

			case ILOAD_1:
			case FLOAD_1:
			case LLOAD_1:
			case DLOAD_1:
			case ALOAD_1:
			case ISTORE_1:
			case FSTORE_1:
			case LSTORE_1:
			case DSTORE_1:
			case ASTORE_1:
				{
					if (! check_local_index(analyzer, max_locals, 1)) {
						ERROR_RETURN();
					}
					pc++;
				}
				break;

			case ILOAD_2:
			case FLOAD_2:
			case LLOAD_2:
			case DLOAD_2:
			case ALOAD_2:
			case ISTORE_2:
			case FSTORE_2:
			case LSTORE_2:
			case DSTORE_2:
			case ASTORE_2:
				{
					if (! check_local_index(analyzer, max_locals, 2)) {
						ERROR_RETURN();
					}
					pc++;
				}
				break;

			case ILOAD_3:
			case FLOAD_3:
			case LLOAD_3:
			case DLOAD_3:
			case ALOAD_3:
			case ISTORE_3:
			case FSTORE_3:
			case LSTORE_3:
			case DSTORE_3:
			case ASTORE_3:
				{
					if (! check_local_index(analyzer, max_locals, 3)) {
						ERROR_RETURN();
					}
					pc++;
				}
				break;
			
			case IALOAD:
			case LALOAD:
			case FALOAD:
			case DALOAD:
			case AALOAD:
			case BALOAD:
			case CALOAD:
			case SALOAD:
			case IASTORE:
			case LASTORE:
			case FASTORE:
			case DASTORE:
			case AASTORE:
			case BASTORE:
			case CASTORE:
			case SASTORE:
				pc++;
				break;

			case POP:
			case POP2:
				pc++;
				break;
	
			case DUP:
			case DUP_X1:
			case DUP_X2:
			case DUP2:
			case DUP2_X1:
			case DUP2_X2:
				pc++;
				break;

			case SWAP:
				pc++;
				break;

			case IADD:
			case LADD:
			case FADD:
			case DADD:
			case ISUB:
			case LSUB:
			case FSUB:
			case DSUB:
			case IMUL:
			case LMUL:
			case FMUL:
			case DMUL:
			case IDIV:
			case LDIV:
			case FDIV:
			case DDIV:
			case IREM:
			case LREM:
			case FREM:
			case DREM:
			case INEG:
			case LNEG:
			case FNEG:
			case DNEG:
			case ISHL:
			case LSHL:
			case ISHR:
			case LSHR:
			case IUSHR:
			case LUSHR:
			case IAND:
			case LAND:
			case IOR:
			case LOR:
			case IXOR:
			case LXOR:
				pc++;
				break;

			case IINC:
				{
					int param_len;
					if (wide) {
						param_len = 4;
					} else {
						param_len = 2;
					}
					if (! check_code_overrun(analyzer, code_length, pc + param_len)) {
						ERROR_RETURN();
					}
					u2 index;
					if (wide) {
						index = MAKE_U2(code[pc + 1], code[pc + 2]);
					} else {
						index = code[pc + 1];
					}
					if (! check_local_index(analyzer, max_locals, index)) {
						ERROR_RETURN();
					}
					if (wide) {
						pc += 5;
					} else {
						pc += 3;
					}
					wide = false;
				}
				break;

			case I2L:
			case I2F:
			case I2D:
			case L2I:
			case L2F:
			case L2D:
			case F2I:
			case F2L:
			case F2D:
			case D2I:
			case D2L:
			case D2F:
			case I2B:
			case I2C:
			case I2S:
				pc++;
				break;

			case LCMP:
			case FCMPL:
			case FCMPG:
			case DCMPL:
			case DCMPG:
				pc++;
				break;

			case IFEQ:
			case IFNE:
			case IFLT:
			case IFGE:
			case IFGT:
			case IFLE:
			case IF_ICMPEQ:
			case IF_ICMPNE:
			case IF_ICMPLT:
			case IF_ICMPGE:
			case IF_ICMPGT:
			case IF_ICMPLE:
			case IF_ACMPEQ:
			case IF_ACMPNE:
			case IFNONNULL:
			case IFNULL:
				{
					// ベリファイユニットの個数をできる限り少なくするため、
					// 条件分岐命令はベリファイユニットの終端とはしない
					// SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);

					if (! check_code_overrun(analyzer, code_length, pc + 2)) {
						ERROR_RETURN();
					}
					__int16 offset = MAKE_INT16(code[pc + 1], code[pc + 2]);
					u4 target_pc = pc + offset;
					SET_CODE_FLAG(target_pc, CF_START_OF_VERIFIED_UNIT);
					
					pc += 3;
				}
				break;

			case GOTO:
				{
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					if (! check_code_overrun(analyzer, cattr->code_length, pc + 2)) {
						ERROR_RETURN();
					}

					__int16 offset = MAKE_INT16(code[pc + 1] , code[pc + 2]);
					u4 target_pc = pc + offset;
					SET_CODE_FLAG(target_pc, CF_START_OF_VERIFIED_UNIT);
					pc += 3;
				}
				break;
			
			case GOTO_W:
				{
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					if (! check_code_overrun(analyzer, cattr->code_length, pc + 4)) {
						ERROR_RETURN();
					}
					__int32 offset = MAKE_INT32(code[pc + 1] , code[pc + 2], code[pc + 3], code[pc + 4]);
					u4 target_pc = pc + offset;
					SET_CODE_FLAG(target_pc, CF_START_OF_VERIFIED_UNIT);

					pc += 5;
				}
				break;

			case JSR:
				{
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					
					if (! check_code_overrun(analyzer, code_length, pc + 3)) {
						ERROR_RETURN();
					}
					__int16 offset = MAKE_INT16(code[pc + 1], code[pc + 2]);
					u4 target_pc = pc + offset;
					SET_CODE_FLAG(target_pc, CF_START_OF_VERIFIED_UNIT);

					pc += 3;
					// jsrの次の命令に開始フラグをつける
					SET_CODE_FLAG(pc, CF_START_OF_VERIFIED_UNIT);
				}
				break;

			case JSR_W:
				{
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					if (! check_code_overrun(analyzer, code_length, pc + 5)) {
						ERROR_RETURN();
					}
					__int32 offset = MAKE_INT32(code[pc + 1], code[pc + 2], code[pc + 3], code[pc + 4]);
					u4 target_pc = pc + offset;
					SET_CODE_FLAG(target_pc, CF_START_OF_VERIFIED_UNIT);

					pc += 5;
					// jsr_wの次の命令に開始フラグをつける
					SET_CODE_FLAG(pc, CF_START_OF_VERIFIED_UNIT);
				}
				break;

			case RET:
				{
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					int param_len;
					if (wide) {
						param_len = 2;
					} else {
						param_len = 1;
					}
					if (! check_code_overrun(analyzer, code_length, pc + param_len)) {
						ERROR_RETURN();
					}
					u2 index;
					if (wide) {
						index = MAKE_U2(code[pc + 1], code[pc + 2]);
					} else {
						index = code[pc + 1];
					}
					if (! check_local_index(analyzer, max_locals, index)) {
						ERROR_RETURN();
					}
					if (wide) {
						pc += 3;
					} else {
						pc += 2;
					}
					wide = false;
				}
				break;

			case TABLESWITCH:
				{
					// この命令はベリファイユニットの終端となる
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);

					// 4バイト境界からデータを読み込む
					const u2 startpos = (u2) ALIGN_UP4(pc);
					if (! check_code_overrun(analyzer, cattr->code_length, startpos + 12)) {
						ERROR_RETURN();
					}
					const __int32* data = (__int32*) &code[startpos];
					
					u4 target_pc = pc + TO_LITTLE_ENDIAN(data[0]);
					if (! check_code_overrun(analyzer, cattr->code_length, target_pc)) {
						ERROR_RETURN();
					}
					// ジャンプ先をマークする
					SET_CODE_FLAG(target_pc, CF_START_OF_VERIFIED_UNIT);

					__int32 low = TO_LITTLE_ENDIAN(data[1]);
					__int32 high = TO_LITTLE_ENDIAN(data[2]);
					if (low > high) {
						// lowとhightの値がおかしい
						throw_exception(current_frame,
										"java/lang/VerifyError",
										"illegal tableswitch");
						ERROR_RETURN();
					}
					
					for (int i = low; i <= high; ++i) {
						int index = i - low;
						target_pc = pc + TO_LITTLE_ENDIAN(data[3 + index]);
						// ジャンプ先をマークする
						SET_CODE_FLAG(target_pc, CF_START_OF_VERIFIED_UNIT);
					}

					// 次の命令位置を計算する
					pc = startpos
						  + 4							// defaultbytes
						  + 4							// low
						  + 4							// high
						  + ((high - low + 1) * 4);		// jump offsets...
				}
				break;

			case LOOKUPSWITCH:
				{
					// 4バイト境界からデータを読み込む
					const u2 startpos = (u2) ALIGN_UP4(pc);
					if (! check_code_overrun(analyzer, cattr->code_length, startpos + 8)) {
						ERROR_RETURN();
					}
					const __int32* data = (__int32*) &code[startpos];
					__int32 npairs = data[1];
					// little-endianにする
					npairs = TO_LITTLE_ENDIAN(npairs);

					if (npairs <= 0) {
						throw_verify_error(analyzer, "Illegal npairs in lookupswitch");
						ERROR_RETURN();
					}

					// ジャンプ先のpcを計算し、印をつける
					__int32 defaultoffset = TO_LITTLE_ENDIAN(data[0]);
					u4 target_pc = pc + defaultoffset;
					SET_CODE_FLAG(target_pc, CF_START_OF_VERIFIED_UNIT);

					for (__int32 i = 2; i < npairs + 2; i += 2) {
						target_pc = pc + TO_LITTLE_ENDIAN(data[i + 1]);
						SET_CODE_FLAG(target_pc, CF_START_OF_VERIFIED_UNIT);
					}

					// 次の命令位置を決定する
					pc = startpos
						+ 4					// defaultbytes
						+ 4					// npairs
						+ (npairs * 8);		// match-offset
				}
				break;
			
			case IRETURN:
				{
					// メソッドの戻り値をチェックする
					if (return_type->type != ITEM_Integer) {
						throw_verify_error(analyzer, "Return type mismatch");
					}
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					pc++;
				}
				break;
				
			case FRETURN:
				{
					// メソッドの戻り値をチェックする
					if (return_type->type != ITEM_Float) {
						throw_verify_error(analyzer, "Return type mismatch");
					}
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					pc++;
				}
				break;

			case LRETURN:
				{
					// メソッドの戻り値をチェックする
					if (return_type->type != ITEM_Long) {
						throw_verify_error(analyzer, "Return type mismatch");
					}
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					pc++;
				}
				break;

			case DRETURN:
				{
					// メソッドの戻り値をチェックする
					if (return_type->type != ITEM_Double) {
						throw_verify_error(analyzer, "Return type mismatch");
					}
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					pc++;
				}
				break;

			case ARETURN:
				{
					if (return_type->type != ITEM_Object) {
						throw_verify_error(analyzer, "Return type mismatch");
					}
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					pc++;
				}
				break;

			case RETURN:
				{
					// メソッドの戻り値をチェックする
					if (return_type->type != ITEM_Invalid) {
						throw_verify_error(analyzer, "Return type mismatch");
					}
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					pc++;
				}
				break;

			case GETSTATIC:
			case PUTSTATIC:
			case GETFIELD:
			case PUTFIELD:
				pc += 3;
				break;

			case INVOKEVIRTUAL:
			case INVOKESPECIAL:
			case INVOKESTATIC:
				pc += 3;
				break;

			case INVOKEINTERFACE:
				pc += 5;
				break;

			case NEW:
				{
					if (! check_code_overrun(analyzer, code_length, pc + 2)) {
						ERROR_RETURN();
					}
					// CONSTANT_Class_infoを参照しているか調べる
					u2 index = ((u2) code[(u2) pc + 1] << 8) | (code[(u2) pc + 2] & 0xff);
					if (index >= analyzer->cfile->constant_pool_count
							|| analyzer->cfile->constant_pool_tags[index] != CONSTANT_Class) {
						throw_verify_error(analyzer, "Illegal new instruction");
						ERROR_RETURN();
					}
					pc += 3;
				}
				break;

			case NEWARRAY:
				pc += 2;
				break;

			case ANEWARRAY:
				pc += 3;
				break;

			case ARRAYLENGTH:
				pc++;
				break;

			case ATHROW:
				{
					SET_CODE_FLAG(pc, CF_END_OF_VERIFIED_UNIT);
					pc++;
				}
				break;

			case CHECKCAST:
				{
					// CONSTANT_Class_infoを参照しているか調べる
					u2 index = MAKE_U2(code[pc + 1],
									   code[pc + 2]);

					if (index >= analyzer->cfile->constant_pool_count
						|| analyzer->cfile->constant_pool_tags[index] != CONSTANT_Class) {
						throw_verify_error(analyzer, "Invalid index of checkcast instruction");
						return false;
					}

					pc += 3;
				}
				break;

			case INSTANCEOF:
				pc += 3;
				break;

			case MONITORENTER:
			case MONITOREXIT:
				pc++;
				break;

			case WIDE:
				{
					// 次の命令で長さが決まる
					if (! check_code_overrun(analyzer, code_length, pc + 1)) {
						ERROR_RETURN();
					}
					u1 opcode = code[pc + 1];
					switch (opcode) {
					case 21:	// iload
					case 22:	// lload
					case 23:	// fload
					case 24:	// dload
					case 25:	// aload
					case 54:	// istore
					case 55:	// lstore
					case 56:	// fstore
					case 57:	// dstore
					case 58:	// astore
					case 132:	// iinc
					case 169:	// ret
						wide = true;
						break;
					
					default:
						throw_verify_error(analyzer, "illegal wide instruction");
						ERROR_RETURN();
					}

					pc++;	// 次の命令は wide で修飾された命令の先頭になる
				}
				break;

			case MULTIANEWARRAY:
				pc += 4;
				break;

			default:
				throw_verify_error(analyzer, "Unknown instruction");
				ERROR_RETURN();
			}

			if (pc == cattr->code_length) {
				// 命令終わり
				done = true;
			} else if (cattr->code_length < pc) {
				// 命令がはみ出している
				throw_exception(current_frame,
								"java/lang/VerifyError",
								"Invalid instruction");
				ERROR_RETURN();
			}
		}
	}

	// 例外ハンドラをマークする
	{
		u2 exception_table_length = cattr->exception_table_length;
		for (u2 i = 0; i < exception_table_length; ++i) {
			exception_handler_info* handler = cattr->exception_table + i;
			if (! (code_flags[handler->start_pc] & CF_INSTRUCTION)
				|| (handler->start_pc >= handler->end_pc) ) {
				throw_verify_error(analyzer, "Illegal exception handler");
				ERROR_RETURN();
			}
			SET_CODE_FLAG(handler->start_pc, CF_START_OF_PROTECTED_CODE);
			SET_CODE_FLAG(handler->handler_pc, (CF_START_OF_VERIFIED_UNIT | CF_START_OF_EXCEPTION_HANDLER));
		}
	}

	// ジャンプ先がすべて命令の先頭であることのチェックと
	// CF_END_OF_VERIFIED_UNITフラグの追加を行う
	{
		u2 verified_unit_count = 0;
		u4 prev_pc = 0;
		bool end_flag_exists = true;
		for (u2 i = 0; i < code_length; ++i) {
			if (code_flags[i] & CF_START_OF_VERIFIED_UNIT) {
				// ベリファイユニットの先頭
				if (! (code_flags[i] & CF_INSTRUCTION)) {
					// 命令の先頭ではない
					throw_verify_error(analyzer, "Illegal jump target");
					ERROR_RETURN();
				}
				if (! end_flag_exists) {
					// CF_END_OF_VERIFIED_UNITフラグが登場する前にCF_START_OF_VERIFIED_UNITフラグを
					// 見つけた場合、１つ手前の命令にCF_END_OF_VERIFIED_UNITフラグをつける
					SET_CODE_FLAG(prev_pc, CF_END_OF_VERIFIED_UNIT);
				}
				verified_unit_count++;
				end_flag_exists = false;
			}
			if (code_flags[i] & CF_END_OF_VERIFIED_UNIT) {
				end_flag_exists = true;
			}
			if (code_flags[i] & CF_INSTRUCTION) {
				prev_pc = i;
			}
		}
		SET_CODE_FLAG(prev_pc, CF_END_OF_VERIFIED_UNIT);

		// verified_unitを確保する
		analyzer->verified_unit_count = verified_unit_count;
		analyzer->verified_units
			= (verified_unit*) calloc(sizeof(verified_unit) * verified_unit_count, 1);

		// verified_unitを初期化する
		int unit_index = -1;
		bool in_unit = false;
		for (u4 i = 0; i < code_length; ++i) {
			if (code_flags[i] & CF_START_OF_VERIFIED_UNIT) {
				unit_index++;
				assert(unit_index < verified_unit_count);
				in_unit = true;
				analyzer->verified_units[unit_index].start_pc = i;
				if (code_flags[i] & CF_START_OF_EXCEPTION_HANDLER) {
					analyzer->verified_units[unit_index].exception_handler = 1;
				}
			}
			if (code_flags[i] & CF_END_OF_VERIFIED_UNIT) {
				if (! in_unit) {
					continue;
				}
				analyzer->verified_units[unit_index].end_pc = i;
				in_unit = false;
			}
		}
	}

	// 構造体に設定する
	analyzer->code_flags = code_flags;

#ifdef DEBUG
	// デバッグ出力
	DBG(_T("Verified verified_units:\n"));
	for (int idx = 0; idx < analyzer->verified_unit_count; ++idx) {
		verified_unit* unit = &analyzer->verified_units[idx];
		DBG(_T("start_pc="));
		DBG_INT(unit->start_pc);
		DBG(_T(" end_pc="));
		DBG_INT(unit->end_pc);
		if (unit->exception_handler) {
			DBG(_T(" (exception handler)"));
		}
		DBG(_T("\n"));
	}
#endif

	return true;

// 関数内でのみ使用するマクロをundefする
#undef SET_CODE_FLAG
#undef ERROR_RETURN
}

/**
 * 変更されたベリファイユニットへのポインタを返す
 * 存在しない場合はNULLを返す。
 */
static verified_unit* get_changed_unit(data_flow_analyzer* analyzer) {
	for (u2 i = 0; i < analyzer->verified_unit_count; ++i) {
		verified_unit* unit = &analyzer->verified_units[i];
		if (unit->changed || unit->ret_list_changed) {
			return unit;
		}
	}
	return NULL;
}

/**
 * データフローアナライザ
 */
static bool analyze(data_flow_analyzer* analyzer) {
	// 変更ビットがすべてクリアされるまで、以下のループを繰り返す
	verified_unit* unit;
	while ((unit = get_changed_unit(analyzer)) != NULL) {
		// 命令を分析する
		DBG(_T("Verifying unit start_pc="));
		DBG_INT(unit->start_pc);
		DBG(_T(" end_pc="));
		DBG_INT(unit->end_pc);
		DBG(_T("\n"));

		if (! analyze(analyzer, unit)) {
			// ベリファイ失敗
			return false;
		}
	}
	// ベリファイ成功	
	return true;
}

/**
 * メソッドのデータフロー解析を行う
 */
static bool analyze(data_flow_analyzer* analyzer,
					verified_unit* target_unit) {
	method_info* minfo = analyzer->minfo;
	Code_attribute* cattr = minfo->code_attribute;
	const u1* code = cattr->code;
	bool already_merged = false;

	if (target_unit->changed) {
		DBG(_T("data model is changed. analyze it.\n"));

		// 変更フラグをクリアする
		target_unit->changed = 0;

		// 開始位置を設定する
		u2 current_pc = target_unit->start_pc;

		// 作業領域にコピーする
		verified_unit* unit = &analyzer->work_unit;
		unit->changed = target_unit->changed;
		unit->ret_list_changed = target_unit->ret_list_changed;
		unit->exception_handler = target_unit->exception_handler;
		unit->start_pc = target_unit->start_pc;
		unit->end_pc = target_unit->end_pc;
		unit->operand_stack_position = target_unit->operand_stack_position;
		unit->ret_target_count = target_unit->ret_target_count;
		unit->ret_target_pc = target_unit->ret_target_pc;
		unit->ret_target_pc_list = target_unit->ret_target_pc_list;
		memcpy(unit->local_variables,
			   target_unit->local_variables,
			   sizeof(data_item) * (cattr->max_locals + target_unit->operand_stack_position));


		// ユニットの終端まで解析する
		bool wide = false;
		while (current_pc <= unit->end_pc) {
			u1 inst = code[current_pc];
			u2 inst_len = g_instruction_length[inst];
			bool local_variable_changed = false;
			switch (inst) {
			case NOP:
				break;

			case ACONST_NULL:
				{
					// nullをpushする
					data_item item;
					item.type = ITEM_Null;
					if (! push(analyzer, unit, &item)) {
						return false;
					}
				}
				break;

			case BIPUSH:
			case SIPUSH:
			case ICONST_M1:
			case ICONST_0:
			case ICONST_1:
			case ICONST_2:
			case ICONST_3:
			case ICONST_4:
			case ICONST_5:
				// 整数（カテゴリ１）push命令
				{
					// スタックに整数値をpushする
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case LCONST_0:
			case LCONST_1:
				// longのpush命令
				{
					// スタックにpushする
					if (! push_long( analyzer, unit)) {
						return false;
					}
				}
				break;

			case FCONST_0:
			case FCONST_1:
			case FCONST_2:
				// floatのpush命令
				{
					// スタックにfloatをpushする
					if (! push_float( analyzer, unit)) {
						return false;
					}
				}
				break;

			case DCONST_0:
			case DCONST_1:
				// doubleをpushする命令
				{
					// スタックにdoubleをpushする
					if (! push_double( analyzer, unit)) {
						return false;
					}
				}
				break;

			case LDC:
			case LDC_W:
				{
					// コンスタントプールの内容に従い、スタックに値をpushする
					u2 index = code[(u2) current_pc + 1];
					if (inst == LDC_W) {
						index = (index << 8) | (code[(u2) current_pc + 2] & 0xff);
					}
					switch (analyzer->cfile->constant_pool_tags[index]) {
					case CONSTANT_Integer:
						if (! push_int( analyzer, unit)) {
							return false;
						}
						break;
					
					case CONSTANT_Float:
						if (! push_float( analyzer, unit)) {
							return false;
						}
						break;
					
					case CONSTANT_String:
						if (! push_object(analyzer, unit, intern_utf8("java/lang/String"))) {
							return false;
						}
						break;
					default:
						// ここにはこないはず（split_instruction内でチェック済みのため）
						assert(false);
					}
				}
				break;

			case LDC2_W:
				{
					// コンスタントプールの内容に従い、スタックに値をpushする
					u2 index = ((u2) code[(u2) current_pc + 1] << 8) | (code[(u2) current_pc + 2] & 0xff);
					switch (analyzer->cfile->constant_pool_tags[index]) {
					case CONSTANT_Long:
						if (! push_long( analyzer, unit)) {
							return false;
						}
						break;
					
					case CONSTANT_Double:
						if (! push_double( analyzer, unit)) {
							return false;
						}
						break;
					
					default:
						// ここにはこないはず（split_instruction内でチェック済みのため）
						assert(false);
					}
				}
				break;

			case ILOAD:
				{
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}

					// ローカル変数を取得する
					if (! get_int(analyzer, unit, index)) {
						return false;
					}
					
					// スタックにintをpushする
					if (! push_int(analyzer, unit)) {
						return false;
					}
				}
				break;

			case ILOAD_0:
			case ILOAD_1:
			case ILOAD_2:
			case ILOAD_3:
				{
					u2 index = inst - ILOAD_0;

					// ローカル変数を取得する
					if (! get_int(analyzer, unit, index)) {
						return false;
					}
					
					// スタックにintをpushする
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case LLOAD:
				{
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}

					// ローカル変数を取得する
					if (! get_long(analyzer, unit, index)) {
						return false;
					}
					
					// スタックにlongをpushする
					if (! push_long( analyzer, unit)) {
						return false;
					}
				}
				break;

			case LLOAD_0:
			case LLOAD_1:
			case LLOAD_2:
			case LLOAD_3:
				{
					u2 index = inst - LLOAD_0;

					// ローカル変数を取得する
					if (! get_long(analyzer, unit, index)) {
						return false;
					}
					
					// スタックにlongをpushする
					if (! push_long( analyzer, unit)) {
						return false;
					}
				}
				break;

			case FLOAD:
				{
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}

					// ローカル変数を取得する
					if (! get_float(analyzer, unit, index)) {
						return false;
					}
					
					// スタックにfloatをpushする
					if (! push_float( analyzer, unit)) {
						return false;
					}
				}
				break;

			case FLOAD_0:
			case FLOAD_1:
			case FLOAD_2:
			case FLOAD_3:
				{
					u2 index = inst - FLOAD_0;

					// ローカル変数を取得する
					if (! get_float(analyzer, unit, index)) {
						return false;
					}
					
					// スタックにfloatをpushする
					if (! push_float( analyzer, unit)) {
						return false;
					}
				}
				break;

			case DLOAD:
				{
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}

					// ローカル変数を取得する
					if (! get_double(analyzer, unit, index)) {
						return false;
					}
					
					// スタックにdoubleをpushする
					if (! push_double( analyzer, unit)) {
						return false;
					}
				}
				break;

			case DLOAD_0:
			case DLOAD_1:
			case DLOAD_2:
			case DLOAD_3:
				{
					u2 index = inst - DLOAD_0;

					// ローカル変数を取得する
					if (! get_double(analyzer, unit, index)) {
						return false;
					}
					
					// スタックにdoubleをpushする
					if (! push_double( analyzer, unit)) {
						return false;
					}
				}
				break;

			case ALOAD:
				{
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}

					// ローカル変数を取得する
					data_item item;
					if (! get_object(analyzer, unit, index, &item)) {
						return false;
					}
					
					// スタックにobjectをpushする
					if (! push(analyzer,
							   unit,
							   &item)) {
						return false;
					}
				}
				break;

			case ALOAD_0:
			case ALOAD_1:
			case ALOAD_2:
			case ALOAD_3:
				{
					u2 index = inst - ALOAD_0;

					// ローカル変数を取得する
					data_item item;
					if (! get_object(analyzer, unit, index, &item)) {
						return false;
					}
					
					// スタックにObjectをpushする
					if (! push(analyzer,
							   unit,
							   &item)) {
						return false;
					}
				}
				break;

			case IALOAD:
				{
					// indexをpop
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// int 配列かをチェックする
					data_item item;
					if (! pop_object(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_INT_ARRAY(&item)) {
						throw_verify_error(analyzer,
										   "Must be int or boolean array class");
						return false;
					}
					// intをpushする
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case LALOAD:
				{
					// indexをpop
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// long 配列かをチェックする
					data_item item;
					if (! pop_object(analyzer, unit, &item)) {
						return false;
					}
					if (! (IS_LONG_ARRAY(&item))) {
						throw_verify_error(analyzer,
										   "Must be long array class");
						return false;
					}

					// longをpushする
					if (! push_long( analyzer, unit)) {
						return false;
					}
				}
				break;

			case FALOAD:
				{
					// indexをpop
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// float 配列かをチェックする
					data_item item;
					if (! pop_object(analyzer, unit, &item)) {
						return false;
					}
					if (! (IS_FLOAT_ARRAY(&item))) {
						throw_verify_error(analyzer,
										   "Must be float array class");
						return false;
					}

					// floatをpushする
					if (! push_float( analyzer, unit)) {
						return false;
					}
				}
				break;

			case DALOAD:
				{
					// indexをpop
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// double 配列かをチェックする
					data_item item;
					if (! pop_object(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_DOUBLE_ARRAY(&item)) {
						throw_verify_error(analyzer,
										   "Must be double array class");
						return false;
					}

					// doubleをpushする
					if (! push_double( analyzer, unit)) {
						return false;
					}
				}
				break;

			case AALOAD:
				{
					// indexをpop
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					// Object 配列かをチェックする
					if (! is_object_array(analyzer, &item)) {
						return false;
					}

					// 配列構成要素をpushする
					switch (item.type) {
					case ITEM_Object:
						{
							const java_utf8* class_name = item.class_name;
							int len = strlen(class_name);
							const java_utf8* element_name
								= get_component_of_object_array(class_name);
							if (! push_object(
											  analyzer,
											  unit,
											  element_name)) {
								return false;
							}
						}
						break;

					case ITEM_MergedObjects:
						{
							// ToDo: 配列内のクラス名を再度マージする
							assert(false);
						}
						break;

					default:
						throw_verify_error(analyzer, "Must be an array");
						return false;
					}
				}
				break;

			case BALOAD:
				{
					// indexをpop
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// byte or boolean 配列かをチェックする
					data_item item;
					if (! pop_object(analyzer, unit, &item)) {
						return false;
					}
					if (! (IS_BYTE_ARRAY(&item) || IS_BOOLEAN_ARRAY(&item))) {
						throw_verify_error(analyzer,
										   "Must be byte array class");
						return false;
					}

					// intをpushする
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case CALOAD:
				{
					// indexをpop
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// char 配列かをチェックする
					data_item item;
					if (! pop_object(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_CHAR_ARRAY(&item)) {
						throw_verify_error(analyzer,
										   "Must be double array class");
						return false;
					}

					// intをpushする
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case SALOAD:
				{
					// indexをpop
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// short 配列かをチェックする
					data_item item;
					if (! pop_object(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_SHORT_ARRAY(&item)) {
						throw_verify_error(analyzer,
										   "Must be short array class");
						return false;
					}

					// intをpushする
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case ISTORE:
				{
					// intを pop する
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					// ローカル変数に入れる
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}
					data_item item;
					item.type = ITEM_Integer;
					set_local(analyzer, unit, index, &item);

					local_variable_changed = true;
				}
				break;

			case LSTORE:
				{
					// longを pop する
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					// ローカル変数に入れる
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}
					data_item item;
					item.type = ITEM_Long;
					set_local(analyzer, unit, index, &item);

					local_variable_changed = true;
				}
				break;

			case FSTORE:
				{
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					// ローカル変数に入れる
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}
					data_item item;
					item.type = ITEM_Float;
					set_local(analyzer, unit, index, &item);

					local_variable_changed = true;
				}
				break;

			case DSTORE:
				{
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					// ローカル変数に入れる
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}
					data_item item;
					item.type = ITEM_Double;
					set_local(analyzer, unit, index, &item);

					local_variable_changed = true;
				}
				break;

			case ASTORE:
				{
					data_item item;
					if (! pop_object(analyzer, unit, &item)) {
						return false;
					}
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
					} else {
						index = code[current_pc + 1];
					}
					if (! set_local(analyzer, unit, index, &item)) {
						return false;
					}

					local_variable_changed = true;
				}
				break;

			case ISTORE_0:
			case ISTORE_1:
			case ISTORE_2:
			case ISTORE_3:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					// ローカル変数に入れる
					u2 index = inst - ISTORE_0;
					data_item item;
					item.type = ITEM_Integer;
					if (! set_local(analyzer, unit, index, &item)) {
						return false;
					}

					local_variable_changed = true;
				}
				break;

			case LSTORE_0:
			case LSTORE_1:
			case LSTORE_2:
			case LSTORE_3:
				{
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					// ローカル変数に入れる
					u2 index = inst - LSTORE_0;
					data_item item;
					item.type = ITEM_Long;
					if (! set_local(analyzer, unit, index, &item)) {
						return false;
					}

					local_variable_changed = true;
				}
				break;

			case FSTORE_0:
			case FSTORE_1:
			case FSTORE_2:
			case FSTORE_3:
				{
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					// ローカル変数に入れる
					u2 index = inst - FSTORE_0;
					data_item item;
					item.type = ITEM_Float;
					if (! set_local(analyzer, unit, index, &item)) {
						return false;
					}

					local_variable_changed = true;
				}
				break;

			case DSTORE_0:
			case DSTORE_1:
			case DSTORE_2:
			case DSTORE_3:
				{
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					// ローカル変数に入れる
					u2 index = inst - DSTORE_0;
					data_item item;
					item.type = ITEM_Double;
					if (! set_local(analyzer, unit, index, &item)) {
						return false;
					}

					local_variable_changed = true;
				}
				break;

			case ASTORE_0:
			case ASTORE_1:
			case ASTORE_2:
			case ASTORE_3:
				{
					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					// オブジェクトまたはreturnAddress型かをチェックする
					if (! (IS_OBJECT(&item) || IS_RETURN_ADDRESS(&item))) {
						throw_verify_error(analyzer, "Must be Object");
						return false;
					}

					// ローカル変数に入れる
					u2 index = inst - ASTORE_0;
					if (! set_local(analyzer, unit, index, &item)) {
						return false;
					}

					local_variable_changed = true;
				}
				break;

			case IASTORE:
				{
					// valueをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					// indexをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// int配列かどうかをチェックする
					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_INT_ARRAY(&item) || IS_BOOLEAN_ARRAY(&item)) {
						throw_verify_error(analyzer, "Must be int array or boolean array");
						return false;
					}
				}
				break;

			case LASTORE:
				{
					// valueをpopする
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					// indexをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// long配列かどうかをチェックする
					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_LONG_ARRAY(&item)) {
						throw_verify_error(analyzer, "Must be long array class");
						return false;
					}
				}
				break;

			case FASTORE:
				{
					// valueをpopする
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					// indexをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// float配列かどうかをチェックする
					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_FLOAT_ARRAY(&item)) {
						throw_verify_error(analyzer, "Must be float array class");
						return false;
					}
				}
				break;

			case DASTORE:
				{
					// valueをpopする
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					// indexをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// double配列かどうかをチェックする
					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_DOUBLE_ARRAY(&item)) {
						throw_verify_error(analyzer, "Must be double array class");
						return false;
					}
				}
				break;

			case AASTORE:
				{
					// valueをpopする
					data_item item;
					if (! pop_object(analyzer, unit, &item)) {
						return false;
					}
					// indexをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// Objectの配列かどうかをチェックする
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					if (! is_object_array(analyzer, &item)) {
						return false;
					}
				}
				break;

			case BASTORE:
				{
					// valueをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					// indexをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// boolean または byte配列かどうかをチェックする
					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					if (! (IS_BYTE_ARRAY(&item) || IS_BOOLEAN_ARRAY(&item))) {
						throw_verify_error(analyzer, "Must be boolean array or byte array class");
						return false;
					}
				}
				break;

			case CASTORE:
				{
					// valueをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					// indexをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// char配列かどうかをチェックする
					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_CHAR_ARRAY(&item)) {
						throw_verify_error(analyzer, "Must be char array class");
						return false;
					}
				}
				break;

			case SASTORE:
				{
					// valueをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					// indexをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// short配列かどうかをチェックする
					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					if (! IS_SHORT_ARRAY(&item)) {
						throw_verify_error(analyzer, "Must be short array class");
						return false;
					}
				}
				break;

			case POP:
				{
					if (! pop(analyzer, unit)) {
						return false;
					}
				}
				break;

			case POP2:
				{
					if (! pop2(analyzer, unit)) {
						return false;
					}
				}
				break;

			case DUP:
				{
					data_item item;
					if (! pop(analyzer, unit, &item)) {
						return false;
					}
					if (IS_CATEGORY2_TYPE(&item) || IS_CATEGORY2_TYPE(&item)) {
						// long, doubleを取り出そうとした
						throw_verify_error(analyzer, "Illegal dup");
						return false;
					}
					if (! push( analyzer, unit, &item)) {
						return false;
					}
					if (! push( analyzer, unit, &item)) {
						return false;
					}
				}
				break;

			case DUP_X1:
				{
					data_item value1;
					if (! pop(analyzer, unit, &value1)) {
						return false;
					}
					data_item value2;
					if (! pop(analyzer, unit, &value2)) {
						return false;
					}
					if (IS_CATEGORY2_TYPE(&value1) || IS_CATEGORY2_TYPE(&value2)) {
						// long, doubleを取り出そうとした
						throw_verify_error(analyzer, "dup_x1 cannot handle long or double");
						return false;
					}
					if (! push( analyzer, unit, &value1)) {
						return false;
					}
					if (! push( analyzer, unit, &value2)) {
						return false;
					}
					if (! push( analyzer, unit, &value1)) {
						return false;
					}
				}
				break;

			case DUP_X2:
				{
					data_item value1;
					if (! pop(analyzer, unit, &value1)) {
						return false;
					}
					if (IS_CATEGORY2_TYPE(&value1)) {
						throw_verify_error(analyzer, "Illegal stack state in dup_x2 instruction");
						return false;
					}
					data_item value2;
					if (! pop(analyzer, unit, &value2)) {
						return false;
					}
					// value2がカテゴリ１か２かで、その後の動作が異なる
					if (IS_CATEGORY2_TYPE(&value2)) {
						// value1, value2, value1 の順にpushする
						bool result1 = push( analyzer, unit, &value1);
						bool result2 = push( analyzer, unit, &value2);
						bool result3 = push( analyzer, unit, &value1);
						if (! (result1 && result2 && result3)) {
							return false;
						}
					} else {
						// もう１つpopする
						data_item value3;
						if (! pop(analyzer, unit, &value3)) {
							return false;
						}
						if (IS_CATEGORY2_TYPE(&value3)) {
							throw_verify_error(analyzer, "Illegal stack state in dup_x2");
							return false;
						}
						// value1, value3, value2, value1 の順にpushする
						bool result1 = push( analyzer, unit, &value1);
						bool result2 = push( analyzer, unit, &value3);
						bool result3 = push( analyzer, unit, &value2);
						bool result4 = push( analyzer, unit, &value1);
						if (! (result1 && result2 && result3 && result4)) {
							return false;
						}
					}
				}
				break;

			case DUP2:
				{
					data_item value1;
					if (! pop(analyzer, unit, &value1)) {
						return false;
					}
					if (IS_CATEGORY2_TYPE(&value1)) {
						// カテゴリ２
						bool result1 = push( analyzer, unit, &value1);
						bool result2 = push( analyzer, unit, &value1);
						if (! (result1 && result2)) {
							return false;
						}
					} else {
						// カテゴリ１
						// もう１つpopする
						data_item value2;
						if (! pop(analyzer, unit, &value2)) {
							return false;
						}
						if (IS_CATEGORY2_TYPE(&value2)) {
							throw_verify_error(analyzer, "Illegal stack state in dup2");
							return false;
						}
						// value2, value1, value2, value1 の順にpushする
						bool result1 = push( analyzer, unit, &value2);
						bool result2 = push( analyzer, unit, &value1);
						bool result3 = push( analyzer, unit, &value2);
						bool result4 = push( analyzer, unit, &value1);
						if (! (result1 && result2 && result3 && result4)) {
							return false;
						}
					}
				}
				break;

			case DUP2_X1:
				{
					data_item value1;
					if (! pop(analyzer, unit, &value1)) {
						return false;
					}
					if (IS_CATEGORY2_TYPE(&value1)) {
						// form2
						data_item value2;
						if (! pop(analyzer, unit, &value2)) {
							return false;
						}
						if (IS_CATEGORY2_TYPE(&value2)) {
							// 不正なデータタイプ
							throw_verify_error(analyzer, "Illegal stack state in dup2_x1");
							return false;
						}
						// value1, value2, value1 の順番でpushする
						bool result1 = push( analyzer, unit, &value1);
						bool result2 = push( analyzer, unit, &value2);
						bool result3 = push( analyzer, unit, &value1);
						if (! (result1 && result2 && result3)) {
							return false;
						}
					} else {
						// form1
						data_item value2;
						if (! pop(analyzer, unit, &value2)) {
							return false;
						}
						data_item value3;
						if (! pop(analyzer, unit, &value3)) {
							return false;
						}
						if (IS_CATEGORY2_TYPE(&value2) || IS_CATEGORY2_TYPE(&value3)) {
							throw_verify_error(analyzer, "Illegal stack state in dup2_x1");
							return false;
						}

						// value2, value1, value3, value2, value1 の順にpushする
						bool result1 = push( analyzer, unit, &value2);
						bool result2 = push( analyzer, unit, &value1);
						bool result3 = push( analyzer, unit, &value3);
						bool result4 = push( analyzer, unit, &value2);
						bool result5 = push( analyzer, unit, &value1);
						if (! (result1 && result2 && result3 && result4 && result5)) {
							return false;
						}
					}
				}
				break;

			case DUP2_X2:
				{
					data_item value1;
					if (! pop(analyzer, unit, &value1)) {
						return false;
					}
					data_item value2;
					if (! pop(analyzer, unit, &value2)) {
						return false;
					}

					if (IS_CATEGORY2_TYPE(&value1)) {
						// form 2 または form 4
						if (IS_CATEGORY2_TYPE(&value2)) {
							// form 4
							// value1, value2, value1 の順にpushする
							bool result1 = push( analyzer, unit, &value1);
							bool result2 = push( analyzer, unit, &value2);
							bool result3 = push( analyzer, unit, &value1);
							if (! (result1 && result2 && result3)) {
								return false;
							}
						} else {
							// form2
							data_item value3;
							if (! pop(analyzer, unit, &value3)) {
								return false;
							}
							if (IS_CATEGORY2_TYPE(&value3)) {
								throw_verify_error(analyzer, "Illegal stack state in dup2_x2");
								return false;
							}
							// value1, value3, value2, value1 の順にpushする
							bool result1 = push( analyzer, unit, &value1);
							bool result2 = push( analyzer, unit, &value3);
							bool result3 = push( analyzer, unit, &value2);
							bool result4 = push( analyzer, unit, &value1);
							if (! (result1 && result2 && result3 && result4)) {
								return false;
							}
						}
					} else {
						// form 1 または form 3
						if (IS_CATEGORY2_TYPE(&value2)) {
							throw_verify_error(analyzer, "Illegal stack state in dup2_x2");
							return false;
						}
						data_item value3;
						if (! pop(analyzer, unit, &value3)) {
							return false;
						}
						if (IS_CATEGORY2_TYPE(&value3)) {
							// form 3
							// value2, value1, value3, value2, value1 の順にpushする
							bool result1 = push( analyzer, unit, &value2);
							bool result2 = push( analyzer, unit, &value1);
							bool result3 = push( analyzer, unit, &value3);
							bool result4 = push( analyzer, unit, &value2);
							bool result5 = push( analyzer, unit, &value1);
							if (! (result1 && result2 && result3 && result4 && result5)) {
								return false;
							}
						} else {
							// form 1
							data_item value4;
							if (! pop(analyzer, unit, &value4)) {
								return false;
							}
							if (IS_CATEGORY2_TYPE(&value4)) {
								throw_verify_error(analyzer, "Illegal stack state in dup2_x2");
								return false;
							}
							// value2, value1, value4, value3, value2, value1 の順にpushする
							bool result1 = push( analyzer, unit, &value2);
							bool result2 = push( analyzer, unit, &value1);
							bool result3 = push( analyzer, unit, &value4);
							bool result4 = push( analyzer, unit, &value3);
							bool result5 = push( analyzer, unit, &value2);
							bool result6 = push( analyzer, unit, &value1);
							if (! (result1 && result2 && result3 && result4 && result5 && result6)) {
								return false;
							}

						}
					}

				}
				break;

			case SWAP:
				{
					data_item value1;
					if (! pop(analyzer, unit, &value1)) {
						return false;
					}
					data_item value2;
					if (! pop(analyzer, unit, &value2)) {
						return false;
					}

					if (IS_CATEGORY2_TYPE(&value1) || IS_CATEGORY2_TYPE(&value2)) {
						throw_verify_error(analyzer, "Illegal stack state in swap");
						return false;
					}
					if (! push( analyzer, unit, &value1)) {
						return false;
					}
					if (! push( analyzer, unit, &value2)) {
						return false;
					}
				}
				break;

			case IADD:
			case ISUB:
			case IMUL:
			case IDIV:
			case IREM:
			case IAND:
			case IOR:
			case IXOR:
			case ISHL:
			case ISHR:
			case IUSHR:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case LADD:
			case LSUB:
			case LMUL:
			case LDIV:
			case LREM:
			case LAND:
			case LOR:
			case LXOR:
				{
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					if (! push_long( analyzer, unit)) {
						return false;
					}
				}
				break;

			case LSHL:
			case LSHR:
			case LUSHR:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					if (! push_long( analyzer, unit)) {
						return false;
					}
				}
				break;

			case FADD:
			case FSUB:
			case FMUL:
			case FDIV:
			case FREM:
				{
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					if (! push_float( analyzer, unit)) {
						return false;
					}
				}
				break;

			case DADD:
			case DSUB:
			case DMUL:
			case DDIV:
			case DREM:
				{
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					if (! push_double( analyzer, unit)) {
						return false;
					}
				}
				break;

			case INEG:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					if (! push_int(analyzer, unit)) {
						return false;
					}
				}
				break;

			case LNEG:
				{
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					if (! push_long(analyzer, unit)) {
						return false;
					}
				}
				break;

			case FNEG:
				{
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					if (! push_float(analyzer, unit)) {
						return false;
					}
				}
				break;

			case DNEG:
				{
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					if (! push_double(analyzer, unit)) {
						return false;
					}
				}
				break;

			case IINC:
				{
					u2 index;
					if (wide) {
						index = MAKE_U2(code[current_pc + 1],
										code[current_pc + 2]);
						wide = false;
						inst_len = 5;
					} else {
						index = code[current_pc + 1];
						inst_len = 3;
					}
				}
				break;

			case I2L:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					if (! push_long( analyzer, unit)) {
						return false;
					}
				}
				break;

			case I2F:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					if (! push_float( analyzer, unit)) {
						return false;
					}
				}
				break;

			case I2B:
			case I2C:
			case I2S:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case I2D:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					if (! push_double( analyzer, unit)) {
						return false;
					}
				}
				break;

			case L2I:
				{
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case L2F:
				{
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					if (! push_float( analyzer, unit)) {
						return false;
					}
				}
				break;

			case L2D:
				{
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					if (! push_double( analyzer, unit)) {
						return false;
					}
				}
				break;

			case F2I:
				{
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case F2L:
				{
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					if (! push_long( analyzer, unit)) {
						return false;
					}
				}
				break;

			case F2D:
				{
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					if (! push_double(analyzer, unit)) {
						return false;
					}
				}
				break;

			case D2I:
				{
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					if (! push_int(analyzer, unit)) {
						return false;
					}
				}
				break;

			case D2F:
				{
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					if (! push_float(analyzer, unit)) {
						return false;
					}
				}
				break;

			case D2L:
				{
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					if (! push_long( analyzer, unit)) {
						return false;
					}
				}
				break;

			case LCMP:
				{
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					if (! pop_long(analyzer, unit)) {
						return false;
					}
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case FCMPL:
			case FCMPG:
				{
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					if (! pop_float(analyzer, unit)) {
						return false;
					}
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case DCMPL:
			case DCMPG:
				{
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					if (! pop_double(analyzer, unit)) {
						return false;
					}
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case IFEQ:
			case IFNE:
			case IFLT:
			case IFGE:
			case IFGT:
			case IFLE:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// ジャンプ先のデータモデルマージ
					__int16 offset = MAKE_INT16(code[current_pc + 1],
												code[current_pc + 2]);
					if (! merge(analyzer, unit, current_pc, current_pc + offset)) {
						return false;
					}
				}
				break;

			case IF_ICMPEQ:
			case IF_ICMPNE:
			case IF_ICMPLT:
			case IF_ICMPGE:
			case IF_ICMPGT:
			case IF_ICMPLE:
				{
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// ジャンプ先のデータモデルをマージする
					__int16 branchoffset = MAKE_INT16(code[current_pc + 1],
													  code[current_pc + 2]);
					if (! merge(analyzer, unit, current_pc, current_pc + branchoffset)) {
						return false;
					}
				}
				break;

			case IF_ACMPEQ:
			case IF_ACMPNE:
				{
					if (! pop_object(analyzer, unit)) {
						return false;
					}
					if (! pop_object(analyzer, unit)) {
						return false;
					}
					// ジャンプ先のデータモデルをマージする
					__int16 branchoffset = MAKE_INT16(code[current_pc + 1],
													  code[current_pc + 2]);
					if (! merge(analyzer, unit, current_pc, current_pc + branchoffset)) {
						return false;
					}
				}
				break;

			case IFNULL:
			case IFNONNULL:
				{
					if (! pop_object(analyzer, unit)) {
						return false;
					}
					// ジャンプ先のデータモデルをマージする
					__int16 branchoffset = MAKE_INT16(code[current_pc + 1],
													  code[current_pc + 2]);
					if (! merge(analyzer, unit, current_pc, current_pc + branchoffset)) {
						return false;
					}
				}
				break;

			case GOTO:
				// 無条件分岐命令
				{
					// ジャンプ先アドレスを計算する
					__int16 branchoffset
						= ((__int16) code[current_pc + 1]) << 8 | (code[current_pc + 2] & 0xff);

					if (! merge(analyzer, unit, current_pc, (u2) (current_pc + branchoffset))) {
						return false;
					}

					// すでにマージ済み
					already_merged = true;
				}
				break;

			case GOTO_W:
				// 無条件分岐命令
				{
					// ジャンプ先アドレスを計算する
					__int32 branchoffset
						= MAKE_INT32(code[current_pc + 1],
									 code[current_pc + 2],
									 code[current_pc + 3],
									 code[current_pc + 4]);

					if (! merge(analyzer, unit, current_pc, (u2) (current_pc + branchoffset))) {
						return false;
					}

					// すでにマージ済み
					already_merged = true;
				}
				break;

			case JSR:
				{
					data_item item;
					item.type = ITEM_ReturnAddress;
					push( analyzer, unit, &item);
					
					// データモデルのマージ
					__int16 branchoffset = MAKE_INT16(code[current_pc + 1],
													  code[current_pc + 2]);
					// JSR命令
					if (! merge(analyzer, unit, current_pc, current_pc + branchoffset)) {
						return false;
					}

					// マージ済み
					already_merged = true;
				}
				break;

			case JSR_W:
				{
					data_item item;
					item.type = ITEM_ReturnAddress;
					push( analyzer, unit, &item);

					// データモデルのマージ
					__int32 branchoffset = MAKE_INT32(code[current_pc + 1],
													  code[current_pc + 2],
													  code[current_pc + 3],
													  code[current_pc + 4]);
					if (! merge(analyzer, unit, current_pc, (u2) (current_pc + branchoffset))) {
						return false;
					}
					// マージ済み
					already_merged = true;
				}
				break;

			case RET:
				{
					// ToDo: jsrの次の命令について、以下の特殊なマージ処理を行う
					// 1. ローカル変数は、「ローカル変数変更フラグ」がついているもののみマージする
					//    初回マージ時には、jsr/jsr_w命令実行ユニットのローカル変数と
					//    マージする。
					// 2. オペランドスタックをマージする。

					already_merged = true;
				}
				break;

			case TABLESWITCH:
				{
					// パラメタをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// ジャンプしうるすべての命令について
					// マージ処理を行う
					// 4バイト境界からデータを読み込む
					const u2 startpos = (u2) ALIGN_UP4(current_pc);
					const __int32* data = (__int32*) &code[startpos];

					// デフォルトのジャンプ先のデータモデルをマージする
					u4 target_pc = current_pc + TO_LITTLE_ENDIAN(data[0]);
					if (! merge(analyzer, unit, current_pc, (u2) target_pc)) {
						return false;
					}

					__int32 low = TO_LITTLE_ENDIAN(data[1]);
					__int32 high = TO_LITTLE_ENDIAN(data[2]);
					for (int i = low; i <= high; ++i) {
						int index = i - low;
						target_pc = current_pc + TO_LITTLE_ENDIAN(data[3 + index]);
						// ジャンプ先のデータモデルをマージする
						if (! merge(analyzer, unit, current_pc, (u2) target_pc)) {
							return false;
						}
					}

					// tableswitchの命令長を計算する
					inst_len = (startpos - current_pc)
						  + 4							// defaultbytes
						  + 4							// low
						  + 4							// high
						  + ((high - low + 1) * 4);		// jump offsets...

					// データモデルのマージ処理は完了している
					already_merged = true;
				}
				break;

			case LOOKUPSWITCH:
				{
					// パラメタをpopする
					if (! pop_int(analyzer, unit)) {
						return false;
					}

					// 4バイト境界からデータを読み込む
					const u2 startpos = (u2) ALIGN_UP4(current_pc);
					const __int32* data = (__int32*) &code[startpos];
					__int32 npairs = data[1];
					// little-endianにする
					npairs = TO_LITTLE_ENDIAN(npairs);

					// ジャンプ先のpcを計算し、マージ処理を行う
					__int32 defaultoffset = TO_LITTLE_ENDIAN(data[0]);
					u4 target_pc = current_pc + defaultoffset;
					if (! merge(analyzer, unit, current_pc, (u2) target_pc)) {
						return false;
					}

					// すべてのジャンプ先へマージ処理を行う
					for (__int32 i = 2; i < npairs + 2; i += 2) {
						target_pc = current_pc + TO_LITTLE_ENDIAN(data[i + 1]);
						if (! merge(analyzer, unit, current_pc, (u2) target_pc)) {
							return false;
						}
					}

					// 命令長を決定する
					inst_len = (startpos - current_pc)
						+ 4					// defaultbytes
						+ 4					// npairs
						+ (npairs * 8);		// match-offset

					// データモデルのマージ処理は完了している
					already_merged = true;
				}
				break;

			case IRETURN:
			case LRETURN:
			case FRETURN:
			case DRETURN:
			case ARETURN:
			case RETURN:
				already_merged = true;
				break;

			case GETSTATIC:
				{
					// CONSTANT_Fieldref_infoを取得する
					u2 index = read_u2(&code[current_pc + 1]);
					CONSTANT_Fieldref_info* fieldref
						= get_CONSTANT_Fieldref_info(analyzer->cfile, index);

					// フィールドの型を取得
					data_item item;
					if (! get_field_type(analyzer,
										 fieldref->field_descriptor,
										 &item)) {
						return false;
					}

					// 型をスタックにpushする
					push( analyzer, unit, &item);
				}

				break;

			case GETFIELD:
				{
					// CONSTANT_Fieldref_infoを取得する
					u2 index = read_u2(&code[current_pc + 1]);
					CONSTANT_Fieldref_info* fieldref
						= get_CONSTANT_Fieldref_info(analyzer->cfile, index);

					// objectrefをpopする
					// ToDo:型チェック
					if (! pop_object(analyzer, unit)) {
						return false;
					}

					// フィールドの型を取得
					data_item item;
					if (! get_field_type(analyzer,
										 fieldref->field_descriptor,
										 &item)) {
						return false;
					}

					// 型をスタックにpushする
					if (! push( analyzer, unit, &item)) {
						return false;
					}
				}

				break;

			case PUTSTATIC:
				{
					// CONSTANT_Fieldref_infoを取得する
					u2 index = read_u2(&code[current_pc + 1]);
					CONSTANT_Fieldref_info* fieldref
						= get_CONSTANT_Fieldref_info(analyzer->cfile, index);

					data_item value;
					// スタックから value をpopする
					if (! pop(analyzer, unit, &value)) {
						return false;
					}

					// フィールドの型を取得
					data_item ftype;
					if (! get_field_type(analyzer,
										 fieldref->field_descriptor,
										 &ftype)) {
						return false;
					}

					// ToDo: 型チェック
				}
				break;

			case PUTFIELD:
				{
					// CONSTANT_Fieldref_infoを取得する
					u2 index = read_u2(&code[current_pc + 1]);
					CONSTANT_Fieldref_info* fieldref
						= get_CONSTANT_Fieldref_info(analyzer->cfile, index);

					// フィールドの型を取得
					data_item item;
					if (! get_field_type(analyzer,
										 fieldref->field_descriptor,
										 &item)) {
						return false;
					}

					// スタックから value をpopする
					data_item value;
					if (! pop(analyzer, unit, &value)) {
						return false;
					}
					// objectrefをpopする
					// ToDo:型チェック
					if (! pop_object(analyzer, unit)) {
						return false;
					}

				}
				break;

			case INVOKEVIRTUAL:
				{
					// CONSTANT_Methodref_infoを取得する
					u2 index = read_u2(&code[(u2) current_pc + 1]);
					CONSTANT_Methodref_info* methodref
						= get_CONSTANT_Methodref_info(analyzer->cfile, index);
					data_item params[256];
					data_item rettype;
					int parameters_count = parse_method_descriptor(analyzer,
												  methodref->method_descriptor,
												  params,
												  &rettype);
					if (parameters_count == -1) {
						return false;
					}

					if (! check_stack_underflow(analyzer, unit, parameters_count)) {
						return false;
					}

					// 型チェック
					if (! check_assignable(analyzer,
										   &unit->operand_stack[unit->operand_stack_position - parameters_count],
										   params,
										   parameters_count)) {
						return false;
					}
					
					// パラメータをスタックからpop
					unit->operand_stack_position -= parameters_count;

					// thisをpop
					// ToDo: 型チェック
					if (! pop_object(analyzer, unit)) {
						return false;
					}

					// 戻り値をpush
					if (! push( analyzer, unit, &rettype)) {
						return false;
					}
				}
				break;

			case INVOKESPECIAL:
				{
					u2 index = read_u2(&code[(u2) current_pc + 1]);
					CONSTANT_Methodref_info* methodref
						= get_CONSTANT_Methodref_info(analyzer->cfile, index);
					data_item params[256];
					data_item rettype;
					int parameters_count = parse_method_descriptor(analyzer,
												  methodref->method_descriptor,
												  params,
												  &rettype);
					if (parameters_count == -1) {
						return false;
					}

					if (! check_stack_underflow(analyzer, unit, parameters_count)) {
						return false;
					}
					// 型チェック
					if (! check_assignable(analyzer,
										   &unit->operand_stack[unit->operand_stack_position - parameters_count],
										   params,
										   parameters_count)) {
						return false;
					}
					// スタックからパラメータをpop
					unit->operand_stack_position -= parameters_count;

					// referenceをpop
					data_item this_item;
					if (! pop(analyzer, unit, &this_item)) {
						return false;
					}
					if (! IS_OBJECT(&this_item)) {
						throw_verify_error(analyzer, "Must be Object");
						return false;
					}

					// ITEM_UninitializedThisの場合
					// 自分自身または直接のスーパークラスのコンストラクタを
					// 呼び出す必要がある
					if (this_item.type == ITEM_UninitializedThis) {
						if (methodref->method_name != INIT_METHOD_NAME
							|| (methodref->class_name != analyzer->cfile->this_class_name
								&& methodref->class_name != analyzer->cfile->super_class_name)) {
							throw_verify_error(analyzer, "ITEM_UninitializedThis must call constructor");
							return false;
						} else {
							// UninitializeThisを初期化済み状態にする
							for (int i = 0; i < cattr->max_locals + unit->operand_stack_position; ++i) {
								// local_variables と operand_stack は連続している
								data_item* item = &unit->local_variables[i];
								if (item->type == ITEM_UninitializedThis) {
									item->type = ITEM_Object;
									item->class_name = analyzer->cfile->this_class_name;
								}
							}
						}
					} else if (this_item.type == ITEM_Uninitialized) {
						if (methodref->method_name != INIT_METHOD_NAME) {
							throw_verify_error(analyzer, "Uninitialized object must call <init> method");
							return false;
						} else {
							// オブジェクトの初期化
							// ITEM_UninitializedをCONSTANT_Methodref_infoで示されたクラスで
							// 置き換える
							for (int i = 0; i < cattr->max_locals + unit->operand_stack_position; ++i) {
								// local_variables と operand_stack は連続している
								data_item* item = &unit->local_variables[i];
								if (item->type == ITEM_Uninitialized && item->pc == this_item.pc) {
									item->type = ITEM_Object;
									item->class_name = methodref->class_name;
								}
							}
						}
					}

					// 戻り値をpush
					if (! push( analyzer, unit, &rettype)) {
						return false;
					}
				}
				break;

			case INVOKESTATIC:
				{
					u2 index = read_u2(&code[(u2) current_pc + 1]);
					CONSTANT_Methodref_info* methodref
						= get_CONSTANT_Methodref_info(analyzer->cfile, index);
					data_item params[256];
					data_item rettype;
					int parameters_count = parse_method_descriptor(analyzer,
												  methodref->method_descriptor,
												  params,
												  &rettype);
					if (parameters_count == -1) {
						return false;
					}

					if (! check_stack_underflow(analyzer, unit, parameters_count)) {
						return false;
					}
					// 型チェック
					if (! check_assignable(analyzer,
										   &unit->operand_stack[unit->operand_stack_position - parameters_count],
										   params,
										   parameters_count)) {
						return false;
					}
					// スタックからパラメータをpop
					unit->operand_stack_position -= parameters_count;

					// 戻り値をpush
					if (! push( analyzer, unit, &rettype)) {
						return false;
					}
				}
				break;

			case INVOKEINTERFACE:
				{
					u2 index = read_u2(&code[(u2) current_pc + 1]);
					// InterfaceMethodref情報を取得する
					// constant_pool[index]の下位16ビットに class_index が、上位16ビットに name_and_type_indexが格納されている
					u4 interface_methodref = (u4) analyzer->cfile->constant_pool[index];
					
					// NameAndType情報を取得する
					// constant_pool[name_and_type_index] の下位ビットに name_index が、
					// 上位16ビットに type_index が格納されている
					u4 name_and_type = (u4) analyzer->cfile->constant_pool[(u2) (interface_methodref >> 16)];
					java_utf8* target_method_name = get_java_utf8(analyzer->cfile, (u2) name_and_type);
					java_utf8* target_method_desc = get_java_utf8(analyzer->cfile, (u2) (name_and_type >> 16));

					data_item params[256];
					data_item rettype;
					int parameters_count = parse_method_descriptor(analyzer,
												  target_method_desc,
												  params,
												  &rettype);
					if (parameters_count == -1) {
						return false;
					}

					if (! check_stack_underflow(analyzer, unit, parameters_count)) {
						return false;
					}
					// 型チェック
					if (! check_assignable(analyzer,
										   &unit->operand_stack[unit->operand_stack_position - parameters_count],
										   params,
										   parameters_count)) {
						return false;
					}
					// スタックからパラメータをpop
					unit->operand_stack_position -= parameters_count;

					// thisをpop
					// ToDo: 型チェック
					if (! pop_object(analyzer, unit)) {
						return false;
					}

					// 戻り値をpush
					if (! push( analyzer, unit, &rettype)) {
						return false;
					}
				}
				break;

			case NEW:
				{
					const java_utf8* class_name
						= get_CONSTANT_Class_info(analyzer->cfile,
												  MAKE_U2(code[current_pc + 1], code[current_pc + 2]));

					data_item item;
					item.type = ITEM_Uninitialized;
					item.pc = current_pc;

					// 未初期化オブジェクトをpushする
					if (! push(
							   analyzer,
							   unit,
							   &item)) {
						return false;
					}
				}
				break;

			case NEWARRAY:
				{
					u1 atype = code[current_pc + 1];

					if (! pop_int(analyzer, unit)) {
						return false;
					}
					
					data_item item;
					item.type = ITEM_Object;
					switch (atype) {
					case 4:
						item.class_name = BOOLEAN_ARRAY_CLASS_NAME;
						break;
					case 5:
						item.class_name = CHAR_ARRAY_CLASS_NAME;
						break;
					case 6:
						item.class_name = FLOAT_ARRAY_CLASS_NAME;
						break;
					case 7:
						item.class_name = DOUBLE_ARRAY_CLASS_NAME;
						break;
					case 8:
						item.class_name = BYTE_ARRAY_CLASS_NAME;
						break;
					case 9:
						item.class_name = SHORT_ARRAY_CLASS_NAME;
						break;
					case 10:
						item.class_name = INT_ARRAY_CLASS_NAME;
						break;
					case 11:
						item.class_name = LONG_ARRAY_CLASS_NAME;
						break;
					default:
						throw_verify_error(analyzer, "Illegal atype");
						return false;
					}
					if (! push( analyzer, unit, &item)) {
						return false;
					}
				}
				break;

			case ANEWARRAY:
				{
					u2 index = MAKE_U2(code[current_pc + 1],
									   code[current_pc + 2]);
					const java_utf8* name = get_CONSTANT_Class_info(analyzer->cfile, index);
					
					// 要素数をpop
					if (! pop_int(analyzer, unit)) {
						return false;
					}
					char* array_name = NULL;
					
					u4 name_len = strlen(name);
					if (name_len >= 2 && name[0] == '[') {
						// 要素が配列クラスの場合（例 [I [Ljava/lang/String; )
						array_name = (char*) malloc(name_len + 1 + 1);	// 先頭に'['を追加する
						if (array_name == NULL) {
							fatal_error(FATAL_ERROR_NO_MEMORY);
						}
						array_name[0] = '[';
						strcpy(&array_name[1], name);
					} else {
						// 要素がクラスの場合
						// "[L" + クラス名 + ";"
						array_name = (char *) malloc(name_len + 2 + 1 + 1);
						if (array_name == NULL) {
							fatal_error(FATAL_ERROR_NO_MEMORY);
						}
						array_name[0] = '[';
						array_name[1] = 'L';
						strcpy(&array_name[2], name);
						strcat(array_name, ";");
					}
					data_item item;
					item.type = ITEM_Object;
					item.class_name = intern_utf8(array_name);
					free(array_name);
					if (! push( analyzer, unit, &item)) {
						return false;
					}
				}
				break;

			case ARRAYLENGTH:
				{
					// ToDo:型チェック
					if (! pop_object(analyzer, unit)) {
						return false;
					}
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;

			case ATHROW:
				{
					// ToDo:型チェック
					already_merged = true;
				}
				break;

			case CHECKCAST:
				{
					u2 index = MAKE_U2(code[current_pc + 1],
									   code[current_pc + 2]);
					const java_utf8* cname = get_CONSTANT_Class_info(analyzer->cfile, index);
					
					// 引数をpopした後、対応するクラス名で上書きする
					data_item item;
					if (! pop(analyzer,
							  unit,
							  &item)) {
						return false;
					}
					if (! IS_OBJECT(&item)) {
						throw_verify_error(analyzer, "Must be Object");
						return false;
					}
					item.type = ITEM_Object;
					item.class_name = cname;
					if (! push(
							   analyzer,
							   unit,
							   &item)) {
						return false;
					}
					DBG(_T("checkcast "));
					DBG_UTF8(cname);
					DBG(_T("\n"));
				}
				break;

			case INSTANCEOF:
				{
					// ToDo:シンボル参照のチェック
					if (! pop_object(analyzer, unit)) {
						return false;
					}
					if (! push_int( analyzer, unit)) {
						return false;
					}
				}
				break;
			
			case MONITORENTER:
			case MONITOREXIT:
				{
					if (! pop_object(analyzer, unit)) {
						return false;
					}
				}
				break;

			case WIDE:
				{
					wide = true;
					inst_len = 1;
				}
				break;

			case MULTIANEWARRAY:
				{
					// indexを取り出す
					u2 index = MAKE_U2(code[current_pc + 1],
									   code[current_pc + 2]);
					const java_utf8* class_name = get_CONSTANT_Class_info(analyzer->cfile, index);

					// 次元数を取り出す
					u1 dimensions = code[(u2) current_pc + 3];
					
					// スタックからcountを取り出す
					// ToDo:効率化
					for (u1 i = 0; i < dimensions; ++i) {
						if (! pop_int(analyzer, unit)) {
							return false;
						}
					}

					// 配列の参照をpushする
					data_item item;
					item.type = ITEM_Object;
					item.class_name = class_name;
					if (! push( analyzer, unit, &item)) {
						return false;
					}
				}
				break;

			case BREAKPOINT:
				{
					// ベリファイ対象のコードに登場してはいけない
					throw_verify_error(analyzer, "BREAKPOINT");
					return false;
				}
				break;

			default:
				throw_verify_error(analyzer, "Unknown opcode");
				return false;
			}
			
			// 例外ハンドラへのマージ処理を行う
			if (analyzer->code_flags[current_pc] & CF_START_OF_PROTECTED_CODE
					|| local_variable_changed) {
				for (u2 i = 0; i < cattr->exception_table_length; ++i) {
					exception_handler_info* einfo = cattr->exception_table + i;
					if (einfo->start_pc <= current_pc && current_pc < einfo->end_pc) {
						if (! merge(analyzer, unit, current_pc, einfo->handler_pc)) {
							return false;
						}
					}
				}
			}
			
			DBG(_T("current_pc="));
			DBG_INT(current_pc);
			DBG(_T(" unit->operand_stack_position="));
			DBG_INT(unit->operand_stack_position);
			DBG(_T("\n"));

			assert(inst_len);
			current_pc += inst_len;
		}

		if (! already_merged) {
			// データモデルのマージ処理を行う
			u2 end_pc = unit->end_pc;
			u1 end_inst = analyzer->minfo->code_attribute->code[end_pc];
			if (! merge(analyzer,
						unit,
						unit->end_pc,
						unit->end_pc + g_instruction_length[end_inst])) {
				return false;
			}
		}
	
	} else if (target_unit->ret_list_changed) {
		target_unit->ret_list_changed = 0;
		// ToDo: retリストのマージを行う
	}

	return true;
}


/**
 * データフローアナライザを作成する
 */
static data_flow_analyzer*
create_data_flow_analyzer(frame* current_frame,
						  ClassFile* cfile,
						  method_info* minfo) {
	Code_attribute* cattr = minfo->code_attribute;
	// データフローアナライザが使用する構造体を初期化していく
	data_flow_analyzer* analyzer
		= (data_flow_analyzer*) calloc(sizeof(data_flow_analyzer), 1);

	if (! analyzer) {
		// ベリファイ失敗
		THROW_VERIFY_ERROR(current_frame, "Not enough memory");
		return NULL;
	}
	analyzer->current_frame = current_frame;
	analyzer->cfile = cfile;
	analyzer->minfo = minfo;
	
	data_item param_types[256];
	data_item return_type;

	int parameters_count = parse_method_descriptor(analyzer,
												   minfo->descriptor,
												   param_types,
												   &return_type);
	if (parameters_count == -1) {
		throw_verify_error(analyzer, "Invalid method descriptor");
		delete_data_flow_analyzer(analyzer);
	}

	// 各命令ごとに区切る
	if (! split_instructions(analyzer, &return_type)) {
		delete_data_flow_analyzer(analyzer);
		return NULL;
	}

	// 先頭のユニットにメモリを割り当てる
	if (! alloc_verified_unit(analyzer,
							  &analyzer->verified_units[0],
							  cattr->max_locals,
							  0)) {
		return false;
	}
	// 先頭の変更フラグを立てる
	verified_unit* head_unit = &analyzer->verified_units[0];
	head_unit->changed = 1;
	head_unit->copied = 1;

	// メソッドの引数をローカル変数に設定する
	int this_count = is_static(minfo->access_flags) ? 0 : 1;
	if (minfo->code_attribute->max_locals < (this_count + parameters_count)) {
		throw_verify_error(analyzer, "Invalid max_locals");
		delete_data_flow_analyzer(analyzer);
		return NULL;
	}
	if (this_count) {
		// 0番目にthisを入れる
		data_item this_item;
		if (minfo->name == INIT_METHOD_NAME && return_type.type == ITEM_Invalid) {
			this_item.type = ITEM_UninitializedThis;
		} else {
			this_item.type = ITEM_Object;
			this_item.class_name = cfile->this_class_name;
		}
		head_unit->local_variables[0] = this_item;
	}
	// パラメータをコピーする
	memcpy(&head_unit->local_variables[this_count],
		   param_types,
		   sizeof(data_item) * parameters_count);

	// 作業用のユニットを割り当てる
	if (! alloc_verified_unit(analyzer,
							  &analyzer->work_unit,
							  cattr->max_locals,
							  cattr->max_stack)) {
		return false;
	}
	
	return analyzer;
}

/**
 * data_flow_analyzerに割り当てたメモリをすべて開放する
 */
static bool delete_data_flow_analyzer(data_flow_analyzer* analyzer) {
	free_verified_units(analyzer);
	delete_merged_class_names(analyzer);
	free(analyzer->verified_units);
	free((void*) analyzer->code_flags);
	free(analyzer);
	return true;
}

/**
 * スタックに、指定された型をpushする。
 */
static bool push(data_flow_analyzer* analyzer,
				 verified_unit* unit,
				 data_item* item) {

	if (item->type == ITEM_Invalid) {
		return true;
	}

	data_item* stack = unit->operand_stack;
	u2 operand_stack_position = unit->operand_stack_position;
	const u2 max_stack = analyzer->minfo->code_attribute->max_stack;
	switch (item->type) {
	case ITEM_Double:
	case ITEM_Long:
		{
			if (! check_stack_overflow(analyzer, unit, max_stack, 2)) {
				return false;
			}
			stack[operand_stack_position] = *item;
			stack[operand_stack_position + 1].type = ITEM_Invalid;
			operand_stack_position += 2;
		}
		break;
	
	default:
		{
			if (! check_stack_overflow(analyzer, unit, max_stack, 1)) {
				return false;
			}
			stack[operand_stack_position] = *item;
			operand_stack_position++;
		}
		break;
	}
	unit->operand_stack_position = operand_stack_position;
	return true;
}

/**
 * 整数（カテゴリ１）をPUSHする
 */
static bool push_int(data_flow_analyzer* analyzer,
					 verified_unit* unit) {
	data_item item;
	item.type = ITEM_Integer;
	return push(analyzer, unit, &item);
}

/**
 * floatをPUSHする
 */
static bool push_float(data_flow_analyzer* analyzer,
					   verified_unit* unit) {
	data_item item;
	item.type = ITEM_Float;
	return push(analyzer, unit, &item);
}

/**
 * longをPUSHする
 */
static bool push_long(data_flow_analyzer* analyzer,
					  verified_unit* unit) {
	data_item item;
	item.type = ITEM_Long;
	return push(analyzer, unit, &item);
}

/**
 * doubleをPUSHする
 */
static bool push_double(data_flow_analyzer* analyzer, 
						verified_unit* unit) {
	data_item item;
	item.type = ITEM_Double;
	return push(analyzer, unit, &item);
}

/**
 * ObjectをPUSHする
 */
static bool push_object(data_flow_analyzer* analyzer, 
						verified_unit* unit,
						const java_utf8* class_name) {
	data_item item;
	item.type = ITEM_Object;
	item.class_name = class_name;
	return push( analyzer, unit, &item);
}


/**
 * スタックトップの要素をpopする。
 */
static bool pop(data_flow_analyzer* analyzer, verified_unit* unit, data_item* item) {
	// １つ要素をpopする
	if (! check_stack_underflow(analyzer, unit, 1)) {
		return false;
	}
	u2 stack_position = unit->operand_stack_position - 1;
	*item = unit->operand_stack[stack_position];
	if (item->type == ITEM_Invalid) {
		// longまたはdoubleのはずなので、もう１つ要素をpopする
		if (! check_stack_underflow(analyzer, unit, 1)) {
			return false;
		}
		stack_position--;
		*item = unit->operand_stack[stack_position];
		if (item->type != ITEM_Long && item->type != ITEM_Double) {
			throw_verify_error(analyzer, "Invalid stack state");
			return false;
		}
	}
	unit->operand_stack_position = stack_position;
	return true;
}

/**
 * intをpopする
 */
static bool pop_int(data_flow_analyzer* analyzer,
					verified_unit* unit) {

	data_item item;
	if (! pop(analyzer, unit, &item)) {
		return false;
	}
	if (item.type != ITEM_Integer) {
		throw_verify_error(analyzer, "Must be integer type");
		return false;
	}
	return true;
}

/**
 * floatをpopする
 */
static bool pop_float(data_flow_analyzer* analyzer,
					verified_unit* unit) {
	data_item item;
	if (! pop(analyzer, unit, &item)) {
		return false;
	}
	if (item.type != ITEM_Float) {
		throw_verify_error(analyzer, "Must be float type");
		return false;
	}
	return true;
}

/**
 * longをpopする
 */
static bool pop_long(data_flow_analyzer* analyzer,
					verified_unit* unit) {
	data_item item;
	if (! pop(analyzer, unit, &item)) {
		return false;
	}
	if (item.type != ITEM_Long) {
		throw_verify_error(analyzer, "Must be long type");
		return false;
	}
	return true;
}

/**
 * doubleをpopする
 */
static bool pop_double(data_flow_analyzer* analyzer,
					   verified_unit* unit) {
	data_item item;
	if (! pop(analyzer, unit, &item)) {
		return false;
	}
	if (item.type != ITEM_Double) {
		throw_verify_error(analyzer, "Must be double type");
		return false;
	}
	return true;
}

/**
 * Objectをpopする
 */
static bool pop_object(data_flow_analyzer* analyzer,
					   verified_unit* unit) {
	data_item item;
	if (! pop(analyzer, unit, &item)) {
		return false;
	}
	if (! (IS_OBJECT(&item) || IS_RETURN_ADDRESS(&item))) {
		throw_verify_error(analyzer, "Must be object type");
		return false;
	}
	return true;
}

/**
 * Objectをpopする
 */
static bool pop_object(data_flow_analyzer* analyzer,
					verified_unit* unit,
					data_item* item) {

	if (! pop(analyzer, unit, item)) {
		return false;
	}
	if (IS_OBJECT(item) || IS_RETURN_ADDRESS(item)) {
		return true;
	} else {
		throw_verify_error(analyzer, "Must be object type");
		return false;
	}
}

/**
 * スタックからPOPする
 */
static bool pop(data_flow_analyzer* analyzer,
				verified_unit* unit) {
	data_item item;
	if (! pop(analyzer, unit, &item)) {
		return false;
	}
	// longまたはdouble値の場合はエラーにする
	if (item.type == ITEM_Long || item.type == ITEM_Double) {
		throw_verify_error(analyzer, "Cannot pop long or double type");
		return false;
	}
	return true;
}

/**
 * スタックから２要素popする
 */
static bool pop2(data_flow_analyzer* analyzer,
				 verified_unit* unit) {
	data_item item;
	if (! pop(analyzer, unit, &item)) {
		return false;
	}
	// longまたはdouble値以外の場合はもう１つpopする
	if (! IS_CATEGORY2_TYPE(&item)) {
		if (! pop(analyzer, unit, &item)) {
			return false;
		}
		// カテゴリ1の値は２つ連続していなければならない
		if (IS_CATEGORY2_TYPE(&item)) {
			throw_verify_error(analyzer, "Illegal stack state in pop2");
			return false;
		}
	}
	return true;
}

/**
 * 指定されたフィールドの型を取得して返す
 */
static bool get_field_type(data_flow_analyzer* analyzer,
						   const java_utf8* field_descriptor,
						   data_item* elem) {
	int len = strlen(field_descriptor);
	if (! len) {
		throw_verify_error(analyzer, "Invalid field descriptor");
		return false;
	} else if (len == 1) {
		// プリミティブ型
		switch (*field_descriptor) {
		case 'B':
		case 'Z':
		case 'C':
		case 'S':
		case 'I':
			elem->type = ITEM_Integer;
			break;
		
		case 'F':
			elem->type = ITEM_Float;
			break;

		case 'J':
			elem->type = ITEM_Long;
			break;

		case 'D':
			elem->type = ITEM_Double;
			break;

		default:
			// 不正なディスクリプタ
			throw_verify_error(analyzer, "Invalid field descriptor");
			return false;
		}
	} else {
		// ２バイト以上ある場合
		switch (*field_descriptor) {
		case 'L':
			elem->type = ITEM_Object;
			elem->class_name = intern_utf8(field_descriptor + 1, len - 2);
			break;

		case '[':
			elem->type = ITEM_Object;
			elem->class_name = intern_utf8(field_descriptor);
			break;

		default:
			// 不正なディスクリプタ
			throw_verify_error(analyzer, "Invalid field descriptor");
			return false;
		}
	}
	return true;
}

/**
 * メソッドのパラメタをパースし、data_itemに入れる
 */
static int parse_method_descriptor(data_flow_analyzer* analyzer,
								   const java_utf8* method_descriptor,
                                   data_item* params,
								   data_item* rettype) {
	// debug
	DBG(_T("parse_method_descriptor="));
	DBG_UTF8(method_descriptor);
	DBG(_T("\n"));

	int params_count = 0;
	bool param_end = false;
	while (*method_descriptor) {
		int length = get_token_length_of_descriptor(method_descriptor);
		if (length == -1) {
			return -1;
		}
		if (length == 1) {
			switch (*method_descriptor) {
			case '(':
				if (param_end) {
					return -1;
				}
				break;

			case ')':
			   param_end = true;
			   break;

			case 'V':
				if (param_end) {
					rettype->type = ITEM_Invalid;
				} else {
					return -1;
				}
				break;

			case 'Z': case 'B': case 'C': case 'S': case 'I':
				if (param_end) {
					rettype->type = ITEM_Integer;
				} else {
					params[params_count++].type = ITEM_Integer;
				}
				break;

			case 'F':
				if (param_end) {
					rettype->type = ITEM_Float;
				} else {
					params[params_count++].type = ITEM_Float;
				}
				break;

			case 'J':
				if (param_end) {
					rettype->type = ITEM_Long;
				} else {
					params[params_count].type = ITEM_Long;
					params[params_count+1].type = ITEM_Invalid;
					params_count += 2;
				}
				break;

			case 'D':
				if (param_end) {
					rettype->type = ITEM_Double;
				} else {
					params[params_count].type = ITEM_Double;
					params[params_count+1].type = ITEM_Invalid;
					params_count += 2;
				}
				break;

			 default:
			   // パースできない
			   return -1;
			 }
		} else {
			// 配列またはクラス型
			const java_utf8* class_name = intern_class_name(method_descriptor, length);
			if (! class_name) {
				return -1;
			}
			if (param_end) {
				rettype->type = ITEM_Object;
				rettype->class_name = class_name;
			} else {
				params[params_count].type = ITEM_Object;
				params[params_count].class_name = class_name;
				params_count++;
			}
		}
		method_descriptor += length;
	}

	return params_count;
}

/**
 * verified_unitにメモリを割り当てる
 */
static bool alloc_verified_unit(data_flow_analyzer* analyzer,
								verified_unit* unit,
								u2 locals,
								u2 stack) {
	assert(! unit->local_variables);
	unit->local_variables
			= (data_item*) calloc(sizeof(data_item) * (locals + stack), 1);
	if (! unit->local_variables) {
		throw_verify_error(analyzer, "Not enough memory");
		return false;
	}
	unit->operand_stack = unit->local_variables + locals;
	return true;
}

/**
 * verified_unitに割り当てたメモリをすべて開放する
 */
static void free_verified_units(data_flow_analyzer* analyzer) {
	for (u2 i = 0; i < analyzer->verified_unit_count; ++i) {
		verified_unit* punit = &analyzer->verified_units[i];
		free(punit->local_variables);
		// punit->operand_stack は local_variables と同時に確保しているため
		// free()は必要ない
	}
	free(analyzer->work_unit.local_variables);
}

/**
 * 指定された位置をstart_pcとするベリファイユニットを返す。
 * 見つからない場合はNULLを返す。
 */
static verified_unit* get_verified_unit(data_flow_analyzer* analyzer,
										u2 start_pc) {
	for (u2 i = 0; i < analyzer->verified_unit_count; ++i) {
		verified_unit* unit = &analyzer->verified_units[i];
		if (unit->start_pc == start_pc) {
			return unit;
		}
	}
	return NULL;
}

/**
 * 指定されたモデルを、指定されたpc位置にあるモデルにマージする。
 */
static bool merge(data_flow_analyzer* analyzer,
				  verified_unit* from_unit,
				  u2 current_pc,
				  u2 target_pc) {
	
	verified_unit* to_unit = get_verified_unit(analyzer, target_pc);
	if (! to_unit) {
		verifier_panic(analyzer, "Failed to get verified_unit");
		return false;
	}
	
	// 後方ジャンプ時、または例外ハンドラへのマージ時には、ローカル変数内に
	// ITEM_Uninitialized が存在しないことを確認する
	if (target_pc < current_pc || to_unit->exception_handler) {
		Code_attribute* cattr = analyzer->minfo->code_attribute;
		for (u2 i = 0; i < cattr->max_locals; ++i) {
			if (from_unit->local_variables[i].type == ITEM_Uninitialized) {
				throw_verify_error(analyzer, "Uninitialized object in local variables");
				return false;
			}
		}
	}

	if (! to_unit->copied) {
		// まだコピーされていない
		Code_attribute* cattr = analyzer->minfo->code_attribute;
		u2 stack_size = (to_unit->exception_handler) ? 1
													 : from_unit->operand_stack_position;

		// メモリを割り当てる
		if (! alloc_verified_unit(analyzer,
								  to_unit,
								  cattr->max_locals,
								  stack_size)) {
			return false;
		}

		// ローカル変数とオペランドスタックの内容をコピーする
		// (local_variablesとoperand_stackの内容は連続している）
		if (! to_unit->exception_handler) {
			memcpy(to_unit->local_variables,
				   from_unit->local_variables,
				   sizeof(data_item) * (cattr->max_locals + from_unit->operand_stack_position));

			// オペランド・スタックの開始位置を決定する
			to_unit->operand_stack_position
				= from_unit->operand_stack_position;
		} else {
			// 例外ハンドラの場合、ローカル変数をコピーした後
			// 例外オブジェクトをpushする
			memcpy(to_unit->local_variables,
				   from_unit->local_variables,
				   sizeof(data_item) * cattr->max_locals);
			const java_utf8* exception_name;
			for (u2 i = 0; i < cattr->exception_table_length; ++i) {
				exception_handler_info* handler = cattr->exception_table + i;
				if (handler->handler_pc == target_pc) {
					if (handler->catch_type) {
						exception_name = get_CONSTANT_Class_info(analyzer->cfile, handler->catch_type);
					} else {
						exception_name = intern_utf8("java/lang/Throwable");
					}
					break;
				}
			}

			// オペランド・スタックに例外オブジェクトをpushする
			to_unit->operand_stack_position = 0;
			data_item item;
			item.type = ITEM_Object;
			item.class_name = exception_name;
			push( analyzer, to_unit, &item);
		}

		
		to_unit->changed = 1;
		to_unit->copied = 1;
		
		// ToDo: retターゲットのコピー
	} else {
		Code_attribute* cattr = analyzer->minfo->code_attribute;
		
		// ローカル変数をマージする
		bool changed_flag = false;
		if (! merge_items(analyzer,
						from_unit->local_variables,
						to_unit->local_variables,
						cattr->max_locals,
						false,
						&changed_flag)) {
			return false;
		}
		if (changed_flag) {
			to_unit->changed = true;
		}

		// オペランドスタックの内容をマージする
		if (! to_unit->exception_handler) {
			// オペランドスタックの位置を検証する
			if (to_unit->operand_stack_position != from_unit->operand_stack_position) {
				throw_verify_error(analyzer, "Failed to merge stack position");
				return false;
			}
			if (! merge_items(analyzer,
						from_unit->operand_stack,
						to_unit->operand_stack,
						to_unit->operand_stack_position,
						true,
						&changed_flag)) {
				return false;
			}
		}

		if (changed_flag) {
			to_unit->changed = true;
		}
	}
	return true;
}

/**
 * 指定されたデータアイテムをマージする
 */
static bool
merge_items(data_flow_analyzer* analyzer, 
			data_item* from_items,
			data_item* to_items,
			int count,
			bool error_on_invalidate,
			bool* changed_flag) {

	bool result = true;
	bool flag = false;
	for (int i = 0; i < count; ++i) {
		data_item* from = &from_items[i];
		data_item* to = &to_items[i];
		switch (to->type) {
		case ITEM_Invalid:
			// すでに無効状態なので何もしない
			break;

		case ITEM_Integer:
		case ITEM_Float:
		case ITEM_ReturnAddress:
			// 完全に一致しなければならない
			if (from->type != to->type) {
				to->type = ITEM_Invalid;
				flag = true;
			}
			break;
		
		case ITEM_Long:
		case ITEM_Double:
			// 完全に一致しなければならない
			if (from->type != to->type) {
				to->type = ITEM_Invalid;
				// 2エントリ分無効にする
				to_items[i + 1].type = ITEM_Invalid;
				// 念のため、１つ手前のエントリも調べる
				// 1つ手前のエントリがlongまたはdoubleの
				// 場合そのエントリも無効化する
				if (i != 0 && (to_items[i - 1].type == ITEM_Long || to_items[i - 1].type == ITEM_Double)) {
					to_items[i - 1].type = ITEM_Invalid;
				}
				flag = true;
			}
			assert(to_items[i + 1].type == ITEM_Invalid);
			// ２エントリ分占有する
			i++;
			break;

		default:
			if (IS_OBJECT(from) && IS_OBJECT(to)) {
				// 両方ともオブジェクトの場合はマージする
				merge_object(analyzer, from, to);
			} else {
				// マージできない
				to->type = ITEM_Invalid;
				flag = true;
			}
		}
		if (flag && error_on_invalidate) {
			throw_verify_error(analyzer, "Failed to merge items");
			result = false;
			break;
		}
	}

	if (changed_flag) {
		*changed_flag = flag;
	}

	return result;
}

/**
 * 指定されたオブジェクトに対するマージ処理を行う
 */
static bool
merge_object(data_flow_analyzer* analyzer,
			 data_item* from_object,
			 data_item* to_object) {

	assert(IS_OBJECT(from_object) && IS_OBJECT(to_object));

	if (from_object->type == ITEM_Null) {
		// マージ元がnullの場合には何もしない（変化しない）
		return true;
	}

	switch (to_object->type) {
	case ITEM_Null:
		// nullには上書きする
		*to_object = *from_object;
		break;

	case ITEM_Object:
		// オブジェクト
		if (from_object->type == ITEM_Object) {
			if (from_object->class_name == to_object->class_name) {
				// 同一オブジェクト
				return true;
			} else {
				// マージする
				const merged_class_names* merged
					= create_merged_class_names(analyzer,
												from_object->class_name,
												to_object->class_name);
				to_object->type = ITEM_MergedObjects;
				to_object->merged_classes = merged;
			}
		} else if (from_object->type == ITEM_MergedObjects) {
			// マージする
			const merged_class_names* merged
				= create_merged_class_names(analyzer,
											from_object->merged_classes,
											to_object->class_name);
			to_object->type = ITEM_MergedObjects;
			to_object->merged_classes = merged;
		} else {
			// マージできない
			to_object->type = ITEM_Invalid;
			throw_verify_error(analyzer, "Failed to merge");
			return false;
		}
		break;

	case ITEM_MergedObjects:
		if (from_object->type == ITEM_Object) {
			// マージする
			const merged_class_names* merged
				= create_merged_class_names(analyzer,
											to_object->merged_classes,
											from_object->class_name);
			if (to_object->merged_classes != merged) {
				// 変更された
				to_object->merged_classes = merged;
			}
		} else if (from_object->type == ITEM_MergedObjects) {
			// マージする
			const merged_class_names* merged
				= create_merged_class_names(analyzer,
											from_object->merged_classes,
											to_object->merged_classes);
			if (to_object->merged_classes != merged) {
				to_object->merged_classes = merged;
			}
		} else {
			// マージできない
			to_object->type = ITEM_Invalid;
			throw_verify_error(analyzer, "Failed to merge");
			return false;
		}
		break;

	case ITEM_Uninitialized:
		if (! (from_object->type == ITEM_Uninitialized
			&& from_object->pc == to_object->pc)) {
			// 同一の未初期化オブジェクトではないため
			// マージできない
			to_object->type = ITEM_Invalid;
			throw_verify_error(analyzer, "Failed to merge");
			return false;
		}
		break;

	case ITEM_UninitializedThis:
		if (from_object->type != ITEM_UninitializedThis) {
			// マージできない
			to_object->type = ITEM_Invalid;
			throw_verify_error(analyzer, "Failed to merge");
			return false;
		}
		break;

	default:
		// マージできない
		to_object->type = ITEM_Invalid;
		throw_verify_error(analyzer, "Failed to merge");
		return false;
	}
	return true;
}


/**
 * 指定されたdata_itemの内容を、もう一方の配列内容に代入可能か調べる。
 * この関数は関数の引数や配列操作命令(iaload等）が使用するため、
 * 以下の制約事項がある。
 * 引数 to_items->type の内容は、以下のいずれかでなければならない
 * - ITEM_Integer
 * - ITEM_Float
 * - ITEM_Long
 * - ITEM_Double
 * - ITEM_Null
 * - ITEM_Object
 */
static bool check_assignable(data_flow_analyzer* analyzer,
							 data_item* from_items,
							 data_item* to_items,
							 unsigned int count) {
	bool error = false;
	for (unsigned int i = 0; i < count; ++i) {
		data_item* from = &from_items[i];
		data_item* to = &to_items[i];
		switch (to->type) {
		case ITEM_Integer:
		case ITEM_Float:
		case ITEM_Null:
			if (from->type != to->type) {
				error = true;
				break;
			}
			break;

		case ITEM_Long:
		case ITEM_Double:
			if (from->type != to->type) {
				error = true;
				break;
			}
			// ２エントリ占有する
			i++;
			break;

		case ITEM_Object:
			if (from->type == ITEM_Null) {
				break;
			} else if (from->type == ITEM_Object) {
				if (from->class_name == to->class_name) {
					// 同一クラス
					break;
				} else {
					// 両方のクラスをロードし、instanceof 演算子を適用する
					// ToDo: 「初期化しないでロード」するオプションを用意する？
					ClassFile* target_cfile
						= find_ClassFile(analyzer->current_frame,
										 get_defining_loader(analyzer->cfile),
										 to->class_name);
					ClassFile* from_cfile
						= find_ClassFile(analyzer->current_frame,
										 get_defining_loader(analyzer->cfile),
										 from->class_name);
					if (! from_cfile || ! target_cfile) {
						return false;
					}
					if (! is_assignable_from(analyzer->current_frame, from_cfile, target_cfile)) {
						error = true;
						break;
					}
				}
			} else if (from->type == ITEM_MergedObjects) {
				// 型チェック
				ClassFile* target_cfile
					= find_ClassFile(analyzer->current_frame,
									 get_defining_loader(analyzer->cfile),
									 to->class_name);
				if (! target_cfile) {
					return false;
				}
				const merged_class_names* merged = from->merged_classes;
				for (unsigned int i = 0; i < merged->count; ++i) {
					// 両方のクラスをロードし、instanceof 演算子を適用する
					// ToDo: 「初期化しないでロード」するオプションを用意する？
					ClassFile* from_cfile
						= find_ClassFile(analyzer->current_frame,
										 get_defining_loader(analyzer->cfile),
										 merged->class_names[i]);
					if (! from_cfile) {
						return false;
					}
					if (! is_assignable_from(analyzer->current_frame, from_cfile, target_cfile)) {
						error = true;
						break;
					}
				}
			}
			break;

		default:
			verifier_panic(analyzer, "Unexpected data type");
			return false;

		}

		if (error) {
			throw_verify_error(analyzer, "Faild to assign value");
			return false;
		}
	}

	return true;
}

/**
 * ローカル変数の指定されたインデックス位置に、データを格納する。
 */
static bool set_local(data_flow_analyzer* analyzer,
					  verified_unit* unit,
					  u2 index,
					  data_item* item) {
	
	unit->local_variables[index] = *item;
	
	if (index) {
		data_item* prev_item = &unit->local_variables[index - 1];
		if (prev_item->type == ITEM_Long || prev_item->type == ITEM_Double) {
			// long, doubleの上位32ビットを上書きした場合、
			// 下位32ビットをITEM_Invalidに変更する
			prev_item->type = ITEM_Invalid;
		}
	}
	// 指定された値がlong, doubleの場合は、上位32ビットにITEM_Invalidを入れる
	if (item->type == ITEM_Long || item->type == ITEM_Double) {
		unit->local_variables[index + 1].type = ITEM_Invalid;
	}
	return true;
}

/**
 * ローカル変数の指定された位置から、データを取得する
 */
inline static bool get_local(data_flow_analyzer* analyzer,
					  verified_unit* unit,
					  u2 index,
					  data_item* item) {
	*item = unit->local_variables[index];
	return true;
}

/**
 * ローカル変数の指定された位置から、intを取得する
 */
static bool get_int(data_flow_analyzer* analyzer,
					  verified_unit* unit,
					  u2 index) {
	data_item item;
	get_local(analyzer, unit, index, &item);
	if (item.type != ITEM_Integer) {
		throw_verify_error(analyzer, "Local variable must be integer");
		return false;
	}
	return true;
}

/**
 * ローカル変数の指定された位置から、floatを取得する
 */
static bool get_float(data_flow_analyzer* analyzer,
					  verified_unit* unit,
					  u2 index) {
	data_item item;
	get_local(analyzer, unit, index, &item);
	if (item.type != ITEM_Float) {
		throw_verify_error(analyzer, "Local variable must be float");
		return false;
	}
	return true;
}

/**
 * ローカル変数の指定された位置から、longを取得する
 */
static bool get_long(data_flow_analyzer* analyzer,
					  verified_unit* unit,
					  u2 index) {
	data_item item;
	get_local(analyzer, unit, index, &item);
	if (item.type != ITEM_Long) {
		throw_verify_error(analyzer, "Local variable must be long");
		return false;
	}
	return true;
}

/**
 * ローカル変数の指定された位置から、floatを取得する
 */
static bool get_double(data_flow_analyzer* analyzer,
					  verified_unit* unit,
					  u2 index) {
	data_item item;
	get_local(analyzer, unit, index, &item);
	if (item.type != ITEM_Double) {
		throw_verify_error(analyzer, "Local variable must be double");
		return false;
	}
	return true;
}

/**
 * ローカル変数の指定された位置から、Objectを取得する
 */
static bool get_object(data_flow_analyzer* analyzer,
					  verified_unit* unit,
					  u2 index,
					  data_item* item) {
	get_local(analyzer, unit, index, item);
	if (! (IS_OBJECT(item) || IS_RETURN_ADDRESS(item))) {
		throw_verify_error(analyzer, "Local variable must be Object");
		return false;
	}
	return true;
}

/**
 * ローカル変数の指定された位置から、Objectを取得する
 */
static bool get_object(data_flow_analyzer* analyzer,
					  verified_unit* unit,
					  u2 index) {
	data_item item;
	return get_object(analyzer, unit, index, &item);
}


/**
 * merged_class_names構造体を作成する
 */
static merged_class_names*
create_merged_class_names(data_flow_analyzer* analyzer,
						  u2 class_count) {
	merged_class_names* merged
			= (merged_class_names*) calloc(sizeof(merged_class_names), 1);
	if (! merged) {
		throw_verify_error(analyzer, "Not enough memory");
		return NULL;
	}
	merged->class_names
			= (const java_utf8**) calloc(sizeof(java_utf8*) * class_count, 1);
	if (! merged->class_names) {
		free(merged);
		throw_verify_error(analyzer, "Not enough memory");
		return NULL;
	}
	merged->count = class_count;
	merged->next = analyzer->merged_classes_root;
	analyzer->merged_classes_root = merged;
	
	return merged;
}

/**
 * merged_class_names構造体を開放する
 */
static void
delete_merged_class_names(data_flow_analyzer* analyzer) {
	merged_class_names* merged = analyzer->merged_classes_root;
	while (merged) {
		merged_class_names* next = merged->next;
		free(merged->class_names);
		free(merged);
		merged = next;
	}
}

/**
 * 指定された２つのクラス名を保持する構造体を返す。
 */
static const merged_class_names*
create_merged_class_names(data_flow_analyzer* analyzer,
						  const java_utf8* class_name1,
						  const java_utf8* class_name2) {

	merged_class_names* merged
		= create_merged_class_names(analyzer, 2);
	if (merged) {
		merged->class_names[0] = class_name1;
		merged->class_names[1] = class_name2;
	} else {
		throw_verify_error(analyzer, "Not enough memory");
	}
	return merged;
}

/**
 * 指定されたクラス名を保持する構造体を返す。
 * merged内にclass_nameが存在した場合、mergedを返す。
 */
static const merged_class_names*
create_merged_class_names(data_flow_analyzer* analyzer,
						  const merged_class_names* merged,
						  const java_utf8* class_name) {
	// すでにマージされているかを調べる
	for (u2 i = 0; i < merged->count; ++i) {
		if (merged->class_names[i] == class_name) {
			return merged;
		}
	}

	// メモリを割り当てる
	merged_class_names* result
		= create_merged_class_names(analyzer, merged->count + 1);
	if (result) {
		memcpy(result->class_names,
		       merged->class_names,
		       sizeof(java_utf8*) * merged->count);
		result->class_names[result->count - 1] = class_name;
	} else {
		throw_verify_error(analyzer, "Not enough memory");
	}
	return result;
}

/**
 * 指定されたクラス名を保持する構造体を返す。
 * merged1 と merged2 の内容が等しい場合、merged1を返す
 */
static const merged_class_names*
create_merged_class_names(data_flow_analyzer* analyzer,
						  const merged_class_names* merged1,
						  const merged_class_names* merged2) {
	// すでにマージされているかを調べる
	bool found = false;
	for (u2 i = 0; i < merged1->count; ++i) {
		found = false;
		const java_utf8* class_name1 = merged1->class_names[i];
		for (u2 j = 0; j < merged2->count; ++j) {
			if (merged2->class_names[j] == class_name1) {
				found = true;
				break;
			}
		}
		if (! found) {
			// 完全には一致しない
			break;
		}
	}

	if (found) {
		// 内容が完全に一致した
		return merged1;
	}

	// 新規にメモリを割り当てる
	merged_class_names* result
		= create_merged_class_names(analyzer, merged1->count + merged2->count);
	if (result) {
		memcpy(result->class_names,
		       merged1->class_names,
		       sizeof(java_utf8*) * merged1->count);
		memcpy(result->class_names + merged1->count,
		       merged2->class_names,
		       sizeof(java_utf8*) * merged2->count);
	} else {
		throw_verify_error(analyzer, "Not enough memory");
	}
	return result;
}

/**
 * 指定された配列クラス名から、配列要素のクラス名を取り出す
 */
static const
java_utf8* get_component_of_object_array(const java_utf8* object_array_name) {
	int len = strlen(object_array_name);
	const java_utf8* element_name;
	if (object_array_name[1] == 'L') {
		// オブジェクトの配列
		element_name = intern_utf8(&object_array_name[2], len - 3);
	} else {
		// 配列の配列
		element_name = intern_utf8(&object_array_name[1], len - 1);
	}
	return element_name;
}
