/* class_loader.cpp
   Copyright (C) 2005 Free Software Foundation, Inc.

This file is part of Mysaifu JVM

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

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

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


#include "StdAfx.h"
#include "ClassFile.h"
#include "class_loader.h"
#include "java_utf8.h"
#include "unzip/unzip.h"
#include "java_object.h"
#include "instruction.h"
#include "java_thread.h"
#include "jni_funcs.h"
#include "common_funcs.h"
#include "verifier.h"
#include "java_utf8.h"

/**
 * staticデータ情報
 */
struct static_data_status {
	/**
	 * staticデータ
	 */
	jobject static_data;

	/**
	 * ステータス
	 */
	int status;

	/**
	 * 初期化処理中のスレッドID
	 */
	DWORD initializing_thread_id;
};

/**
 * クラスローダの要素
 */
struct class_loader_element {
	/**
	 * 開始ローダを示す場合 true。
	 */
	bool initiating_loader;

	/**
	 * ロードしたClassFile構造体の参照
	 */
	ClassFile* cfile;
	
	/**
	 * java.lang.Classのインスタンス（必要になった時点で生成）
	 */
	jclass class_object;
};

/**
 * ClassCircularityError をチェックするための構造体
 */
struct loading_class {
	loading_class* next;
	const java_utf8* name;
};

/**
 * クラスローダ
 */
struct class_loader {
	/**
	 * クラスローダのロック
	 */
	CRITICAL_SECTION* lock;

	/**
	 * クラスローダのインスタンス。
	 * ブートストラップローダの場合はNULL。
	 */
	jobject instance;

	/**
	 * 有効な要素数
	 */
	unsigned int elements_count;

	/**
	 * 要素のサイズ（確保済みのサイズ）
	 */
	unsigned int elements_size;

	/**
	 * 要素
	 */
	class_loader_element** elements;

	/**
	 * このクラスローダがロードしたクラスに対して
	 * ベリファイが必要か
	 */
	bool verify;

	/**
	 * ClassCircularityErrorチェック用
	 */
	loading_class* circularity_info;

	/**
	 * ClassFile構造体を割り当てるためのメモリ領域
	 */
	PermanentHeap* heap;
};

// ロード制約
struct loading_constraints {
	loading_constraints* next;		// 単方向リストとして管理する
	const java_utf8* class_name;	// クラス名
	class_loader* loader1;			// 制約１
	class_loader* loader2;			// 制約２
};

// ロード制約テーブル数（適当な素数）
#define LOADING_CONSTRAINTS_TABLE_SIZE	1021 

// ロード制約テーブル
static loading_constraints* g_loading_constraints_table[LOADING_CONSTRAINTS_TABLE_SIZE];

// ブートストラップ・クラスローダが使用するクラスパス
struct classpath_element {
	char*		path;		// パス名。ディレクトリを表す場合、必ず "\" で終わっている必要がある
	bool		jar;		// jarファイルの場合true
	ZIPFILE*	zipfile;	// jarファイル解凍用
};

/**
 * ブートストラップクラスパス
 */
static classpath_element** g_bootstrap_classpath;

/**
 * ブートストラップクラスパス（文字列）
 */
static _TCHAR* g_bootstrap_classpath_string;

/**
 * ブートストラップクラスパスの数
 */
static int g_bootstrap_classpath_count;

/**
 * システムクラスパス
 */
static _TCHAR* g_system_classpath;

/**
 * ブートストラップ・クラスローダ
 */
class_loader* g_bootstrap_class_loader = NULL;

/**
 * その他のクラスローダ
 */
static unsigned int g_class_loaders_count;
static class_loader** g_class_loaders;

// static 関数の定義
/**
 * 指定されたクラスローダにより、指定された名前のClassFile構造体を読み込む。
 */
static ClassFile* load_ClassFile(frame* frm, class_loader* loader, const java_utf8* class_name);

/**
 * ブートストラップクラスローダにより、指定された名前のクラスをロードする
 * 引数 class_name は必ず「インターン」しておく必要がある。
 */
inline static ClassFile* load_ClassFile(frame* current_frame, const java_utf8* class_name) {
	return load_ClassFile(current_frame, get_bootstrap_class_loader(), class_name);
}
static ClassFile* load_ClassFile_from_file(frame* current_frame, const java_utf8* class_name, class_loader* loader);
static ClassFile* load_ClassFile_from_jar(frame* current_frame, const java_utf8* class_name, class_loader* loader, classpath_element* elem, const char* classfilename);


/**
 * 指定されたクラスを初期化し、クラスローダに登録する
 */
static bool link_ClassFile(frame* current_frame, class_loader* loader, ClassFile* cfile);

/**
 * 指定されたロード制約の妥当性をチェックする。
 */
static bool check_loading_constraints(frame* frm, const java_utf8* class_name, class_loader* loader, ClassFile* cfile);

/**
 * ロード制約を追加する。
 * ロード制約の追加に失敗した場合（＝ロード済みクラスの間ですでにロード制約が破られていた場合）
 * falseを返す。
 */
static bool add_loading_constraints(frame* frm, const java_utf8* class_name, class_loader* loader1, class_loader* loader2);

// その他のstatic関数
static jclass create_class_object(frame* current_frame, jobject static_data, jobject pd);
static void add_static_data(jobject data);
static classpath_element* create_classpath_element(char* path, bool jar);
bool ensure_static_data_initialized(frame* current_frame, ClassFile* cfile);


/**
 * 指定されたクラス名に対応するclass_loader_element構造体を検索する
 */
static class_loader_element* find_class_loader_element(class_loader* l, const java_utf8* name);

/**
 * 指定されたClassFile構造体をclass_loaderに追加する
 */
static void add_ClassFile(class_loader* l, ClassFile* cfile);

/**
 * class_loaderを確保する
 */
static class_loader* allocate_class_loader(jobject cl);

ClassFile* g_primitive_classes[9];
jclass g_primitive_class_objects[9];

/**
 * ベリファイモード
 */
static VerifyMode g_verify_mode;

/**
 * クラスローダが使用するヒープのデフォルトサイズ
 */
#define DEFAULT_CLASS_LOADER_HEAP_SIZE (32*1024)

/**
 * クラスローダ設定を初期化する
 */
void init_class_loader_settings(VerifyMode verify_mode) {
	g_verify_mode = verify_mode;

	// ブートストラップクラスローダを初期化する
	g_bootstrap_class_loader = 	allocate_class_loader(NULL);
}

/**
 * 指定された名前のプリミティブクラスをロードする
 */
ClassFile* load_primitive_ClassFile(frame* frm, const java_utf8* class_name) {
	class_name = intern_utf8(class_name);

	// クリティカルセクションに入る
	EnterCriticalSection(g_bootstrap_class_loader->lock);

	ClassFile* cfile = NULL;
	int i;
	for (i = 0; i < 9; ++i) {
		cfile = g_primitive_classes[i];
		if (! cfile) {
			break;
		} else if (cfile->this_class_name == class_name) {
			break;
		}
	}
	if (cfile == NULL && i < 9) {
		cfile = create_primitive_ClassFile(class_name);
		// クラスファイルを初期化する
		prepare_ClassFile(frm,
					   cfile,
					   NULL,	// スーパークラスなし
					   NULL);	// 実装インタフェースなし
		g_primitive_classes[i] = cfile;
		jobject static_data = alloc_static_data(cfile);
		if (static_data == NULL) {
			throw_OutOfMemoryError(frm);
		} else {
			add_static_data(static_data);
		}
	}
	// クリティカルセクションから抜ける
	LeaveCriticalSection(g_bootstrap_class_loader->lock);
	
	return cfile;
}

/**
 * プリミティブクラスの Classオブジェクトを返す。
 */
jclass find_primitive_class_object(frame* current_frame, jobject static_data) {
	ClassFile* cfile = get_ClassFile(static_data);
	jclass class_object = NULL;
	// クリティカルセクションに入る
	EnterCriticalSection(g_bootstrap_class_loader->lock);
	for (int i = 0; i < 9; ++i) {
		if (cfile == g_primitive_classes[i]) {
			class_object = g_primitive_class_objects[i];
			if (class_object == NULL) {
				class_object = create_class_object(current_frame, static_data, NULL);
				g_primitive_class_objects[i] = class_object;
			}
		}
	}
	// クリティカルセクションから抜ける
	LeaveCriticalSection(g_bootstrap_class_loader->lock);
	return class_object;
}

/**
 * 指定されたクラスローダによってクラスの定義を読み込み、ClassFile構造体へのポインタを返す
 * ロードに失敗した場合は、NULLを返す
 * 引数 class_name は必ず「インターン」しておく必要がある。
 *
 */
static ClassFile* load_ClassFile(frame* current_frame, class_loader* l, const java_utf8* class_name) {
	// 配列クラスが指定されている場合には、配列の構成要素によって定義ローダが決まる
	// 配列要素のアクセスフラグ
	u2 array_element_access_flags = ACC_PUBLIC;
	if (*class_name == '[') {
		ClassFile* acfile = NULL;
		if (class_name[1] == '[' || class_name[1] == 'L') {
			// 構成要素をロードする
			if (class_name[1] == '[') {
				const char* tmp = &class_name[1];
				acfile = load_ClassFile(current_frame, l, intern_utf8(tmp));
				if (is_public(acfile->access_flags)) {
					array_element_access_flags = ACC_PUBLIC;
				} else if (is_protected(acfile->access_flags)) {
					array_element_access_flags = ACC_PROTECTED;
				} else if (is_private(acfile->access_flags)) {
					array_element_access_flags = ACC_PRIVATE;
				}
			} else if (class_name[1] == 'L') {
				int len = strlen(&class_name[2]);
				char* tmp = (char*) malloc(len);
				if (tmp == NULL) {
					fatal_error(FATAL_ERROR_NO_MEMORY);
				}
				strncpy(tmp, &class_name[2], len - 1);
				tmp[len - 1] = '\0';
				acfile = load_ClassFile(current_frame, l, intern_utf8(tmp));
				free(tmp);
			}
			if (acfile == NULL) {
				return NULL;
			}
			// 構成要素の定義クラスローダを、生成した配列クラスの定義ローダとする
			l = acfile->defining_loader;
		} else {
			l = get_bootstrap_class_loader();
		}
	}

	// この関数呼び出し時点で「インターン」されていることを前提にしている
	// 以下の部分のコメントをはずすと、この前提が正しいかチェックできる
	// assert(class_name == intern_utf8(class_name));

	// もしロード済みであれば、その構造体を返す
	// クリティカルセクションに入り、検索処理を行う
	ClassFile* cfile = NULL;
	EnterCriticalSection(l->lock);
	class_loader_element* elem = find_class_loader_element(l, class_name);
	if (elem) {
		cfile = elem->cfile;
		if (! elem->initiating_loader) {
			elem->initiating_loader = true;
			// ロード制約をチェックする
			if (! check_loading_constraints(current_frame, class_name, l, cfile)) {
				// 開始ローダの記録を撤回
				elem->initiating_loader = false;
				LeaveCriticalSection(l->lock);
				return NULL;
			}
		}
	}
	
	if (cfile != NULL) {
		// すでにロード済みの場合は、そのファイルを返す
		// クリティカルセクションを抜ける
		LeaveCriticalSection(l->lock);
		return cfile;
	}

	// 以降はロード済みでなかった場合に実行される

	if (*class_name == '[') {
		// 配列クラスはVMが生成する
		cfile = create_array_ClassFile(l, class_name, array_element_access_flags);
		if (! link_ClassFile(current_frame, l, cfile)) {
			free_ClassFile(cfile);
		}

		// クリティカルセクションを抜ける
		LeaveCriticalSection(l->lock);
		
		return cfile;
	}

	// ブートストラップクラスローダ以外が指定されていた場合は、loadClass()メソッドを
	// 呼び出す
	if (l != g_bootstrap_class_loader) {
		// クリティカルセクションを抜ける
		LeaveCriticalSection(l->lock);
		cfile = call_loadClass(current_frame, l->instance, class_name);
		if (cfile) {
			elem = find_class_loader_element(l, class_name);
			if (! elem) {
				// 開始ローダとして記録するため、ここで登録する
				add_ClassFile(l, cfile);
				elem = find_class_loader_element(l, class_name);
			}
			assert(elem);
			if (! elem->initiating_loader) {
				elem->initiating_loader = true;
				// ロード制約をチェックする
				if (! check_loading_constraints(current_frame, class_name, l, cfile)) {
					// 開始ローダの記録を撤回
					elem->initiating_loader = false;
					LeaveCriticalSection(l->lock);
					return NULL;
				}
			}
		}
	}
	
	// ここから先は、ブートストラップクラスローダによるロード処理
	
	bool error = false;
	if (cfile == NULL) {
		// 通常のクラスは、ファイルからロードする
		cfile = load_ClassFile_from_file(current_frame, class_name, l);
		if (cfile == NULL || exception_occurred(current_frame)) {
			error = true;
		} else {
			elem = find_class_loader_element(l, class_name);
			assert(elem);
			if (! elem->initiating_loader) {
				elem->initiating_loader = true;
				// ロード制約をチェックする
				if (! check_loading_constraints(current_frame, class_name, l, cfile)) {
					// 開始ローダの記録を撤回
					elem->initiating_loader = false;
					LeaveCriticalSection(l->lock);
					return NULL;
				}
			}
		}
	}
	
	// クリティカルセクション終了
	LeaveCriticalSection(l->lock);
	
	if (cfile == NULL) {
		DBG(_T("Bootstrap class loader could not find class="));
		DBG_UTF8(class_name);
		DBG(_T("\n"));
	}
	return (! error) ? cfile : NULL;
}

/*	
 * ファイルからクラスをロードする
 */
static ClassFile* load_ClassFile_from_file(frame* current_frame, const java_utf8* class_name, class_loader* loader) {
	const char* ext = ".class";
	unsigned int cfnamelen = strlen(class_name) + strlen(ext) + 1;	// ".class" + '\0'
	char* classfilename = (char*) malloc(cfnamelen);
	if (! classfilename) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	_snprintf(classfilename, cfnamelen, "%s%s", class_name, ext);

	ClassFile* cfile = NULL;
	for (int i = 0; i < g_bootstrap_classpath_count ; ++i) {
		classpath_element* path = g_bootstrap_classpath[i];
		if (path->jar) {
			// Jarファイルから読み込む
			cfile = load_ClassFile_from_jar(current_frame,
											class_name,
											loader,
											path,
											classfilename);
		} else {
			const int pathlen = strlen(path->path) + strlen(classfilename) + 1;
			char* abspath = (char*) malloc(pathlen);
			if (abspath == NULL) {
				fatal_error(FATAL_ERROR_NO_MEMORY);
			}
			_snprintf(abspath, pathlen, "%s%s", path->path, classfilename);

			// ファイルをオープンし、内容を読み込む
			FILE* file = fopen(abspath, "rb");
			free(abspath);

			if (file != NULL) {
				fseek(file, 0L, SEEK_END);
				size_t file_size = (size_t) ftell(file);
				fseek(file, 0L, SEEK_SET);
				char* classbytes = (char*) malloc(file_size);
				if (classbytes == NULL) {
					fatal_error(FATAL_ERROR_NO_MEMORY);
				}
				if (classbytes != NULL) {
					// クラスファイルを読み込み、パースする
					fread(classbytes, file_size, file_size, file);
					fclose(file);
					
					cfile = define_ClassFile(current_frame,
											class_name,
											loader,
											classbytes,
											0,
											file_size);
				}
			}
		}
	}
	free(classfilename);
	return cfile;
}

/**
 * 指定された２つのクラスファイルが、同一のパッケージに属しているかを返す。
 */
bool is_same_package(class_loader* loader, ClassFile* c1, ClassFile* c2) {
	static u1 slash = (u1) '/';

	// 現在の実装では、第１引数のクラスローダは無視する
	const java_utf8* name1 = c1->this_class_name;
	const java_utf8* name2 = c2->this_class_name;
	
	char* pos1 = strrchr(name1, slash);
	char* pos2 = strrchr(name2, slash);
	if (pos1) {
		// スラッシュまでを比較する
		return ((pos1 - name1) == (pos2 - name2))
				&& (strncmp(name1, name2, pos1 - name1) == 0);
	} else {
		// どちらも無名パッケージの場合のみtrue
		return ! pos2;
	}
}

/**
 * 指定されたjarファイルから、対応するClassFileを取り出す
 */
static ClassFile* load_ClassFile_from_jar(frame* current_frame, const java_utf8* class_name, class_loader* loader, classpath_element* elem, const char* classfilename) {
	// jarファイルをオープン
	if (elem->zipfile == NULL) {
		const char* jarfile = elem->path;
		int len = strlen(jarfile);
		_TCHAR* filename = (_TCHAR*) malloc(sizeof(_TCHAR) * (len + 1));
		if (filename == NULL) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		convert_to_TCHAR(jarfile, filename, len + 1);
		elem->zipfile = unzip_open(filename);
		free(filename);
	}
	if (elem->zipfile == NULL) {
		return NULL;
	}
	
	ClassFile* cfile = NULL;
	if (unzip_seek(elem->zipfile, classfilename)) {
		DWORD uncompressed_size = unzip_get_uncompressed_size(elem->zipfile);
		char* classbytes = (char*) malloc(uncompressed_size);
		if (classbytes == NULL) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		if (unzip_read(elem->zipfile, classbytes, uncompressed_size)) {
			cfile = define_ClassFile(current_frame,
									 class_name,
									 loader,
									 classbytes,
									 0,
									 uncompressed_size);
		}
		free(classbytes);
 	}
	
	return cfile;

}

/**
 * 指定されたClassFileのstaticデータを検索する
 * 存在していない場合はNULLを返す
 *
 * @param	cfile	検索対象となるClassFile構造体へのポインタ
 * @return	対応するstaticデータ。存在しない場合NULL
 */
jobject get_static_data(ClassFile* cfile) {
	if (cfile->static_data_stat != NULL) {
		return cfile->static_data_stat->static_data;
	} else {
		assert(false);
		return NULL;
	}
}

/**
 * 指定されたstaticデータに対応する java.lang.Classのインスタンスを生成する
 */
static jclass create_class_object(frame* current_frame, jobject static_data, jobject pd) {
	// 例外を一時的にクリアしておく
	jthrowable t = current_frame->body->current_exception;
	jthrowable global_throwable = NULL;
	if (t) {
		// ガベージコレクトされないようにグローバル参照として保持しておく
		global_throwable = (jthrowable) new_global_reference(t);
	}
	current_frame->body->current_exception = NULL;

	// java/lang/Class をロードする
	jclass class_object = NULL;
	ClassFile* cfile = load_ClassFile(current_frame, CLASS_CLASS_NAME);
	ClassFile* target_cfile = get_ClassFile(static_data);

	jclass tmp = (jclass) alloc_object(cfile, current_frame);
	if (tmp != NULL) {
		DUP_STACK(current_frame);
		PUSH_OBJECT(current_frame, static_data);	// vmdata
		
		if (target_cfile->this_class_name == JAVA_LANG_VMCLASS_CLASS_NAME
			|| target_cfile->this_class_name == CLASS_CLASS_NAME) {
			// java.lang.Classオブジェクトそのものを表す Class は特別扱いする。
			// コンストラクタ呼び出しではなく、フィールド vmdata に直接値を設定する
			// (そうしないと無限に再帰してしまう）
			jfieldID fid = get_field_id(cfile,
										intern_utf8("vmdata"),
										intern_utf8("Ljava/lang/Object;"));
			assert(fid);
			put_field(current_frame, cfile, fid);
			class_object = tmp;

		} else {
			// Class(Object vmdata, ProtectionDomain pd) を呼び出す
			jmethodID mid = get_method_id(cfile,
										  INIT_METHOD_NAME,
										  JAVA_LANG_CLASS_CONSTRUCTOR_DESCRIPTOR);
			if (mid != NULL) {
				method_info* minfo = get_method_info(cfile, mid);
				if (minfo != NULL) {
					// ProtectionDomainを引数に追加する
					PUSH_OBJECT(current_frame, pd);		
					// Classのコンストラクタをここで実行してもデッドロックは発生しない？
					invoke_method(current_frame, tmp, cfile, minfo);
					if (! exception_occurred(current_frame)) {
						class_object = tmp;
					}
				}
			} else {
				fatal_error(_T("Could not found Class constructor"));
			}
		}
	}
	if (class_object != NULL) {
		// ガベージコレクトされないように、グローバル参照として登録する
		class_object = (jclass) new_global_reference(class_object);
		// スタックからpopする
		jobject tmp;
		POP_OBJECT(current_frame, tmp);
	}

	class_loader_element* elem = find_class_loader_element(get_defining_loader(target_cfile),
														   target_cfile->this_class_name);
	if (elem) {
		assert(elem->cfile->defining_loader == target_cfile->defining_loader);
		elem->class_object = class_object;
	} else {
		// プリミティブクラスの場合は elem は存在しない
	}

	// 例外を戻す
	if (t) {
		current_frame->body->current_exception = t;
		delete_global_reference(global_throwable);
	}

	return class_object;
}

/**
 * 指定されたstaticデータに対応するjava.lang.Classのインスタンスを検索する。
 * クラスローダ内に存在していなかった場合、新規にインスタンスを生成する
 *
 * @param	cfile	検索対象となるClassFile構造体へのポインタ
 * @return	対応するClassオブジェクト。存在しない場合NULL
 */
jclass find_class_object(frame* current_frame, jobject static_data, jobject pd) {
	ClassFile* cfile = get_ClassFile(static_data);
	if (is_primitive_class_name(cfile->this_class_name)) {
		// プリミティブクラスをこの関数でロードすることはできない
		return NULL;
	}

	class_loader* l = cfile->defining_loader;

	// クリティカルセクションに入る
	EnterCriticalSection(l->lock);
	
	jclass class_object = NULL;
	class_loader_element* elem = find_class_loader_element(l, cfile->this_class_name);
	assert(elem != NULL);
	
	class_object = elem->class_object;
	if (! class_object) {
		// 新規にクラスオブジェクトを作成する
		class_object = create_class_object(current_frame, static_data, pd);
	}
	
	// クリティカルセクションから抜ける
	LeaveCriticalSection(l->lock);

	return class_object;
}

/**
 * 指定された 名前に対応するロード済み Class オブジェクトを返す。
 * ロードされていない場合はNULLを返す。
 */
jclass find_loaded_class_object(class_loader* l, frame* current_frame, const java_utf8* name) {
	// クリティカルセクションに入る
	EnterCriticalSection(l->lock);
	
	jclass class_object = NULL;
	class_loader_element* elem = find_class_loader_element(l, name);
	if (elem) {
		class_object = elem->class_object;
	}
	// クリティカルセクションから抜ける
	LeaveCriticalSection(l->lock);

	return class_object;
}

/**
 * 指定されたクラスのstaticデータを登録する
 */
static void add_static_data(jobject ref) {
	ClassFile* cfile = get_ClassFile(ref);
	class_loader* l = get_defining_loader(cfile);
	static_data_status* status = (static_data_status*) malloc(sizeof(static_data_status));
	if (status == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	status->static_data = ref;
	status->status = STATIC_DATA_ALLOCATED;
	// ClassFile構造体に設定する
	cfile->static_data_stat = status;
}

/**
 * staticデータの状態を返す。
 */
int get_static_data_status(jobject static_data) {
	ClassFile* cfile = get_ClassFile(static_data);
	static_data_status* s = cfile->static_data_stat;
	if (s != NULL) {
		return s->status;
	}
	return STATIC_DATA_ERROR;
}

/**
 * staticデータの状態を設定する。
 */
void set_static_data_status(jobject static_data, int stat) {
	ClassFile* cfile = get_ClassFile(static_data);
	cfile->static_data_stat->status = stat;
	if (stat == STATIC_DATA_INITIALIZING) {
		cfile->static_data_stat->initializing_thread_id = GetCurrentThreadId();
	} else {
		cfile->static_data_stat->initializing_thread_id = NULL;
	}
}

/**
 * 他のスレッドがstaticデータを初期化中であるかを返す。
 */
bool is_another_thread_initializing(jobject static_data) {
	bool result = false;
	ClassFile* cfile = get_ClassFile(static_data);
	static_data_status* status = cfile->static_data_stat;
	if (status->status == STATIC_DATA_INITIALIZING
			&& status->initializing_thread_id != GetCurrentThreadId()) {
		result = true;
	}
	return result;
}

/**
 * 指定された class_loader 構造体に対応する ClassLoader のインスタンスを返す
 */
jobject get_ClassLoader(class_loader* loader) {
	return loader->instance;
}

static class_loader* allocate_class_loader(jobject cl) {
	class_loader* loader = (class_loader*) calloc(1, sizeof(class_loader));
	if (loader == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	loader->instance = cl;
	loader->lock = (CRITICAL_SECTION*) malloc(sizeof(CRITICAL_SECTION));
	if (loader->lock == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	InitializeCriticalSection(loader->lock);
	
	if (g_verify_mode != VERIFY_MODE_NONE && loader->instance) {
		// ベリファイモードを設定する
		loader->verify = true;
	}

	// ヒープ領域を確保する
	loader->heap = new PermanentHeap(DEFAULT_CLASS_LOADER_HEAP_SIZE);
	if (! loader->heap) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	loader->heap->setMinimumAllocationSize(sizeof(double));	// 適切か？
	return loader;
}

/**
 * 指定されたClassLoaderオブジェクトに対応するclass_loader構造体を返す
 */
class_loader* get_class_loader(jobject cl) {
	if (cl == NULL) {
		// ブートストラップクラスローダ
		return g_bootstrap_class_loader;
	} else {
		class_loader* loader = NULL;
		// クリティカルセクションに入る
		EnterCriticalSection(&g_global_critical_section);
		for (unsigned int i = 0; i < g_class_loaders_count; ++i) {
			class_loader* tmp = g_class_loaders[i];
			if (tmp->instance == cl) {
				loader = tmp;
				break;
			}
		}
		if (loader == NULL) {
			// グローバル配列に追加する
			loader = allocate_class_loader(cl);
			if (loader == NULL) {
				fatal_error(FATAL_ERROR_NO_MEMORY);
			}
			g_class_loaders_count++;
			g_class_loaders = (class_loader**) realloc(g_class_loaders, sizeof(class_loader*) * g_class_loaders_count);
			g_class_loaders[g_class_loaders_count - 1] = loader;
		}

		LeaveCriticalSection(&g_global_critical_section);
		return loader;
	}
}

/**
 * 指定されたバイト配列を解釈し、ClassFile構造体を生成する。
 * 引数 loader で指定されたクラスローダが、定義クラスローダとして記録される。
 */
ClassFile* define_ClassFile(frame* current_frame,
							const java_utf8* name,
							class_loader* loader,
							const char* buff,
							int offset,
							int length) {
	
	EnterCriticalSection(loader->lock);
	
	// ClassCircularityErrorのチェックを行う
	bool circularity_error = false;
	loading_class* c = loader->circularity_info;
	while (c) {
		if (c->name == name) {
			// ClassCircularityError発生
			circularity_error = true;
			throw_exception(current_frame, "java/lang/ClassCircularityError", name);
			break;
		}
		c = c->next;
	}

	ClassFile* cfile = NULL;
	if (! circularity_error) {
		// クラス情報をパースする
		cfile = parse_ClassFile(current_frame, loader, buff + offset, length);

		// クラスを初期化し、クラスローダに登録する
		if (cfile) {
			if (cfile->this_class_name != name) {
				// 名前が違う
				char* msg = (char*) malloc(128);
				if (! msg) {
					fatal_error(FATAL_ERROR_NO_MEMORY);
				}
				_snprintf(msg,
						  128 - 1,
						  "%s (wrong name %s)",
						  name,
						  cfile->this_class_name);
				msg[127] = '\0';
				throw_exception(current_frame, "java/lang/NoClassDefFoundError", msg);
				free(msg);

				free_ClassFile(cfile);
				cfile = NULL;
			} else {
				// ClassCircularityErrorをチェックできるように、クラスローダに一時的に
				// 登録しておく
				loading_class lc;
				lc.name = name;
				lc.next = loader->circularity_info;
				loader->circularity_info = &lc;

				// Linkingを行う
				if (! link_ClassFile(current_frame, loader, cfile)) {
					free_ClassFile(cfile);
				}
			
				// 初期化と登録が完了したので、circularity_info リストからはずす
				loading_class* c = loader->circularity_info;
				assert(c == &lc);
				loader->circularity_info = lc.next;
			}
		}
	}

	LeaveCriticalSection(loader->lock);
	
	return cfile;
}

/**
 * クラスファイルを初期化し、クラスローダに登録する
 * Linkingに相当する処理
 */
static bool link_ClassFile(frame* current_frame, class_loader* loader, ClassFile* cfile) {
	if (! cfile) {
		return true;
	}
	// スーパークラスを再帰的にロードしていく
	ClassFile* superclass = NULL;
	if (cfile->super_class_name != NULL) {
		superclass = load_ClassFile(current_frame, cfile->defining_loader, cfile->super_class_name);
		if (superclass == NULL) {
			return false;
		}
		// ToDo:スーパークラスのメソッドをオーバーライドしている場合、
		// 引数および戻り値のロード制約を記録する
	}

	// スーパーインタフェースを再帰的にロードしていく
	ClassFile** interfaces = (ClassFile**) malloc(sizeof(ClassFile*) * cfile->interfaces_count);
	if (interfaces == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	for (unsigned int i = 0; i < cfile->interfaces_count; ++i) {
		const java_utf8* iname = get_CONSTANT_Class_info(cfile, cfile->interfaces[i]);
		ClassFile* ifile = load_ClassFile(current_frame, cfile->defining_loader, iname);
		if (ifile == NULL) {
			free(interfaces);
			return false;
		}
		interfaces[i] = ifile;
	}

	// ベリファイのパス２を実行する
	if (loader->verify) {
		if (! verify_pass2(current_frame,
						   cfile,
						   superclass,
						   interfaces)) {
			// ベリファイ失敗
			return false;
		}
	}

	// Preparation処理を実行する
	bool prepared = prepare_ClassFile(current_frame, cfile, superclass, interfaces);
	free(interfaces);

	if (! prepared) {
		return false;
	}
	// static_data を割り当てる
	jobject result = alloc_static_data(cfile);
	if (result == NULL) {
		throw_OutOfMemoryError(current_frame);
		return false;
	}
	add_static_data(result);

	// クラスローダ内の配列にClassFileを追加する
	add_ClassFile(loader, cfile);
	
	return true;
}

/**
 * ブートクラスパスを初期化する
 * 一度設定すると、変更できない。（無視される）
 */
void init_boot_classpath(const _TCHAR* classpath) {
	if (g_bootstrap_classpath == NULL) {
		// グローバル変数にコピーしておく
		g_bootstrap_classpath_string = (_TCHAR*) malloc(sizeof(_TCHAR) * (_tcslen(classpath) + 1));
		if (g_bootstrap_classpath_string == NULL) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		_tcscpy(g_bootstrap_classpath_string, classpath);

		// ';' で区切られたパスを読み取る
		char utf8[256];
		_TCHAR tmp[256];
		int tmppos = 0;
		tmp[0] = _T('\0');
		while (*classpath) {
			while (*classpath && *classpath != _T(';') && tmppos < (sizeof(tmp) / sizeof(*tmp)) - 1) {
				tmp[tmppos++] = *classpath++;
			}
			tmp[tmppos] = _T('\0');
			tmppos = 0;
			if (*classpath == _T(';')) {
				classpath++;
			}

			// 実在するパスかどうかを調べる
			WIN32_FIND_DATA find_data;
			HANDLE hFind = FindFirstFile(tmp, &find_data);
			if (hFind != INVALID_HANDLE_VALUE) {
				int len = _tcslen(tmp);
				convert_to_utf8(tmp, -1, utf8, sizeof(utf8) / sizeof(utf8[0]));
				FindClose(hFind);
				classpath_element* elem = NULL;
				if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
					elem = create_classpath_element(utf8, false);
				} else {
					elem = create_classpath_element(utf8, true);
				}
				g_bootstrap_classpath_count++;
				g_bootstrap_classpath
					= (classpath_element**) realloc(g_bootstrap_classpath,
												    sizeof(classpath_element) * g_bootstrap_classpath_count);
				g_bootstrap_classpath[g_bootstrap_classpath_count - 1] = elem;
			}
		}
	}
}

/**
 * ブートクラスパスを返す
 */
const _TCHAR* get_boot_classpath() {
	return g_bootstrap_classpath_string;
}

/**
 * システムクラスパスを初期化する
 * 一度設定すると、変更できない。（無視される）
 */
void init_system_classpath(const _TCHAR* classpath) {
	if (g_system_classpath == NULL) {
		int len = _tcslen(classpath);
		g_system_classpath = (_TCHAR*) malloc(sizeof(_TCHAR) * (len + 1));
		if (g_system_classpath == NULL) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		_tcscpy(g_system_classpath, classpath);
	}
}

/**
 * システムクラスパスを返す
 */
const _TCHAR* get_system_classpath() {
	return g_system_classpath;
}

static classpath_element* create_classpath_element(char* path, bool jar) {
	classpath_element* elem = (classpath_element*) malloc(sizeof(classpath_element));
	if (elem == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	elem->jar = jar;
	elem->zipfile = NULL;
	if (jar) {
		elem->path = (char*) malloc(strlen(path) + 1);
		if (elem->path == NULL) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		strcpy(elem->path, path);
	} else {
		// ディレクトリの場合には、末尾が必ず '\' で終わるようにする
		int len = strlen(path);
		if (path[len - 1] != '\\') {
			elem->path = (char*) malloc(len + 2);
			if (elem->path == NULL) {
				fatal_error(FATAL_ERROR_NO_MEMORY);
			}
			strcpy(elem->path, path);
			elem->path[len] = '\\';
			elem->path[len + 1] = '\0';
		} else {
			elem->path = (char*) malloc(len + 1);
			if (elem->path) {
				fatal_error(FATAL_ERROR_NO_MEMORY);
			}
			strcpy(elem->path, path);
		}
	}
	return elem;
}

/**
 * 指定されたクラスローダを用いて、指定された名前のクラスをロードする
 * 指定されたクラスについての初期化処理（static final フィールドの初期化および<clinit>メソッドの実行）
 * も行われる。
 */
ClassFile* find_ClassFile(frame* frm, class_loader* loader, const java_utf8* class_name) {
	// 例外を一時退避しておく
	jthrowable t = frm->body->current_exception;
	jthrowable global_throwable = NULL;
	if (t) {
		// ガベージコレクトされないようにグローバル参照として保持しておく
		global_throwable = (jthrowable) new_global_reference(t);
	}
	frm->body->current_exception = NULL;

	// ClassFile構造体をロードする
	// この関数呼び出し時点で intern_utf8()されていることを前提にする
	// ClassFile* cfile = load_ClassFile(frm, loader, intern_utf8(class_name));
	ClassFile* cfile = load_ClassFile(frm, loader, class_name);

	if (cfile == NULL) {
		if (! exception_occurred(frm)) {
			// 例外が設定されていない場合は、NoClassDefFoundErrorをthrowする
			throw_exception(frm, "java/lang/NoClassDefFoundError", class_name);
		}
		return NULL;
	}
	if (! ensure_static_data_initialized(frm, cfile)) {
		// 初期化に失敗。ギブアップ
		if (! exception_occurred(frm)) {
			// 例外が設定されていない場合は、NoClassDefFoundErrorをthrowする
			throw_exception(frm, "java/lang/NoClassDefFoundError", class_name);
		}
		return NULL;
	}
	// 例外を復元する
	if (t) {
		frm->body->current_exception = t;
		delete_global_reference(global_throwable);
	}
	return cfile;
}

/**
 * 指定されたClassFile構造体に対応するstaticフィールドが初期化されていることを
 * 保障する
 */
bool ensure_static_data_initialized(frame* current_frame, ClassFile* cfile) {
	if (cfile == NULL) {
		return false;
	}
	
	jobject static_instance = get_static_data(cfile);
	if (static_instance == NULL) {
		assert(false);
		return false;
	}

	// すでに初期化されている場合には true を返す
	int status = get_static_data_status(static_instance);
	if (status == STATIC_DATA_INITIALIZED) {
		return true;
	}
	
	// 1. ロックを取得する
	monitor_enter(static_instance);

	// 2. 他スレッドが初期化中の間wait()する
	while (is_another_thread_initializing(static_instance)) {
		monitor_wait(static_instance, 0);
	}

	// 3. カレント・スレッドが初期化中である場合、再帰的な処理である
	//    ロックを解除して、リターンする
	if (! is_another_thread_initializing(static_instance) && status == STATIC_DATA_INITIALIZING) {
		monitor_exit(static_instance);
		return true;
	}
	
	// 4. すでに初期化されたインスタンスが存在している場合にはそのままリターンする
	status = get_static_data_status(static_instance);
	if (status == STATIC_DATA_INITIALIZED) {
		monitor_exit(static_instance);
		return true;
	}
	
	// 5. エラー状態の場合にはリターンする
	else if (status == STATIC_DATA_ERROR) {
		monitor_exit(static_instance);
		return false;
	}

	// 6. カレントスレッドがこのクラスを初期化中であることを記録し、ロックを解除する
	set_static_data_status(static_instance, STATIC_DATA_INITIALIZING);
	monitor_exit(static_instance);
	
	// すでに例外が発生していた場合は、その例外を保持しておく。
	jthrowable old_exception = exception_occurred(current_frame);

	// ベリファイのパス３(バイトコード・ベリファイヤ）を実行する
	if (needs_verify(get_defining_loader(cfile))) {
		if (! verify_pass3(current_frame, cfile)) {
			// ベリファイのパス３失敗
			// このクラスの初期化に失敗したことを登録する
			set_static_data_status(static_instance, STATIC_DATA_ERROR);
			monitor_enter(static_instance);
			monitor_notify(static_instance, true);	// notifyAll
			monitor_exit(static_instance);
			return false;
		}
	}

	// 7. スーパークラス／スーパーインタフェースの初期化処理を再帰的に実施する
	ClassFile* superClassFile = NULL;
	if (cfile->super_class_name != NULL) {
		superClassFile = find_ClassFile(current_frame,
										get_defining_loader(cfile),
										cfile->super_class_name);
		if (superClassFile == NULL) {
			// 初期化失敗
			// このクラスの初期化に失敗したことを登録する
			set_static_data_status(static_instance, STATIC_DATA_ERROR);
			monitor_enter(static_instance);
			monitor_notify(static_instance, true);	// notifyAll
			monitor_exit(static_instance);
			// 例外を設定してfalseを返す
			throw_exception(current_frame, "java/lang/NoClassDefFoundError", cfile->super_class_name);
			return false;
		}
	}

	// スーパーインタフェースの初期化
	for (u2 i = 0; i < cfile->interfaces_count; ++i) {
		const java_utf8* superinterface_name = get_CONSTANT_Class_info(cfile, cfile->interfaces[i]);
		ClassFile* interfaceClassFile = find_ClassFile(current_frame,
														get_defining_loader(cfile),
														superinterface_name);
		if (interfaceClassFile == NULL) {
			// 初期化不可能
			// このクラスの初期化に失敗したことを登録する
			set_static_data_status(static_instance, STATIC_DATA_ERROR);
			monitor_enter(static_instance);
			monitor_notify(static_instance, true);	// notifyAll
			monitor_exit(static_instance);
			// 例外を設定してfalseを返す
			throw_exception(current_frame, "java/lang/NoClassDefFoundError", superinterface_name);
			return false;
		}
	}

	// 8. クラスの変数初期化子と静的初期化子、インタフェースのフィールド初期化子を
	// 順番に実行する
	// final指定されたstaticフィールドを初期化する
	for (int i = 0; i < cfile->fields_count; ++i) {
		field_info* finfo = &cfile->fields[i];
		if (is_static(finfo->access_flags) && is_final(finfo->access_flags)) {
			// static finalフィールド
			if (finfo->constantvalue_index) {
				jfieldID fid = get_static_field_id(cfile, finfo->name, finfo->descriptor);
				cp_info* cinfo = cfile->constant_pool[finfo->constantvalue_index];
				switch (cfile->constant_pool_tags[finfo->constantvalue_index]) {
				case CONSTANT_Integer:
					{
						PUSH_INT(current_frame, (__int32) cinfo);
						put_static_field(current_frame, static_instance, fid);
						break;
					}
				case CONSTANT_Long:
					{
						// ARMプロセッサ上でも32ビット単位にアクセスされるため、アライメント違反にはならない
						__int64 value = *(__int64*) &cfile->constant_pool[finfo->constantvalue_index];
						PUSH_LONG(current_frame, value);
						put_static_field(current_frame, static_instance, fid);
						break;
					}
				case CONSTANT_Float:
					{
						PUSH_FLOAT(current_frame, *(float*) &cinfo);
						put_static_field(current_frame, static_instance, fid);
						break;
					}

				case CONSTANT_Double:
					{
						// ARMプロセッサ上でも32ビット単位にアクセスされるため、アライメント違反にはならない
						double value = *(double*) &cfile->constant_pool[finfo->constantvalue_index];
						PUSH_DOUBLE(current_frame, value);
						put_static_field(current_frame, static_instance, fid);
						break;
					}

				case CONSTANT_String:
					{
						jobject string_reference;
						if (IS_SPECIAL_POINTER(cinfo)) {
							// jstringが入っている
							string_reference = (jstring) MAKE_NORMAL_POINTER(cinfo);
						} else {
							// java_utf8* が入っている
							java_utf8* utf = (java_utf8*) cinfo;
							string_reference = intern_string(utf, current_frame);
							if (exception_occurred(current_frame)) {
								return false;
							}
							// 値をキャッシュする
							cfile->constant_pool[finfo->constantvalue_index]
										= (cp_info*) MAKE_SPECIAL_POINTER(string_reference);
						}
						PUSH_OBJECT(current_frame, string_reference);
						put_static_field(current_frame, static_instance, fid);
						break;
					}
				}
			}
			// finalでない場合0またはnullに初期化する
			// この処理はメモリ確保時に行われるので、ここで実施する必要はない
		}
	}
	
	// メソッド<clinit>を実行する。
	method_info* clinit_method_info = find_declaring_method_info(cfile, CLINIT_METHOD_NAME, VOID_NOARG_METHOD_DESCRIPTOR);
	if (clinit_method_info != NULL) {
		jobject static_data = get_static_data(cfile);
		invoke_method(current_frame, static_data, cfile, clinit_method_info);
	}

	// 10. 例外処理を行う
	// <clinit>実行中にエラーが発生した場合
	if (old_exception != exception_occurred(current_frame)) {
		set_static_data_status(static_instance, STATIC_DATA_ERROR);
		monitor_enter(static_instance);
		monitor_notify(static_instance, true);	// notifyAll
		monitor_exit(static_instance);

		// ExceptionInInitializerErrorをthrowする
		// ただし、OutOfMemoryErrorの場合には新しくインスタンスを生成せずに、すぐにリターンする
		ClassFile* current_exception_class = get_ClassFile(current_frame->body->current_exception);
		if (! strcmp(current_exception_class->this_class_name, "java/lang/OutOfMemoryError")) {
			return false;
		}

		// いったん例外をクリア
		jthrowable caused_exception = current_frame->body->current_exception;
		current_frame->body->current_exception = NULL;
		ClassFile* eiie_file = find_ClassFile(current_frame,
												intern_utf8("java/lang/ExceptionInInitializerError"));
		if (eiie_file) {
			jthrowable obj = (jthrowable) alloc_object(eiie_file, current_frame);
			if (obj == NULL) {
				return false;
			}
			PUSH_OBJECT(current_frame, obj);
			PUSH_OBJECT(current_frame, caused_exception);
			jmethodID mid = get_method_id(eiie_file,
										  INIT_METHOD_NAME,
										  intern_utf8("(Ljava/lang/Throwable;)V"));
			method_info* minfo = get_method_info(eiie_file, mid);
			invoke_method(current_frame, obj, eiie_file, minfo);

			current_frame->body->current_exception = obj;
		}

		return false;
	} else {
		// 9. 正常に終了したことを確認後、登録する
		set_static_data_status(static_instance, STATIC_DATA_INITIALIZED);
		// 11.クラスをロックし、スレッドに通知する
		monitor_enter(static_instance);
		monitor_notify(static_instance, true);	// notifyAll
		monitor_exit(static_instance);
	}
	return true;
}

/**
 * 指定されたクラス名に対応するclass_loader_element構造体を検索する
 * 二分検索を行うため、以下の前提条件がある。
 *
 * 1. 指定された名前は intern_utf8()で"インターン"されている。
 * 2. elementsの内容は name のアドレス順にソートされている
 *
 * 見つからなかった場合は NULL を返す。
 */
 static class_loader_element* find_class_loader_element(class_loader* l, const java_utf8* name) {
	 class_loader_element** elements = l->elements;
	 const unsigned int count = l->elements_count;

	if (count) {
		// クラス名のポインタでソートされているため、二分探索する
		int start = 0;
		int end = count - 1;
		while (start <= end) {
			int index = start + (end - start) / 2;
			class_loader_element* elem = elements[index];
			const java_utf8* cname = elem->cfile->this_class_name;
			if (cname > name) {
				end = index - 1;
			} else if  (cname < name) {
				start = index + 1;
			} else {
				return elem;
			}
		}
	}
	return NULL;
}

/**
 * 指定されたClassFileを、class_loader_elementの配列に追加する
 * ClassFile.this_class_name のポインタ値順にソートされて格納するため
 * クラス名は「インターン」されていなければならない。、
 */
static void add_ClassFile(class_loader* l, ClassFile* cfile) {
	const unsigned int count = l->elements_count;
	class_loader_element** elements = l->elements;
	
	// 要素の挿入位置を検索する
	int start = 0;
	int end = count ? count - 1 : 0;
	if (count) {
		const java_utf8* name = cfile->this_class_name;
		while (start <= end) {
			int ind = start + (end - start) / 2;
			class_loader_element* elem = elements[ind];
			const java_utf8* cname = elem->cfile->this_class_name;
			if (cname > name) {
				end = ind - 1;
			} else if  (cname < name) {
				start = ind + 1;
			} else {
				// すでに存在しているのでリターン
				return;
			}
		}
	}
	
	// 挿入位置を計算
	int index = start + (end - start) / 2;

	// 必要に応じて配列を拡張する
	l->elements_count++;
	if (l->elements_count >= l->elements_size) {
		l->elements_size += 64;	// 64個ずつ増やす
		l->elements = (class_loader_element**) realloc(l->elements,
														sizeof(class_loader_element*) * l->elements_size);
		if (! l->elements) {
			// メモリ確保失敗
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
	}

	// 挿入位置より後ろを１つずらす
	memmove(&l->elements[index + 1],
			&l->elements[index],
			sizeof(class_loader_element*) * (l->elements_count - index - 1));
	
	// 要素を格納する
	class_loader_element* elem = (class_loader_element*) calloc(sizeof(class_loader_element), 1);
	if (! elem) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	elem->cfile = cfile;
	elem->class_object = NULL;

	l->elements[index] = elem;
}

/**
 * 指定されたクラスローダがロードするクラスに対して
 * ベリファイが必要かどうかを返す。
 */
bool needs_verify(class_loader* loader) {
	return loader->verify;
}

/**
 * ロード制約テーブル上のインデックス値を計算する
 */
static inline int get_loading_constraints_table_index(const java_utf8* class_name) {
	// アドレスの下位３ビットは常に０（のはず）なので、３ビットシフトしてから
	// 余りを求める
	return ((int) class_name >> 3) % LOADING_CONSTRAINTS_TABLE_SIZE;
}

/**
 * ロード制約を追加する。
 * ロード制約の追加に失敗した場合（＝ロード済みクラスの間ですでにロード制約が破られていた場合）
 * falseを返す。
 */
static bool add_loading_constraints(frame* frm, const java_utf8* class_name, class_loader* loader1, class_loader* loader2) {
	EnterCriticalSection(&g_global_critical_section);

	int index = get_loading_constraints_table_index(class_name);
	loading_constraints* c = g_loading_constraints_table[index];
	bool exists = false;
	while (c) {
		if ((c->class_name == class_name)
				&& (c->loader1 == loader1 || c->loader2 == loader1)
				&& (c->loader1 == loader2 || c->loader2 == loader2)) {
			// 該当の制約がすでに存在する
			exists = true;
			break;
		}
		c = c->next;
	}
	
	bool failed = false;
	ClassFile* cfile = NULL;
	// ロード制約がすでに破られていないか調べる
	class_loader_element* e1 = find_class_loader_element(loader1, class_name);
	class_loader_element* e2 = find_class_loader_element(loader2, class_name);
	if (e1 && e2
			&& e1->initiating_loader && e2->initiating_loader
			&& e1->cfile != e2->cfile) {
		// 破られている
		failed = true;
	}

	if (! failed && ! exists) {
		// 新しい制約を登録する
		c = (loading_constraints*) calloc(sizeof(loading_constraints), 1);
		if (! c) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		c->class_name = class_name;
		c->loader1 = loader1;
		c->loader2 = loader2;
		c->next = g_loading_constraints_table[index];
		g_loading_constraints_table[index] = c;

		DBG(_T("Loading constraints added class name="));
		DBG_UTF8(class_name);
		DBG(_T("\n"));
	}

	LeaveCriticalSection(&g_global_critical_section);

	if (failed) {
		// 例外を throw する
		char* msg = (char*) malloc(128);
		if (! msg) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		_snprintf(msg,
				  128 - 1,
				  "Class %s violates loader constraints",
				  class_name);
		msg[127] = '\0';
		throw_exception(frm, "java/lang/LinkageError", msg);
		free(msg);
	}

	return ! failed;
}

/**
 * 指定されたロード制約の妥当性をチェックする。
 */
static bool check_loading_constraints(frame* frm, const java_utf8* class_name, class_loader* loader, ClassFile* cfile) {
	// テーブルのインデックスを求める
	int index = get_loading_constraints_table_index(class_name);
	loading_constraints* c = g_loading_constraints_table[index];
	bool failed = false;
	while (c) {
		if ((c->class_name == class_name)
				&& (c->loader1 == loader || c->loader2 == loader)) {
			class_loader* opposite = (c->loader1 == loader) ? c->loader2 : c->loader1;
			class_loader_element* elem = find_class_loader_element(opposite, class_name);
			if (elem && elem->initiating_loader && elem->cfile != cfile) {
				// チェック失敗
				failed = true;
			}
			break;
		}
		c = c->next;
	}
	if (failed) {
		char* msg = (char*) malloc(128);
		if (! msg) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		_snprintf(msg,
				  128 - 1,
				  "Class %s violates loader constraints",
				  class_name);
		msg[127] = '\0';
		throw_exception(frm, "java/lang/LinkageError", msg);
		free(msg);

	}
	return ! failed;
}

/**
 * 指定されたフィールドディスクリプタに関するロード制約を追加する
 */
bool add_loading_constraints_of_field(frame* current_frame, ClassFile* declaring_ClassFile, const java_utf8* descriptor) {
	class_loader* loader1 = get_defining_loader(current_frame->current_class_file);
	class_loader* loader2 = get_defining_loader(declaring_ClassFile);
	if (loader1 != loader2) {
		// フィールドの型についてロード制約を追加する
		if (descriptor[0] == '[' || descriptor[0] == 'L') {
			if (descriptor[0] == 'L') {
				descriptor = intern_utf8(&descriptor[1], strlen(descriptor) - 2);
			}
			if (! add_loading_constraints(current_frame,
										  descriptor,
										  loader1,
										  loader2)) {
				// 制約の追加に失敗
				return false;
			}
		}
	}
	return true;
}

/**
 * 指定されたメソッドディスクリプタに関するロード制約を追加する
 */
bool add_loading_constraints_of_method(frame* current_frame,
									   class_loader* loader1,
									   class_loader* loader2,
									   const java_utf8* descriptor) {
	if (loader1 != loader2) {
		descriptor++;
		// メソッドの引数と戻り値に関してロード制約を追加する
		char c;
		while ((c = *descriptor++)) {
			switch (c) {
			case ')':
				break;
			
			case '[':
				{
					const char* start = descriptor - 1;
					while (*descriptor == '[') {
						descriptor++;
					}
					if (*descriptor == 'L') {
						// 要素は参照型
						while (*descriptor != ';') {
							// クラス名の終わりまでスキップ
							descriptor++;
						}
						// ';' も含める必要がある
						descriptor++;

					} else {
						// 要素はプリミティブ型
						descriptor++;
					}
					const java_utf8* param_name = intern_utf8(start, descriptor - start);
					if (! add_loading_constraints(current_frame,
												  param_name,
												  loader1,
												  loader2)) {
						// 制約の追加に失敗
						return false;
					}
				}
				break;

			case 'L':
				{
					const char* start = descriptor;
					while (*descriptor != ';') {
						descriptor++;
					}
					const java_utf8* param_name = intern_utf8(start, descriptor - start);
					if (! add_loading_constraints(current_frame,
												  param_name,
												  loader1,
												  loader2)) {
						// 制約の追加に失敗
						return false;
					}
				}
				break;

			default:
				break;
			}
		}
	}
	return true;
}

/**
 * 指定されたメソッドディスクリプタに関するロード制約を追加する
 */
bool add_loading_constraints_of_method(frame* current_frame,
									   ClassFile* declaring_ClassFile,
									   const java_utf8* descriptor) {
	if (! declaring_ClassFile) {
		// 追加の必要なし
		return true;
	}
	class_loader* loader1 = get_defining_loader(current_frame->current_class_file);
	class_loader* loader2 = get_defining_loader(declaring_ClassFile);
	
	return add_loading_constraints_of_method(current_frame, loader1, loader2, descriptor);
}

PermanentHeap* get_heap_of_class_loader(class_loader* loader) {
	return loader->heap;
}