/* jni_funcs.cpp
   Copyright (C) 2005-2006 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 <jni.h>
#include "ClassFile.h"
#include "class_loader.h"
#include "java_utf8.h"
#include "java_object.h"
#include "jni_funcs.h"
#include "instruction.h"
#include "java_thread.h"
#include "common_funcs.h"
#include "console.h"

#include <com_mysaifu_jvm_io_VMConsoleInputStream.h>
#include <com_mysaifu_jvm_io_VMConsoleOutputStream.h>
#include <gnu_classpath_VMStackWalker.h>
#include <gnu_classpath_VMSystemProperties.h>
#include <java_io_VMFile.h>
#include <java_lang_ref_Reference.h>
#include <java_lang_reflect_Constructor.h>
#include <java_lang_reflect_Field.h>
#include <java_lang_reflect_Method.h>
#include <java_lang_VMClass.h>
#include <java_lang_VMClassLoader.h>
#include <java_lang_VMDouble.h>
#include <java_lang_VMFinalizeThread.h>
#include <java_lang_VMFloat.h>
#include <java_lang_VMMainThread.h>
#include <java_lang_VMObject.h>
#include <java_lang_VMProcess.h>
#include <java_lang_VMRuntime.h>
#include <java_lang_VMString.h>
#include <java_lang_VMSystem.h>
#include <java_lang_VMThread.h>
#include <java_lang_VMThrowable.h>
#include <java_util_VMTimeZone.h>

#include <java_util_concurrent_atomic_AtomicLong.h>
#include <sun_misc_Unsafe.h>

/**
 * JNIEnvに frame 情報を追加した構造体
 */
struct jni_env {
	/**
	 * JNIEnv 構造体
	 */
	JNIEnv env;

	/**
	 * フレーム
	 */
	frame* jni_frame;
};

/**
 * RegisterNativesで指定された関数のポインタを保管する構造体
 */
struct native_method_info {
	class_loader* loader;				// 対応するクラスローダ
	const java_utf8* class_name;		// クラス名
	JNINativeMethod* native_method;		// RegisterNativesで渡された構造体
};

/**
 * ネイティブメソッドの情報
 */
static int g_native_methods_count = 0;
static int g_native_methods_limit = 0;
static native_method_info** g_native_methods;

/**
 * java.lang.Class.vmdata フィールド
 */
static jfieldID g_Class_vmdata_id;

/**
 * JNI関数へのポインタ
 */
static JNINativeInterface_ g_functions;

/**
 * JavaVMへのポインタ
 */
static _Jv_JavaVM g_java_vm;

/**
 * JAVA_HOME
 */
static _TCHAR* g_java_home;

/**
 * 現在のarguments
 */
static arguments* g_current_arguments;

/**
 * ネイティブメソッドに渡すことのできる最大パラメータ数
 */
//#define MAX_PARAMETERS_COUNT	16
#define MAX_PARAMETERS_COUNT	24

/**
 * ネイティブメソッドに渡す引数を入れる構造体
 */
struct native_method_parameters {
	__int32 values[MAX_PARAMETERS_COUNT + 2];	// JNIEnvとjclass(jobject)分を足している
};

/**
 * ネイティブメソッド呼び出しの引数種別
 */
// 引数は va_list として渡される。
enum native_argtype_t {
	// 引数は va_list として渡される。
	VA_LIST,

	// 引数は jvalue として渡される。
	JVALUE
};

/**
 * スタックトレースの最大数
 */
#define MAX_STACK_TRACE_ELEMENTS	128

/**
 * ローカル参照の数
 */
#define DEFAULT_LOCAL_REFERENCES_COUNT	16
 
 /**
 * 今までにロードしたDLLの数
 */
static unsigned int g_dlls_count;
static unsigned int g_dlls_limit;

/**
 * ロードしたDLLのHMODULE
 */
static HMODULE*	g_hmodules;

static jobject jni_new_object_common(JNIEnv *env, jclass clazz, jmethodID methodID, native_argtype_t argtype, const void* args);

static int create_native_method_name(const TCHAR* java_name, TCHAR* buff);
static bool init_native_method_parameters(native_method_parameters* params,
										  u2 parameters_count,
										  jni_env* env,
										  ClassFile*,
										  method_info*);
static bool add_native_method_info(class_loader* loader, const char* class_name, const JNINativeMethod* method);

static jobject get_static_data(JNIEnv* env, jclass clazz);

static jobjectArray create_parameter_types(JNIEnv* env, const char* sig, class_loader* lc);
static void convert_to_jvalues(JNIEnv* env, ClassFile* target_cfile, const java_utf8* method_desc, jobjectArray args, jvalue* values);
static jclass load_class(JNIEnv* env, jstring name);
static jclass find_class(JNIEnv* env, const char* name, class_loader* l);
static jni_env* get_jni_env(frame* current_frame, frame* new_local_frame);
static void release_jni_env(jni_env* env);

static void get_Constructor_data(JNIEnv* env, jobject constructor, ClassFile** defining, method_info** minfo);
static void get_Method_data(JNIEnv* env, jobject method, ClassFile** defining, method_info** minfo);
static void get_Field_data(JNIEnv* env, jobject field, jclass* pdeclaring, field_info** pfinfo, jfieldID* pfid);

static bool is_primitve_class_name(const java_utf8* name);

static inline void jni_call_method_common(JNIEnv *env, jobject obj, jmethodID methodID, native_argtype_t args_type, const void* args);
static inline void jni_call_nonvirtual_method_common(JNIEnv *env, jobject obj, ClassFile* cfile, jmethodID methodID, native_argtype_t args_type, const void* args);
static inline void jni_call_static_method_common(JNIEnv *env, jclass clazz, jmethodID methodID, native_argtype_t args_type, const void* args);
static inline __int32 jni_call_int32_method(JNIEnv *env, jobject obj, jmethodID methodID, native_argtype_t type, const void* args);
static inline __int64 JNICALL jni_call_int64_method(JNIEnv *env, jobject obj, jmethodID methodID, native_argtype_t type, const void* args);

static bool unwrap_object(JNIEnv* env, char sig, jobject value, jvalue* unwrapped);

static jobjectArray create_argv(JNIEnv* env, const _TCHAR* args);

static void push_parameters(const java_utf8* method_desc, frame* current_frame, va_list args);
static void push_parameters(const java_utf8* method_desc, frame* current_frame, jvalue* args);

// 特別扱いするメソッド（JVM内部情報にアクセスするネイティブメソッド）

/**
 * 特別なメソッド
 */
struct special_method {
	/**
	 * クラス名
	 */
	const char* class_name;

	/**
	 * メソッド名
	 */
	const char* method_name;

	/**
	 * メソッドディスクリプタ
	 */
	const char* method_descriptor;

	/**
	 * 関数のポインタ
	 */
	void* func_ptr;
};

/**
 * 特別なメソッドの配列
 * Windows CEでは、EXEファイル内にエクスポートテーブルがあったとしても、
 * GetProcAddress()でその関数のアドレスを取得することができない（らしい）
 * ToDo: JVM全体をDLL化し、この処理を簡略化する
 */
static special_method g_special_methods[] = {
	{ "com/mysaifu/jvm/io/VMConsoleInputStream", "readNative",	"([B)I",	Java_com_mysaifu_jvm_io_VMConsoleInputStream_readNative },
	{ "com/mysaifu/jvm/io/VMConsoleOutputStream", "writeNative",	"([BII)V",	Java_com_mysaifu_jvm_io_VMConsoleOutputStream_writeNative },

	{ "gnu/classpath/VMStackWalker", "getClassContext",		"()[Ljava/lang/Class;",	Java_gnu_classpath_VMStackWalker_getClassContext },
	{ "gnu/classpath/VMStackWalker", "getClassLoader",		"(Ljava/lang/Class;)Ljava/lang/ClassLoader;",	Java_gnu_classpath_VMStackWalker_getClassLoader },

	{ "gnu/classpath/VMSystemProperties", "getJavaHome",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getJavaHome },
	{ "gnu/classpath/VMSystemProperties", "getTempDir",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getTempDir },
	{ "gnu/classpath/VMSystemProperties", "getJVMVersion",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getJVMVersion },
	{ "gnu/classpath/VMSystemProperties", "getUserName",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getUserName },
	{ "gnu/classpath/VMSystemProperties", "getUserHome",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getUserHome },
	{ "gnu/classpath/VMSystemProperties", "getOSName",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getOSName },
	{ "gnu/classpath/VMSystemProperties", "getOSArch",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getOSArch },
	{ "gnu/classpath/VMSystemProperties", "getOSVersion",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getOSVersion },
	{ "gnu/classpath/VMSystemProperties", "getDefaultEncoding",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getDefaultEncoding },
	{ "gnu/classpath/VMSystemProperties", "getCurrentDirectory",		"()Ljava/lang/String;",	Java_gnu_classpath_VMSystemProperties_getCurrentDirectory },

	{ "gnu/classpath/VMSystemProperties", "postInit",		"(Ljava/util/Properties;)V",	Java_gnu_classpath_VMSystemProperties_postInit },

	{ "java/lang/VMClass", "isInstance",		"(Ljava/lang/Class;Ljava/lang/Object;)Z",	Java_java_lang_VMClass_isInstance },
	{ "java/lang/VMClass", "isAssignableFrom",	"(Ljava/lang/Class;Ljava/lang/Class;)Z",	Java_java_lang_VMClass_isAssignableFrom },
	{ "java/lang/VMClass", "isInterface",		"(Ljava/lang/Class;)Z",						Java_java_lang_VMClass_isInterface },
	{ "java/lang/VMClass", "isPrimitive",		"(Ljava/lang/Class;)Z",						Java_java_lang_VMClass_isPrimitive },
	{ "java/lang/VMClass", "getName",			"(Ljava/lang/Class;)Ljava/lang/String;",	Java_java_lang_VMClass_getName },
	{ "java/lang/VMClass", "getSuperclass",		"(Ljava/lang/Class;)Ljava/lang/Class;",		Java_java_lang_VMClass_getSuperclass },
	{ "java/lang/VMClass", "getInterfaces",		"(Ljava/lang/Class;)[Ljava/lang/Class;",	Java_java_lang_VMClass_getInterfaces },
	{ "java/lang/VMClass", "getComponentType",	"(Ljava/lang/Class;)Ljava/lang/Class;",		Java_java_lang_VMClass_getComponentType },
	{ "java/lang/VMClass", "getModifiers",		"(Ljava/lang/Class;Z)I",					Java_java_lang_VMClass_getModifiers },
	{ "java/lang/VMClass", "getDeclaringClass",	"(Ljava/lang/Class;)Ljava/lang/Class;",		Java_java_lang_VMClass_getDeclaringClass },
	{ "java/lang/VMClass", "getDeclaredClasses","(Ljava/lang/Class;Z)[Ljava/lang/Class;",	Java_java_lang_VMClass_getDeclaredClasses },
	{ "java/lang/VMClass", "getDeclaredFields",	"(Ljava/lang/Class;Z)[Ljava/lang/reflect/Field;",	Java_java_lang_VMClass_getDeclaredFields },
	{ "java/lang/VMClass", "getDeclaredMethods","(Ljava/lang/Class;Z)[Ljava/lang/reflect/Method;",	Java_java_lang_VMClass_getDeclaredMethods },
	{ "java/lang/VMClass", "getDeclaredConstructors","(Ljava/lang/Class;Z)[Ljava/lang/reflect/Constructor;",	Java_java_lang_VMClass_getDeclaredConstructors },
	{ "java/lang/VMClass", "getClassLoader",	"(Ljava/lang/Class;)Ljava/lang/ClassLoader;",	Java_java_lang_VMClass_getClassLoader },
	{ "java/lang/VMClass", "forName",			"(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;",	Java_java_lang_VMClass_forName },
	{ "java/lang/VMClass", "isArray",			"(Ljava/lang/Class;)Z",						Java_java_lang_VMClass_isArray },
	{ "java/lang/VMClass", "throwException",	"(Ljava/lang/Throwable;)V",					Java_java_lang_VMClass_throwException },
	{ "java/lang/VMClass", "getDeclaredAnnotations","(Ljava/lang/Class;)[Ljava/lang/annotation/Annotation;",	Java_java_lang_VMClass_getDeclaredAnnotations },
	{ "java/lang/VMClass", "getEnclosingClass","(Ljava/lang/Class;)Ljava/lang/Class;",	Java_java_lang_VMClass_getEnclosingClass },
	{ "java/lang/VMClass", "getEnclosingConstructor","(Ljava/lang/Class;)Ljava/lang/reflect/Constructor;",	Java_java_lang_VMClass_getEnclosingConstructor },
	{ "java/lang/VMClass", "getEnclosingMethod","(Ljava/lang/Class;)Ljava/lang/reflect/Method;",	Java_java_lang_VMClass_getEnclosingMethod },
	{ "java/lang/VMClass", "getClassSignature","(Ljava/lang/Class;)Ljava/lang/String;",	Java_java_lang_VMClass_getClassSignature },
	{ "java/lang/VMClass", "isAnonymousClass","(Ljava/lang/Class;)Z",	Java_java_lang_VMClass_isAnonymousClass },
	{ "java/lang/VMClass", "isLocalClass","(Ljava/lang/Class;)Z",	Java_java_lang_VMClass_isLocalClass },
	{ "java/lang/VMClass", "isMemberClass","(Ljava/lang/Class;)Z",	Java_java_lang_VMClass_isMemberClass },



	{ "java/lang/VMClassLoader", "defineClass",	"(Ljava/lang/ClassLoader;Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;",	Java_java_lang_VMClassLoader_defineClass },
	{ "java/lang/VMClassLoader", "resolveClass",	"(Ljava/lang/Class;)V",	Java_java_lang_VMClassLoader_resolveClass },
	{ "java/lang/VMClassLoader", "loadClass",	"(Ljava/lang/String;Z)Ljava/lang/Class;",	Java_java_lang_VMClassLoader_loadClass },
	{ "java/lang/VMClassLoader", "getPrimitiveClass",	"(C)Ljava/lang/Class;",	Java_java_lang_VMClassLoader_getPrimitiveClass },
	{ "java/lang/VMClassLoader", "defaultAssertionStatus",	"()Z",	Java_java_lang_VMClassLoader_defaultAssertionStatus },
	{ "java/lang/VMClassLoader", "findLoadedClass",			"(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;", Java_java_lang_VMClassLoader_findLoadedClass },

	{ "java/lang/VMMainThread", "showConsole",	"()V",	Java_java_lang_VMMainThread_showConsole },

	{ "java/lang/VMObject", "getClass",			"(Ljava/lang/Object;)Ljava/lang/Class;",	Java_java_lang_VMObject_getClass },
	{ "java/lang/VMObject", "clone",			"(Ljava/lang/Cloneable;)Ljava/lang/Object;",	Java_java_lang_VMObject_clone },
	{ "java/lang/VMObject", "notify",			"(Ljava/lang/Object;)V",					Java_java_lang_VMObject_notify },
	{ "java/lang/VMObject", "notifyAll",		"(Ljava/lang/Object;)V",					Java_java_lang_VMObject_notifyAll },
	{ "java/lang/VMObject", "wait",				"(Ljava/lang/Object;JI)V",					Java_java_lang_VMObject_wait },

	{ "java/lang/VMRuntime", "availableProcessors",		"()I",	Java_java_lang_VMRuntime_availableProcessors },
	{ "java/lang/VMRuntime", "freeMemory",		"()J",	Java_java_lang_VMRuntime_freeMemory },
	{ "java/lang/VMRuntime", "totalMemory",		"()J",	Java_java_lang_VMRuntime_totalMemory },
	{ "java/lang/VMRuntime", "maxMemory",		"()J",	Java_java_lang_VMRuntime_maxMemory },
	{ "java/lang/VMRuntime", "gc",				"()V",	Java_java_lang_VMRuntime_gc },
	{ "java/lang/VMRuntime", "runFinalization",		"()V",	Java_java_lang_VMRuntime_runFinalization },
	{ "java/lang/VMRuntime", "runFinalizationForExit",		"()V",	Java_java_lang_VMRuntime_runFinalizationForExit },
	{ "java/lang/VMRuntime", "traceInstructions",		"(Z)V",	Java_java_lang_VMRuntime_traceInstructions },
	{ "java/lang/VMRuntime", "traceMethodCalls",		"(Z)V",	Java_java_lang_VMRuntime_traceMethodCalls },
	{ "java/lang/VMRuntime", "runFinalizersOnExit",		"(Z)V",	Java_java_lang_VMRuntime_runFinalizersOnExit },
	{ "java/lang/VMRuntime", "exit",		"(I)V",	Java_java_lang_VMRuntime_exit },
	{ "java/lang/VMRuntime", "nativeLoad",		"(Ljava/lang/String;Ljava/lang/ClassLoader;)I",	Java_java_lang_VMRuntime_nativeLoad },

	{ "java/lang/VMString", "intern",			"(Ljava/lang/String;)Ljava/lang/String;",	Java_java_lang_VMString_intern },

	{ "java/lang/VMSystem", "arraycopy",		"(Ljava/lang/Object;ILjava/lang/Object;II)V",	Java_java_lang_VMSystem_arraycopy },
	{ "java/lang/VMSystem", "identityHashCode",		"(Ljava/lang/Object;)I",	Java_java_lang_VMSystem_identityHashCode },

	{ "java/lang/VMThread", "start",			"(J)V",										Java_java_lang_VMThread_start },
	{ "java/lang/VMThread", "interrupt",		"()V",										Java_java_lang_VMThread_interrupt },
	{ "java/lang/VMThread", "isInterrupted",	"()Z",										Java_java_lang_VMThread_isInterrupted },
	{ "java/lang/VMThread", "suspend",			"()V",										Java_java_lang_VMThread_suspend },
	{ "java/lang/VMThread", "resume",			"()V",										Java_java_lang_VMThread_resume },
	{ "java/lang/VMThread", "nativeSetPriority","(I)V",										Java_java_lang_VMThread_nativeSetPriority },
	{ "java/lang/VMThread", "nativeStop",		"(Ljava/lang/Throwable;)V",					Java_java_lang_VMThread_nativeStop },
	{ "java/lang/VMThread", "currentThread",	"()Ljava/lang/Thread;",						Java_java_lang_VMThread_currentThread },
	{ "java/lang/VMThread", "yield",			"()V",										Java_java_lang_VMThread_yield },
	{ "java/lang/VMThread", "interrupted",		"()Z",										Java_java_lang_VMThread_interrupted },
	{ "java/lang/VMThread", "holdsLock",		"(Ljava/lang/Object;)Z",					Java_java_lang_VMThread_holdsLock },

	{ "java/lang/VMThrowable", "fillInStackTrace", "(Ljava/lang/Throwable;)Ljava/lang/VMThrowable;", Java_java_lang_VMThrowable_fillInStackTrace },
	{ "java/lang/VMThrowable", "getStackTrace", "(Ljava/lang/Throwable;)[Ljava/lang/StackTraceElement;", Java_java_lang_VMThrowable_getStackTrace },

	{ "java/lang/ref/Reference",       "setType",           "(I)V", Java_java_lang_ref_Reference_setType },

	{ "java/lang/reflect/Constructor", "getModifiersInternal",		"()I",							Java_java_lang_reflect_Constructor_getModifiersInternal },
	{ "java/lang/reflect/Constructor", "getParameterTypes",		"()[Ljava/lang/Class;",		Java_java_lang_reflect_Constructor_getParameterTypes },
	{ "java/lang/reflect/Constructor", "getExceptionTypes",		"()[Ljava/lang/Class;",		Java_java_lang_reflect_Constructor_getExceptionTypes },
	{ "java/lang/reflect/Constructor", "constructNative",	"([Ljava/lang/Object;Ljava/lang/Class;I)Ljava/lang/Object;",							Java_java_lang_reflect_Constructor_constructNative },
	{ "java/lang/reflect/Constructor", "getSignature",		"()Ljava/lang/String;",							Java_java_lang_reflect_Constructor_getSignature },
	{ "java/lang/reflect/Constructor", "getParameterAnnotaions", "()[[Ljava/lang/annotation/Annotation;", Java_java_lang_reflect_Constructor_getParameterAnnotations },

	{ "java/lang/reflect/Field", "getModifiersInternal",	"()I",	Java_java_lang_reflect_Field_getModifiersInternal },
	{ "java/lang/reflect/Field", "getType",			"()Ljava/lang/Class;",	Java_java_lang_reflect_Field_getType },
	{ "java/lang/reflect/Field", "get",			"(Ljava/lang/Object;)Ljava/lang/Object;",	Java_java_lang_reflect_Field_get },
	{ "java/lang/reflect/Field", "getBoolean",			"(Ljava/lang/Object;)Z",	Java_java_lang_reflect_Field_getBoolean },
	{ "java/lang/reflect/Field", "getByte",			"(Ljava/lang/Object;)B",	Java_java_lang_reflect_Field_getByte },
	{ "java/lang/reflect/Field", "getChar",			"(Ljava/lang/Object;)C",	Java_java_lang_reflect_Field_getChar },
	{ "java/lang/reflect/Field", "getShort",		"(Ljava/lang/Object;)S",	Java_java_lang_reflect_Field_getShort },
	{ "java/lang/reflect/Field", "getInt",		"(Ljava/lang/Object;)I",	Java_java_lang_reflect_Field_getInt },
	{ "java/lang/reflect/Field", "getLong",		"(Ljava/lang/Object;)J",	Java_java_lang_reflect_Field_getLong },
	{ "java/lang/reflect/Field", "getFloat",		"(Ljava/lang/Object;)F",	Java_java_lang_reflect_Field_getFloat },
	{ "java/lang/reflect/Field", "getDouble",		"(Ljava/lang/Object;)D",	Java_java_lang_reflect_Field_getDouble },
	{ "java/lang/reflect/Field", "set",			"(Ljava/lang/Object;Ljava/lang/Object;)V",	Java_java_lang_reflect_Field_set },
	{ "java/lang/reflect/Field", "setBoolean",	"(Ljava/lang/Object;Z)V",	Java_java_lang_reflect_Field_setBoolean },
	{ "java/lang/reflect/Field", "setByte",		"(Ljava/lang/Object;B)V",	Java_java_lang_reflect_Field_setByte },
	{ "java/lang/reflect/Field", "setChar",		"(Ljava/lang/Object;C)V",	Java_java_lang_reflect_Field_setChar },
	{ "java/lang/reflect/Field", "setShort",	"(Ljava/lang/Object;S)V",	Java_java_lang_reflect_Field_setShort },
	{ "java/lang/reflect/Field", "setInt",		"(Ljava/lang/Object;I)V",	Java_java_lang_reflect_Field_setInt },
	{ "java/lang/reflect/Field", "setLong",		"(Ljava/lang/Object;J)V",	Java_java_lang_reflect_Field_setLong },
	{ "java/lang/reflect/Field", "setFloat",	"(Ljava/lang/Object;F)V",	Java_java_lang_reflect_Field_setFloat },
	{ "java/lang/reflect/Field", "setDouble",	"(Ljava/lang/Object;D)V",	Java_java_lang_reflect_Field_setDouble },
	{ "java/lang/reflect/Field", "getSignature",	"()Ljava/lang/String;",	Java_java_lang_reflect_Field_getSignature },

	{ "java/lang/reflect/Method", "getModifiersInternal",	"()I",	Java_java_lang_reflect_Method_getModifiersInternal },
	{ "java/lang/reflect/Method", "getReturnType",	"()Ljava/lang/Class;",	Java_java_lang_reflect_Method_getReturnType },
	{ "java/lang/reflect/Method", "getParameterTypes",	"()[Ljava/lang/Class;",	Java_java_lang_reflect_Method_getParameterTypes },
	{ "java/lang/reflect/Method", "getExceptionTypes",	"()[Ljava/lang/Class;",	Java_java_lang_reflect_Method_getExceptionTypes },
	{ "java/lang/reflect/Method", "invokeNative",	"(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;I)Ljava/lang/Object;",	Java_java_lang_reflect_Method_invokeNative },
	{ "java/lang/reflect/Method", "getSignature",	"()Ljava/lang/String;",	Java_java_lang_reflect_Method_getSignature },
	{ "java/lang/reflect/Method", "getDefaultValue",	"()Ljava/lang/Object;",	Java_java_lang_reflect_Method_getDefaultValue },
	{ "java/lang/reflect/Method", "getParameterAnnotaions", "()[[Ljava/lang/annotation/Annotation;", Java_java_lang_reflect_Method_getParameterAnnotations },

	{ "java/lang/VMFinalizeThread", "run", "()V", Java_java_lang_VMFinalizeThread_run },

	{ "java/util/concurrent/atomic/AtomicLong", "VMSupportsCS8", "()Z", Java_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8 },

	{ "sun/misc/Unsafe", "objectFieldOffset",    "(Ljava/lang/reflect/Field;)J",                               Java_sun_misc_Unsafe_objectFieldOffset },
	{ "sun/misc/Unsafe", "compareAndSwapInt",    "(Ljava/lang/Object;JII)Z",                                   Java_sun_misc_Unsafe_compareAndSwapInt },
	{ "sun/misc/Unsafe", "compareAndSwapLong",   "(Ljava/lang/Object;JJJ)Z",                                   Java_sun_misc_Unsafe_compareAndSwapLong },
	{ "sun/misc/Unsafe", "compareAndSwapObject", "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", Java_sun_misc_Unsafe_compareAndSwapObject },
	{ "sun/misc/Unsafe", "putOrderedInt",        "(Ljava/lang/Object;JI)V",                                    Java_sun_misc_Unsafe_putOrderedInt },
	{ "sun/misc/Unsafe", "putOrderedLong",       "(Ljava/lang/Object;JJ)V",                                    Java_sun_misc_Unsafe_putOrderedLong },
	{ "sun/misc/Unsafe", "putOrderedObject",     "(Ljava/lang/Object;JLjava/lang/Object;)V",                   Java_sun_misc_Unsafe_putOrderedObject },
	{ "sun/misc/Unsafe", "putIntVolatile",       "(Ljava/lang/Object;JI)V",                                    Java_sun_misc_Unsafe_putIntVolatile },
	{ "sun/misc/Unsafe", "getIntVolatile",       "(Ljava/lang/Object;J)I",                                     Java_sun_misc_Unsafe_getIntVolatile },
	{ "sun/misc/Unsafe", "putLongVolatile",      "(Ljava/lang/Object;JJ)V",                                    Java_sun_misc_Unsafe_putLongVolatile },
	{ "sun/misc/Unsafe", "putLong",              "(Ljava/lang/Object;JJ)V",                                    Java_sun_misc_Unsafe_putLong },
	{ "sun/misc/Unsafe", "getLongVolatile",      "(Ljava/lang/Object;J)J",                                     Java_sun_misc_Unsafe_getLongVolatile },
	{ "sun/misc/Unsafe", "getLong",              "(Ljava/lang/Object;J)J",                                     Java_sun_misc_Unsafe_getLong },
	{ "sun/misc/Unsafe", "putObjectVolatile",    "(Ljava/lang/Object;JLjava/lang/Object;)V",                   Java_sun_misc_Unsafe_putObjectVolatile },
	{ "sun/misc/Unsafe", "putObject",            "(Ljava/lang/Object;JLjava/lang/Object;)V",                   Java_sun_misc_Unsafe_putObject },
	{ "sun/misc/Unsafe", "getObjectVolatile",    "(Ljava/lang/Object;J)Ljava/lang/Object;",                    Java_sun_misc_Unsafe_getObjectVolatile },
	{ "sun/misc/Unsafe", "arrayBaseOffset",      "(Ljava/lang/Class;)I",                                       Java_sun_misc_Unsafe_arrayBaseOffset },
	{ "sun/misc/Unsafe", "arrayIndexScale",      "(Ljava/lang/Class;)I",                                       Java_sun_misc_Unsafe_arrayIndexScale },
	{ "sun/misc/Unsafe", "unpark",               "(Ljava/lang/Object;)V",                                      Java_sun_misc_Unsafe_unpark },
	{ "sun/misc/Unsafe", "park",                 "(ZJ)V",                                                      Java_sun_misc_Unsafe_park },

};

/**
 * アサーションステータス
 */
bool g_assertion_status;

/**
 * GetVersion本体
 */
jint JNICALL jni_get_version(JNIEnv *env);

/**
 * DefineClassの本体
 */
jclass JNICALL jni_define_class(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len);

/**
 * FindClass本体
 */
jclass JNICALL jni_find_class(JNIEnv *env, const char *name);

/**
 * FromReflectedMethodの本体
 */
jmethodID jni_from_reflected_method(JNIEnv *env, jobject method); 

/**
 * FromReflectedFieldの本体
 */
jfieldID jni_from_reflected_field(JNIEnv *env, jobject field); 

/**
 * GetSuperClassの本体
 */
jclass JNICALL jni_get_superclass(JNIEnv *env, jclass sub);

/**
 * IsAssignableFromの本体
 */
jboolean JNICALL jni_is_assignable_from(JNIEnv *env, jclass sub, jclass sup);

/**
 * Throw本体
 */
jint JNICALL jni_throw(JNIEnv *env, jthrowable obj);

/**
 * ThrowNew本体
 */
jint JNICALL jni_throw_new(JNIEnv *env, jclass clazz, const char *msg);

/**
 * ExceptionOccurred本体
 */
jthrowable JNICALL jni_exception_occurred(JNIEnv *env);

/**
 * ExceptionDescribe本体
 */
void JNICALL jni_exception_describe(JNIEnv *env);

/**
 * ExceptionClear本体
 */
void JNICALL jni_exception_clear(JNIEnv *env);

/**
 * FatalError本体
 */
void JNICALL jni_fatal_error(JNIEnv *env, const char *msg);

/**
 * NewGlobalRef本体
 */
jobject JNICALL jni_new_global_ref(JNIEnv *env, jobject lobj);

/**
 * DeleteGlobalRef本体
 */
void JNICALL jni_delete_global_ref(JNIEnv *env, jobject gref);

/**
 * DeleteGlobalRef本体
 */
void JNICALL jni_delete_local_ref(JNIEnv *env, jobject obj);

/**
 * IsSameObject本体
 */
jboolean JNICALL jni_is_same_object(JNIEnv *env, jobject obj1, jobject obj2);

/**
 * NewLocalRef本体
 */
jobject JNICALL jni_new_local_ref(JNIEnv *env, jobject ref);

/**
 * EnsureLocalCapacity本体
 */
jint JNICALL jni_ensure_local_capacity(JNIEnv *env, jint capacity);

/**
 * AllocObject本体
 */
jobject JNICALL jni_alloc_object(JNIEnv *env, jclass clazz);

/**
 * NewObject本体
 */
jobject JNICALL jni_new_object(JNIEnv *env, jclass clazz, jmethodID methodID, ...);

/**
 * NewObjectV本体
 */
jobject JNICALL jni_new_object_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);

/**
 * NewObjectA本体
 */
jobject JNICALL jni_new_object_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);

/**
 * GetObjectClass本体
 */
jclass JNICALL jni_get_object_class(JNIEnv *env, jobject obj);

/**
 * IsInstanceOf本体
 */
jboolean JNICALL jni_is_instance_of(JNIEnv *env, jobject obj, jclass clazz);

/**
 * GetMethodID本体
 */
jmethodID JNICALL jni_get_method_id(JNIEnv *env, jclass clazz, const char *name, const char *sig);

/**
 * CallObjectMethod本体
 */
jobject JNICALL jni_call_object_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jobject JNICALL jni_call_object_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jobject JNICALL jni_call_object_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);

/**
 * CallBooleanMethod本体
 */
jboolean JNICALL jni_call_boolean_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jboolean JNICALL jni_call_boolean_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jboolean JNICALL jni_call_boolean_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);

/**
 * CallByteMethod本体
 */
jbyte JNICALL jni_call_byte_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jbyte JNICALL jni_call_byte_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jbyte JNICALL jni_call_byte_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args);

/**
 * CallCharMethod本体
 */
jchar JNICALL jni_call_char_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jchar JNICALL jni_call_char_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jchar JNICALL jni_call_char_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args);

/**
 * CallShortMethod本体
 */
jshort JNICALL jni_call_short_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jshort JNICALL jni_call_short_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jshort JNICALL jni_call_short_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args);

/**
 * CallIntMethod本体
 */
jint JNICALL jni_call_int_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jint JNICALL jni_call_int_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jint JNICALL jni_call_int_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args);

/**
 * CallLongMethod本体
 */
jlong JNICALL jni_call_long_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jlong JNICALL jni_call_long_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jlong JNICALL jni_call_long_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args);

/**
 * CallFloatMethod本体
 */
jfloat JNICALL jni_call_float_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jfloat JNICALL jni_call_float_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jfloat JNICALL jni_call_float_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args);

/**
 * CallDoubleMethod本体
 */
jdouble JNICALL jni_call_double_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jdouble JNICALL jni_call_double_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jdouble JNICALL jni_call_double_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args);

/**
 * CallVoidMethod本体
 */
void JNICALL jni_call_void_method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
void JNICALL jni_call_void_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
void JNICALL jni_call_void_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);

/**
 * CallNonvirtualVoidMethod本体
 */
void JNICALL jni_call_nonvirtual_void_method(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
void JNICALL jni_call_nonvirtual_void_method_v(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args);
void JNICALL jni_call_nonvirtual_void_method_a(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue * args);

/**
 * GetFieldID本体
 */
jfieldID JNICALL jni_get_field_id(JNIEnv *env, jclass clazz, const char *name, const char *sig);

/**
 * Get<Type>Field本体
 */
jobject JNICALL jni_get_object_field(JNIEnv *env, jobject obj, jfieldID fieldID);
jboolean JNICALL jni_get_boolean_field(JNIEnv *env, jobject obj, jfieldID fieldID);
jbyte JNICALL jni_get_byte_field(JNIEnv *env, jobject obj, jfieldID fieldID);
jshort JNICALL jni_get_short_field(JNIEnv *env, jobject obj, jfieldID fieldID);
jchar JNICALL jni_get_char_field(JNIEnv *env, jobject obj, jfieldID fieldID);
jint JNICALL jni_get_int_field(JNIEnv *env, jobject obj, jfieldID fieldID);
jlong JNICALL jni_get_long_field(JNIEnv *env, jobject obj, jfieldID fieldID);
jfloat JNICALL jni_get_float_field(JNIEnv *env, jobject obj, jfieldID fieldID);
jdouble JNICALL jni_get_double_field(JNIEnv *env, jobject obj, jfieldID fieldID);

/**
 * Set<Type>Field本体
 */
void JNICALL jni_set_object_field(JNIEnv *env, jobject obj, jfieldID fieldID, jobject val);
void JNICALL jni_set_boolean_field(JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val);
void JNICALL jni_set_byte_field(JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val);
void JNICALL jni_set_short_field(JNIEnv *env, jobject obj, jfieldID fieldID, jshort val);
void JNICALL jni_set_char_field(JNIEnv *env, jobject obj, jfieldID fieldID, jchar val);
void JNICALL jni_set_int_field(JNIEnv *env, jobject obj, jfieldID fieldID, jint val);
void JNICALL jni_set_long_field(JNIEnv *env, jobject obj, jfieldID fieldID, jlong val);
void JNICALL jni_set_float_field(JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val);
void JNICALL jni_set_double_field(JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val);

/**
 * GetStaticMethodID本体
 */
jmethodID JNICALL jni_get_static_method_id(JNIEnv *env, jclass clazz, const char *name, const char *sig);

/**
 * CallObjectMethod本体
 */
jobject JNICALL jni_call_static_object_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
jobject JNICALL jni_call_static_object_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
jobject JNICALL jni_call_static_object_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue * args);

/**
 * CallBooleanMethod本体
 */
jboolean JNICALL jni_call_static_boolean_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
jboolean JNICALL jni_call_static_boolean_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
jboolean JNICALL jni_call_static_boolean_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue * args);

/**
 * CallByteMethod本体
 */
jbyte JNICALL jni_call_static_byte_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
jbyte JNICALL jni_call_static_byte_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
jbyte JNICALL jni_call_static_byte_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue* args);

/**
 * CallCharMethod本体
 */
jchar JNICALL jni_call_static_char_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
jchar JNICALL jni_call_static_char_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
jchar JNICALL jni_call_static_char_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue* args);

/**
 * CallShortMethod本体
 */
jshort JNICALL jni_call_static_short_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
jshort JNICALL jni_call_static_short_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
jshort JNICALL jni_call_static_short_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue* args);

/**
 * CallIntMethod本体
 */
jint JNICALL jni_call_static_int_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
jint JNICALL jni_call_static_int_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
jint JNICALL jni_call_static_int_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue* args);

/**
 * CallLongMethod本体
 */
jlong JNICALL jni_call_static_long_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
jlong JNICALL jni_call_static_long_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
jlong JNICALL jni_call_static_long_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue* args);

/**
 * CallFloatMethod本体
 */
jfloat JNICALL jni_call_static_float_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
jfloat JNICALL jni_call_static_float_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
jfloat JNICALL jni_call_static_float_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue* args);

/**
 * CallDoubleMethod本体
 */
jdouble JNICALL jni_call_static_double_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
jdouble JNICALL jni_call_static_double_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
jdouble JNICALL jni_call_static_double_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue* args);

/**
 * CallVoidMethod本体
 */
void JNICALL jni_call_static_void_method(JNIEnv *env, jclass clazz,  jmethodID methodID, ...);
void JNICALL jni_call_static_void_method_v(JNIEnv *env, jclass clazz,  jmethodID methodID, va_list args);
void JNICALL jni_call_static_void_method_a(JNIEnv *env, jclass clazz,  jmethodID methodID, const jvalue * args);


/**
 * GetStaticFieldID本体
 */
jfieldID JNICALL jni_get_static_field_id(JNIEnv *env, jclass clazz, const char *name, const char *sig); 

/**
 * GetStatic<Type>Field本体
 */
jboolean jni_get_static_boolean_field(JNIEnv *env, jclass clazz, jfieldID fieldID);
jbyte jni_get_static_byte_field(JNIEnv *env, jclass clazz, jfieldID fieldID);
jchar jni_get_static_char_field(JNIEnv *env, jclass clazz, jfieldID fieldID);
jshort jni_get_static_short_field(JNIEnv *env, jclass clazz, jfieldID fieldID);
jint jni_get_static_int_field(JNIEnv *env, jclass clazz, jfieldID fieldID);
jlong jni_get_static_long_field(JNIEnv *env, jclass clazz, jfieldID fieldID);
jfloat jni_get_static_float_field(JNIEnv *env, jclass clazz, jfieldID fieldID);
jdouble jni_get_static_double_field(JNIEnv *env, jclass clazz, jfieldID fieldID);
jobject jni_get_static_object_field(JNIEnv *env, jclass clazz, jfieldID fieldID);

/**
 * SetStatic<Type>Field本体
 */
void jni_set_static_boolean_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value);
void jni_set_static_byte_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value);
void jni_set_static_char_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value);
void jni_set_static_short_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value);
void jni_set_static_int_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jint value);
void jni_set_static_long_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value);
void jni_set_static_float_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value);
void jni_set_static_double_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value);
void jni_set_static_object_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value);

/**
 * NewString本体
 */
jstring JNICALL jni_new_string(JNIEnv *env, const jchar *unicode, jsize len);

/**
 * GetStringLength本体
 */
jsize JNICALL jni_get_string_length(JNIEnv *env, jstring str);

/**
 * GetStringChars本体
 */
const jchar* JNICALL jni_get_string_chars(JNIEnv *env, jstring str, jboolean* isCopy);

/**
 * ReleaseStringChars本体
 */
void jni_release_string_chars(JNIEnv *env, jstring string, const jchar *chars);

/**
 * NewStringUTF本体
 */
jstring jni_new_string_utf(JNIEnv *env, const char *bytes);

/**
 * GetStringUTFLength本体
 */
jsize JNICALL jni_get_string_utf_length(JNIEnv *env, jstring str);

/**
 * GetStringUTFChars本体
 */
const char* JNICALL jni_get_string_utf_chars(JNIEnv *env, jstring str, jboolean* isCopy);

/**
 * ReleaseStringUTFChars本体
 */
void jni_release_string_utf_chars(JNIEnv *env, jstring string, const char *chars);

/**
 * GetArrayLength本体
 */
jsize JNICALL jni_get_array_length(JNIEnv *env, jarray array);

/**
 * NewObjectArray
 */
jobjectArray jni_new_object_array(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);

/**
 * GetObjectArrayElement本体
 */
jobject JNICALL jni_get_object_array_element(JNIEnv *env, jobjectArray array, jsize index); 

/**
 * SetObjectArrayElement本体
 */
void JNICALL jni_set_object_array_element(JNIEnv *env, jobjectArray array, jsize index, jobject val);

/**
 * New<PrimitiveType>Array本体
 */
jbooleanArray jni_new_boolean_array(JNIEnv *env, jsize length); 
jbyteArray jni_new_byte_array(JNIEnv *env, jsize length); 
jshortArray jni_new_short_array(JNIEnv *env, jsize length); 
jcharArray jni_new_char_array(JNIEnv *env, jsize length); 
jintArray jni_new_int_array(JNIEnv *env, jsize length); 
jlongArray jni_new_long_array(JNIEnv *env, jsize length); 
jfloatArray jni_new_float_array(JNIEnv *env, jsize length); 
jdoubleArray jni_new_double_array(JNIEnv *env, jsize length); 

/**
 * Get<primitive>ArrayElements本体
 */
jboolean* JNICALL jni_get_boolean_array_elements(JNIEnv *env, jbooleanArray array, jboolean* isCopy); 
jbyte* JNICALL jni_get_byte_array_elements(JNIEnv *env, jbyteArray array, jboolean* isCopy); 
jshort* JNICALL jni_get_short_array_elements(JNIEnv *env, jshortArray array, jboolean* isCopy); 
jchar* JNICALL jni_get_char_array_elements(JNIEnv *env, jcharArray array, jboolean* isCopy); 
jint* JNICALL jni_get_int_array_elements(JNIEnv *env, jintArray array, jboolean* isCopy); 
jlong* JNICALL jni_get_long_array_elements(JNIEnv *env, jlongArray array, jboolean* isCopy); 
jfloat* JNICALL jni_get_float_array_elements(JNIEnv *env, jfloatArray array, jboolean* isCopy); 
jdouble* JNICALL jni_get_double_array_elements(JNIEnv *env, jdoubleArray array, jboolean* isCopy); 

/**
 * Release<primitive>ArrayElements本体
 */
void JNICALL jni_release_boolean_array_elements(JNIEnv *env, jbooleanArray array, jboolean* elems, jint mode); 
void JNICALL jni_release_byte_array_elements(JNIEnv *env, jbyteArray array, jbyte* elems, jint mode); 
void JNICALL jni_release_short_array_elements(JNIEnv *env, jshortArray array, jshort* elems, jint mode); 
void JNICALL jni_release_char_array_elements(JNIEnv *env, jcharArray array, jchar* elems, jint mode); 
void JNICALL jni_release_int_array_elements(JNIEnv *env, jintArray array, jint* elems, jint mode); 
void JNICALL jni_release_long_array_elements(JNIEnv *env, jlongArray array, jlong* elems, jint mode); 
void JNICALL jni_release_float_array_elements(JNIEnv *env, jfloatArray array, jfloat* elems, jint mode); 
void JNICALL jni_release_double_array_elements(JNIEnv *env, jdoubleArray array, jdouble* elems, jint mode); 


/**
 * Get<primitive>ArrayRegion本体
 */
void JNICALL jni_get_boolean_array_region(JNIEnv *env, jbooleanArray array, jsize start, jsize len, jboolean *buf);  
void JNICALL jni_get_byte_array_region(JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf);  
void JNICALL jni_get_short_array_region(JNIEnv *env, jshortArray array,  jsize start, jsize len, jshort *buf);  
void JNICALL jni_get_char_array_region(JNIEnv *env, jcharArray array,  jsize start, jsize len, jchar *buf);  
void JNICALL jni_get_int_array_region(JNIEnv *env, jintArray array,  jsize start, jsize len, jint *buf);  
void JNICALL jni_get_long_array_region(JNIEnv *env, jlongArray array,  jsize start, jsize len, jlong *buf);  
void JNICALL jni_get_float_array_region(JNIEnv *env, jfloatArray array,  jsize start, jsize len, jfloat *buf);  
void JNICALL jni_get_double_array_region(JNIEnv *env, jdoubleArray array,  jsize start, jsize len, jdouble *buf);  

/**
 * Set<primitive>ArrayRegion本体
 */
void JNICALL jni_set_boolean_array_region(JNIEnv *env, jbooleanArray array, jsize start, jsize len, const jboolean *buf);  
void JNICALL jni_set_byte_array_region(JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf);  
void JNICALL jni_set_short_array_region(JNIEnv *env, jshortArray array,  jsize start, jsize len, const jshort *buf);  
void JNICALL jni_set_char_array_region(JNIEnv *env, jcharArray array,  jsize start, jsize len, const jchar *buf);  
void JNICALL jni_set_int_array_region(JNIEnv *env, jintArray array,  jsize start, jsize len, const jint *buf);  
void JNICALL jni_set_long_array_region(JNIEnv *env, jlongArray array,  jsize start, jsize len, const jlong *buf);  
void JNICALL jni_set_float_array_region(JNIEnv *env, jfloatArray array,  jsize start, jsize len, const jfloat *buf);  
void JNICALL jni_set_double_array_region(JNIEnv *env, jdoubleArray array,  jsize start, jsize len, const jdouble *buf);  

/**
 * GetPrimitiveArrayCritical本体
 */
void* JNICALL jni_get_primitive_array_critical(JNIEnv *env, jarray array, jboolean* isCopy); 

/**
 * ReleasePrimitiveArrayCritical本体
 */
void jni_release_primitive_array_critical(JNIEnv *env, jarray array, void* carray, jint mode);


/**
 * RegisterNatives本体
 */
jint JNICALL jni_register_natives(JNIEnv* env, jclass clazz, const JNINativeMethod *methods, jint nMethods);

/**
 * MonitorEnter本体
 */
jint JNICALL jni_monitor_enter(JNIEnv *env, jobject obj); 

/**
 * MonitorExit本体
 */
jint JNICALL jni_monitor_exit(JNIEnv *env, jobject obj); 

/**
 * ExceptionCheck本体
 */
jboolean JNICALL jni_exception_check(JNIEnv *env);

/**
 * NewDirectByteBuffer本体
 */
jobject JNICALL jni_new_direct_byte_buffer(JNIEnv* env, void* address, jlong capacity);

/**
 * GetDirectBufferAddress 本体
 */
void* JNICALL jni_get_direct_buffer_address(JNIEnv* env, jobject buf); 

/**
 * GetDirectBufferCapacity本体
 */
jlong JNICALL jni_get_direct_buffer_capacity(JNIEnv* env, jobject buf); 

/**
 * GetJavaVM本体
 */
jint JNICALL jni_get_java_vm(JNIEnv* env, JavaVM** vm);

/**
 * AttachCurrentThread()本体
 */
jint JNICALL jni_attach_current_thread(JavaVM *vm, void **p_env, void *thr_args);

/**
 * AttachCurrentThreadAsDaemon()本体
 */
jint JNICALL jni_attach_current_thread_as_daemon(JavaVM* vm, void** penv, void* args);

/**
 * DetachCurrentThreadDaemon()本体
 */
jint JNICALL jni_detach_current_thread(JavaVM *vm);

/**
 * GetEnv()本体
 */
jint JNICALL jni_get_env(JavaVM *vm, void **env, jint version);

/**
 * JNI_OnLoad()のポインタ
 */
typedef jint		(*JNI_ON_LOAD)(JavaVM *vm, void *reserved);

/**
 * 外部DLLで定義されている関数
 */
extern "C" jint JNICALL Mysaifu_SetJavaVM(JavaVM* javavm);

/**
 * ネイティブメソッド呼び出し用関数ポインタ
 */
typedef void		(*VOID_FUNC)(native_method_parameters params);
typedef jboolean	(*BOOLEAN_FUNC)(native_method_parameters params);
typedef jbyte		(*BYTE_FUNC)(native_method_parameters params);
typedef jchar		(*CHAR_FUNC)(native_method_parameters params);
typedef jshort		(*SHORT_FUNC)(native_method_parameters params);
typedef jint		(*INT_FUNC)(native_method_parameters params);
typedef jlong		(*LONG_FUNC)(native_method_parameters params);
typedef jfloat		(*FLOAT_FUNC)(native_method_parameters params);
typedef jdouble		(*DOUBLE_FUNC)(native_method_parameters params);
typedef jobject		(*OBJECT_FUNC)(native_method_parameters params);

// レジスタ呼び出しが可能な形式（ARMプロセッサの場合）
typedef void		(*VOID_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);
typedef jboolean	(*BOOLEAN_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);
typedef jbyte		(*BYTE_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);
typedef jchar		(*CHAR_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);
typedef jshort		(*SHORT_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);
typedef jint		(*INT_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);
typedef jlong		(*LONG_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);
typedef jfloat		(*FLOAT_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);
typedef jdouble		(*DOUBLE_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);
typedef jobject		(*OBJECT_REGISTER_FUNC)(jni_env* env, jobject obj, __int32 param1, __int32 param2);

/**
 * static変数 g_funcs を初期化する
 */
static void init_g_funcs() {
	g_functions.GetVersion		= jni_get_version;
	g_functions.DefineClass		= jni_define_class;
	g_functions.FindClass		= jni_find_class;
	
	g_functions.FromReflectedMethod	= jni_from_reflected_method;
	g_functions.FromReflectedField = jni_from_reflected_field;

	g_functions.GetSuperclass	= jni_get_superclass;
	g_functions.IsAssignableFrom = jni_is_assignable_from;
	
	g_functions.Throw			= jni_throw;
	g_functions.ThrowNew		= jni_throw_new;
	g_functions.ExceptionOccurred = jni_exception_occurred;
	g_functions.ExceptionDescribe = jni_exception_describe;
	g_functions.ExceptionClear	= jni_exception_clear;
	g_functions.FatalError		= jni_fatal_error;
	g_functions.ExceptionCheck  = jni_exception_check;

	g_functions.NewGlobalRef	= jni_new_global_ref;
	g_functions.DeleteGlobalRef	= jni_delete_global_ref;
	g_functions.DeleteLocalRef	= jni_delete_local_ref;
	g_functions.IsSameObject	= jni_is_same_object;
	g_functions.NewLocalRef		= jni_new_local_ref;
	g_functions.EnsureLocalCapacity	= jni_ensure_local_capacity;

	g_functions.AllocObject		= jni_alloc_object;
	g_functions.NewObject		= jni_new_object;
	g_functions.NewObjectV		= jni_new_object_v;
	g_functions.NewObjectA		= jni_new_object_a;

	g_functions.GetObjectClass	= jni_get_object_class;
	g_functions.IsInstanceOf	= jni_is_instance_of;
	
	g_functions.GetMethodID		= jni_get_method_id;

	g_functions.CallObjectMethod	= jni_call_object_method;
	g_functions.CallObjectMethodV	= jni_call_object_method_v;
	g_functions.CallObjectMethodA	= jni_call_object_method_a;

	g_functions.CallBooleanMethod	= jni_call_boolean_method;
	g_functions.CallBooleanMethodV	= jni_call_boolean_method_v;
	g_functions.CallBooleanMethodA	= jni_call_boolean_method_a;

	g_functions.CallByteMethod		= jni_call_byte_method;
	g_functions.CallByteMethodV		= jni_call_byte_method_v;
	g_functions.CallByteMethodA		= jni_call_byte_method_a;

	g_functions.CallCharMethod		= jni_call_char_method;
	g_functions.CallCharMethodV		= jni_call_char_method_v;
	g_functions.CallCharMethodA		= jni_call_char_method_a;

	g_functions.CallShortMethod		= jni_call_short_method;
	g_functions.CallShortMethodV	= jni_call_short_method_v;
	g_functions.CallShortMethodA	= jni_call_short_method_a;

	g_functions.CallIntMethod		= jni_call_int_method;
	g_functions.CallIntMethodV		= jni_call_int_method_v;
	g_functions.CallIntMethodA		= jni_call_int_method_a;

	g_functions.CallLongMethod		= jni_call_long_method;
	g_functions.CallLongMethodV		= jni_call_long_method_v;
	g_functions.CallLongMethodA		= jni_call_long_method_a;

	g_functions.CallFloatMethod		= jni_call_float_method;
	g_functions.CallFloatMethodV	= jni_call_float_method_v;
	g_functions.CallFloatMethodA	= jni_call_float_method_a;

	g_functions.CallDoubleMethod	= jni_call_double_method;
	g_functions.CallDoubleMethodV	= jni_call_double_method_v;
	g_functions.CallDoubleMethodA	= jni_call_double_method_a;

	g_functions.CallVoidMethod		= jni_call_void_method;
	g_functions.CallVoidMethodV		= jni_call_void_method_v;
	g_functions.CallVoidMethodA		= jni_call_void_method_a;

	g_functions.CallNonvirtualVoidMethod	= jni_call_nonvirtual_void_method;
	g_functions.CallNonvirtualVoidMethodV	= jni_call_nonvirtual_void_method_v;
	g_functions.CallNonvirtualVoidMethodA	= jni_call_nonvirtual_void_method_a;

	g_functions.GetFieldID				= jni_get_field_id;
	g_functions.GetObjectField			= jni_get_object_field;
	g_functions.GetBooleanField			= jni_get_boolean_field;
	g_functions.GetByteField			= jni_get_byte_field;
	g_functions.GetCharField			= jni_get_char_field;
	g_functions.GetShortField			= jni_get_short_field;
	g_functions.GetIntField				= jni_get_int_field;
	g_functions.GetLongField			= jni_get_long_field;
	g_functions.GetFloatField			= jni_get_float_field;
	g_functions.GetDoubleField			= jni_get_double_field;
	
	g_functions.SetObjectField			= jni_set_object_field;
	g_functions.SetBooleanField			= jni_set_boolean_field;
	g_functions.SetByteField			= jni_set_byte_field;
	g_functions.SetCharField			= jni_set_char_field;
	g_functions.SetShortField			= jni_set_short_field;
	g_functions.SetCharField			= jni_set_char_field;
	g_functions.SetIntField				= jni_set_int_field;
	g_functions.SetLongField			= jni_set_long_field;
	g_functions.SetFloatField			= jni_set_float_field;
	g_functions.SetDoubleField			= jni_set_double_field;
	
	g_functions.GetStaticMethodID		= jni_get_static_method_id;

	g_functions.CallStaticObjectMethod	= jni_call_static_object_method;
	g_functions.CallStaticObjectMethodV	= jni_call_static_object_method_v;
	g_functions.CallStaticObjectMethodA	= jni_call_static_object_method_a;

	g_functions.CallStaticBooleanMethod		= jni_call_static_boolean_method;
	g_functions.CallStaticBooleanMethodV	= jni_call_static_boolean_method_v;
	g_functions.CallStaticBooleanMethodA	= jni_call_static_boolean_method_a;

	g_functions.CallStaticByteMethod		= jni_call_static_byte_method;
	g_functions.CallStaticByteMethodV		= jni_call_static_byte_method_v;
	g_functions.CallStaticByteMethodA		= jni_call_static_byte_method_a;

	g_functions.CallStaticCharMethod		= jni_call_static_char_method;
	g_functions.CallStaticCharMethodV		= jni_call_static_char_method_v;
	g_functions.CallStaticCharMethodA		= jni_call_static_char_method_a;

	g_functions.CallStaticShortMethod		= jni_call_static_short_method;
	g_functions.CallStaticShortMethodV	= jni_call_static_short_method_v;
	g_functions.CallStaticShortMethodA	= jni_call_static_short_method_a;

	g_functions.CallStaticIntMethod		= jni_call_static_int_method;
	g_functions.CallStaticIntMethodV		= jni_call_static_int_method_v;
	g_functions.CallStaticIntMethodA		= jni_call_static_int_method_a;

	g_functions.CallStaticLongMethod		= jni_call_static_long_method;
	g_functions.CallStaticLongMethodV		= jni_call_static_long_method_v;
	g_functions.CallStaticLongMethodA		= jni_call_static_long_method_a;

	g_functions.CallStaticFloatMethod		= jni_call_static_float_method;
	g_functions.CallStaticFloatMethodV	= jni_call_static_float_method_v;
	g_functions.CallStaticFloatMethodA	= jni_call_static_float_method_a;

	g_functions.CallStaticDoubleMethod	= jni_call_static_double_method;
	g_functions.CallStaticDoubleMethodV	= jni_call_static_double_method_v;
	g_functions.CallStaticDoubleMethodA	= jni_call_static_double_method_a;

	g_functions.CallStaticVoidMethod		= jni_call_static_void_method;
	g_functions.CallStaticVoidMethodV		= jni_call_static_void_method_v;
	g_functions.CallStaticVoidMethodA		= jni_call_static_void_method_a;


	g_functions.GetStaticFieldID		= jni_get_static_field_id;
	
	g_functions.GetStaticBooleanField	= jni_get_static_boolean_field;
	g_functions.GetStaticByteField		= jni_get_static_byte_field;
	g_functions.GetStaticShortField		= jni_get_static_short_field;
	g_functions.GetStaticCharField		= jni_get_static_char_field;
	g_functions.GetStaticIntField		= jni_get_static_int_field;
	g_functions.GetStaticLongField		= jni_get_static_long_field;
	g_functions.GetStaticFloatField		= jni_get_static_float_field;
	g_functions.GetStaticDoubleField	= jni_get_static_double_field;
	g_functions.GetStaticObjectField	= jni_get_static_object_field;

	g_functions.SetStaticBooleanField	= jni_set_static_boolean_field;
	g_functions.SetStaticByteField		= jni_set_static_byte_field;
	g_functions.SetStaticCharField		= jni_set_static_char_field;
	g_functions.SetStaticShortField		= jni_set_static_short_field;
	g_functions.SetStaticIntField		= jni_set_static_int_field;
	g_functions.SetStaticLongField		= jni_set_static_long_field;
	g_functions.SetStaticFloatField		= jni_set_static_float_field;
	g_functions.SetStaticDoubleField	= jni_set_static_double_field;
	g_functions.SetStaticObjectField	= jni_set_static_object_field;

	g_functions.NewString				= jni_new_string;
	g_functions.GetStringLength			= jni_get_string_length;
	g_functions.GetStringChars			= jni_get_string_chars;
	g_functions.ReleaseStringChars		= jni_release_string_chars;

	g_functions.NewStringUTF			= jni_new_string_utf;
	g_functions.GetStringUTFLength		= jni_get_string_utf_length;
	g_functions.GetStringUTFChars		= jni_get_string_utf_chars;
	g_functions.ReleaseStringUTFChars	= jni_release_string_utf_chars;
	
	g_functions.GetArrayLength			= jni_get_array_length;
	g_functions.NewObjectArray			= jni_new_object_array;
	g_functions.GetObjectArrayElement	= jni_get_object_array_element;
	g_functions.SetObjectArrayElement	= jni_set_object_array_element;
	
	g_functions.NewBooleanArray			= jni_new_boolean_array;
	g_functions.NewByteArray			= jni_new_byte_array;
	g_functions.NewShortArray			= jni_new_short_array;
	g_functions.NewCharArray			= jni_new_char_array;
	g_functions.NewIntArray				= jni_new_int_array;
	g_functions.NewLongArray			= jni_new_long_array;
	g_functions.NewFloatArray			= jni_new_float_array;
	g_functions.NewDoubleArray			= jni_new_double_array;

	g_functions.GetBooleanArrayElements	= jni_get_boolean_array_elements;
	g_functions.GetByteArrayElements	= jni_get_byte_array_elements;
	g_functions.GetShortArrayElements	= jni_get_short_array_elements;
	g_functions.GetCharArrayElements	= jni_get_char_array_elements;
	g_functions.GetIntArrayElements		= jni_get_int_array_elements;
	g_functions.GetLongArrayElements	= jni_get_long_array_elements;
	g_functions.GetFloatArrayElements	= jni_get_float_array_elements;
	g_functions.GetDoubleArrayElements	= jni_get_double_array_elements;

	g_functions.ReleaseBooleanArrayElements	= jni_release_boolean_array_elements;
	g_functions.ReleaseByteArrayElements	= jni_release_byte_array_elements;
	g_functions.ReleaseShortArrayElements	= jni_release_short_array_elements;
	g_functions.ReleaseCharArrayElements	= jni_release_char_array_elements;
	g_functions.ReleaseIntArrayElements		= jni_release_int_array_elements;
	g_functions.ReleaseLongArrayElements	= jni_release_long_array_elements;
	g_functions.ReleaseFloatArrayElements	= jni_release_float_array_elements;
	g_functions.ReleaseDoubleArrayElements	= jni_release_double_array_elements;

	g_functions.GetBooleanArrayRegion		= jni_get_boolean_array_region;
	g_functions.GetByteArrayRegion			= jni_get_byte_array_region;
	g_functions.GetCharArrayRegion			= jni_get_char_array_region;
	g_functions.GetShortArrayRegion			= jni_get_short_array_region;
	g_functions.GetIntArrayRegion			= jni_get_int_array_region;
	g_functions.GetLongArrayRegion			= jni_get_long_array_region;
	g_functions.GetFloatArrayRegion			= jni_get_float_array_region;
	g_functions.GetDoubleArrayRegion		= jni_get_double_array_region;
	
	g_functions.SetBooleanArrayRegion		= jni_set_boolean_array_region;
	g_functions.SetByteArrayRegion			= jni_set_byte_array_region;
	g_functions.SetCharArrayRegion			= jni_set_char_array_region;
	g_functions.SetShortArrayRegion			= jni_set_short_array_region;
	g_functions.SetIntArrayRegion			= jni_set_int_array_region;
	g_functions.SetLongArrayRegion			= jni_set_long_array_region;
	g_functions.SetFloatArrayRegion			= jni_set_float_array_region;
	g_functions.SetDoubleArrayRegion		= jni_set_double_array_region;

	g_functions.RegisterNatives		= jni_register_natives;

	g_functions.MonitorEnter		= jni_monitor_enter;
	g_functions.MonitorExit			= jni_monitor_exit;

	g_functions.NewDirectByteBuffer			= jni_new_direct_byte_buffer;
	g_functions.GetDirectBufferAddress		= jni_get_direct_buffer_address;
	g_functions.GetDirectBufferCapacity		= jni_get_direct_buffer_capacity;

	// Mysaifu JVMにおいては、GetStringChars() と GetStringCritical() の動作は同じ
	g_functions.GetStringCritical		= jni_get_string_chars;
	g_functions.ReleaseStringCritical	= jni_release_string_chars;

	g_functions.GetPrimitiveArrayCritical		= jni_get_primitive_array_critical;
	g_functions.ReleasePrimitiveArrayCritical	= jni_release_primitive_array_critical;

	g_functions.GetJavaVM = jni_get_java_vm;
}

/**
 * 変数 g_java_vm を初期化する
 */
static void init_g_java_vm() {
	static JNIInvokeInterface_ inv;
	g_java_vm.functions = &inv;

	inv.AttachCurrentThread		  = jni_attach_current_thread;
	inv.AttachCurrentThreadAsDaemon = jni_attach_current_thread_as_daemon;
	inv.DetachCurrentThread		  = jni_detach_current_thread;
	inv.GetEnv					  = jni_get_env;
}

/**
 * 特別扱いするnative methodを登録する
 * JVMの内部構造にアクセスする必要のあるnative methodは、DLLではなくJVMの一部として実装する
 */
static void init_special_methods() {
	for (int i = 0; i < sizeof(g_special_methods) / sizeof(special_method); ++i) {
		special_method* s = &g_special_methods[i];
		JNINativeMethod m;
		m.name = (char*) s->method_name;
		m.signature = (char*) s->method_descriptor;
		m.fnPtr = s->func_ptr;
		add_native_method_info(NULL, s->class_name, &m);
	}
}

/**
 * ネイティブインタフェース関連の情報を初期化する
 */
void init_jni_funcs() {
	// グローバル変数 g_native_methods を初期化する
	g_native_methods_count = 0;
	g_native_methods = NULL;

	// グローバル変数 g_functions を初期化する
	init_g_funcs();

	// JavaVM関数を初期化する
	init_g_java_vm();

	// 特別扱いするメソッド情報を初期化する
	init_special_methods();
	
	// JavaVMを設定する
	Mysaifu_SetJavaVM(&g_java_vm);
}

/**
 * 指定されたクラスオブジェクトから、対応するstaticデータを得る
 */
static jobject get_static_data(JNIEnv* env, jclass clazz) {
	// フィールド"vmdata"の値を取得する。（staticデータが入っている）
	if (! g_Class_vmdata_id) {
		static java_utf8* field_name;
		static java_utf8* field_desc;
		if (! field_name && ! field_desc) {
			field_name = intern_utf8("vmdata");
			field_desc = intern_utf8("Ljava/lang/Object;");
		}

		jobject class_object = clazz;
		ClassFile* cfile = get_ClassFile(class_object);
		g_Class_vmdata_id = get_field_id(cfile,
										 field_name,
										 field_desc);
	}
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	ClassFile* cfile = get_ClassFile(clazz);
	PUSH_OBJECT(current_frame, clazz);
	jobject static_data;
	get_field(current_frame, cfile, g_Class_vmdata_id);
	POP_OBJECT(current_frame, static_data);
	
	return static_data;
}

/**
 * メインスレッドを開始する
 */
bool start_main_thread(java_utf8* main_class_name, _TCHAR* argv, bool jar) {
	// jni_env を作成する
	class_loader* l = get_bootstrap_class_loader();
	
	frame* root_frame = alloc_root_frame(NULL);
	ClassFile* cfile = find_ClassFile(root_frame, l, intern_utf8("java/lang/VMMainThread"));
	free_root_frame(root_frame);

	frame new_local_frame;
	jni_env* jenv = get_jni_env(alloc_root_frame(cfile), &new_local_frame);
	JNIEnv* env = (JNIEnv*) jenv;
	// java/lang/VMMainThreadをロードする
	jclass main_thread_class = env->FindClass("java/lang/VMMainThread");
	if (main_thread_class == NULL) {
		release_jni_env(jenv);
		return false;
	}
	
	// 引数を作成する
	// クラス名
	jstring classname = env->NewStringUTF(main_class_name);
	if (classname == NULL) {
		release_jni_env(jenv);
		return false;
	}
	// args
	jclass string_class = env->FindClass("java/lang/String");
	if (string_class == NULL) {
		release_jni_env(jenv);
		return false;
	}

	jobjectArray args = create_argv(env, argv);
	if (args == NULL) {
		release_jni_env(jenv);
		return false;
	}
	
	// オブジェクトをアロケートする（この時点ではコンストラクタは呼び出さない）
	jobject main_thread = env->AllocObject(main_thread_class);
	if (! main_thread) {
		release_jni_env(jenv);
		return false;
	}

	// カレントスレッドを設定する（ここで設定しておかないと、Threadのコンストラクタ内で
	// NullPointerExceptionが発生してしまう）
	set_current_thread(get_current_frame(((jni_env*) env)->jni_frame), main_thread);

	// 以下のコンストラクタを起動する
	//   public VMMainThread(String classname, String[] args, boolean)
	jmethodID mid = env->GetMethodID(main_thread_class, "<init>", "(Ljava/lang/String;[Ljava/lang/String;Z)V");
	if (mid == NULL) {
		release_jni_env(jenv);
		return false;
	}
	env->CallVoidMethod(main_thread, mid, classname, args, jar);
	if (env->ExceptionCheck()) {
		// コンストラクタ実行中に例外発生
		release_jni_env(jenv);
		return false;
	}
	
	// void start() を起動する
	mid = env->GetMethodID(main_thread_class, "start", "()V");
	if (mid == NULL) {
		release_jni_env(jenv);
		return false;
	}
	env->CallVoidMethod(main_thread, mid);
	if (env->ExceptionCheck()) {
		// start() 実行中に例外発生
		release_jni_env(jenv);
		return false;
	}

	// jni_envを削除する
	release_jni_env(jenv);
	return true;
}

/**
 * 指定されたメソッドシグネチャを解析し、対応するClass[]を返す
 */
static jobjectArray create_parameter_types(JNIEnv* env, const char* sig, class_loader* l) {
	const int max_size = 32;	// 最大で32パラメータまで入る
	jclass* classes = (jclass*) malloc(sizeof(jclass) * max_size);
	int array_index = 0;
	const char* array_start = NULL;
	const char* work = sig;
	while (*work) {
		if (array_index >= max_size) {
			// これ以上は解析できない
			break;
		}
		char c = *work++;
		if (c == '(') {
			continue;
		} else if (c == ')') {
			break;
		} else if (c == '[' && array_start == NULL) {
			array_start = work - 1;	// 配列の開始位置を記録する
			while (*work == '[') {
				// 多次元配列
				work++;
			}
		} else if (c == 'L') {
			// "Lクラス名;"
			// クラス名をコピーする
			const char* start;
			if (array_start != NULL) {
				// 配列の場合
				start = array_start;
				array_start = NULL;
			} else {
				// 配列以外の場合
				start = work;
			}
			while (*work && *work != ';') {
				// ; まで読み込む
				work++;
			}
			int len = work - start;
			if (*work == ';') {
				work++;
			}
			if (*start == '[') {
				len++;
			}
			char* class_name = (char*) malloc(len + 1);
			if (class_name == NULL) {
				fatal_error(FATAL_ERROR_NO_MEMORY);
			}
			memcpy(class_name, start, len);
			if (*start == '[') {
				class_name[len - 1] = ';';
			}
			class_name[len] = '\0';
			jclass klass = find_class(env, class_name, l);
			free(class_name);
			if (klass == NULL) {
				free(classes);
				return NULL;
			}
			classes[array_index++] = klass;

		} else {
			// プリミティブ型
			if (array_start != NULL) {
				// プリミティブ配列
				int len = work - array_start;
				char* class_name = (char*) malloc(len + 1);
				if (class_name == NULL) {
					fatal_error(FATAL_ERROR_NO_MEMORY);
				}
				memcpy(class_name, array_start, len);
				array_start = NULL;
				class_name[len] = '\0';
				jclass klass = find_class(env, class_name, l);
				free(class_name);
				if (klass == NULL) {
					free(classes);
					return NULL;
				}
				classes[array_index++] = klass;

			} else {
				char* class_name = NULL;
				switch (c) {
				case 'Z':	// boolean
					class_name = "boolean";
					break;
				case 'B':	// byte
					class_name = "byte";
					break;
				case 'S':	// short
					class_name = "short";
					break;
				case 'C':	// char
					class_name = "char";
					break;
				case 'I':	// int
					class_name = "int";
					break;
				case 'J':	// long
					class_name = "long";
					break;
				case 'F':	// float
					class_name = "float";
					break;
				case 'D':	// double
					class_name = "double";
					break;
				case 'V':	// void
					// void型のパラメータというのはありえないが、ここでは気にしない
					class_name = "void";
					break;
				}
				if (class_name != NULL) {
					ClassFile* cfile = load_primitive_ClassFile(get_current_frame(((jni_env*) env)->jni_frame), class_name);
					if (cfile == NULL) {
						free(classes);
						return NULL;
					}
					jobject static_data = get_static_data(cfile);
					jclass klass = (jclass) find_primitive_class_object(get_current_frame(((jni_env*) env)->jni_frame), static_data);
					if (klass == NULL) {
						free(classes);
						return NULL;
					}
					classes[array_index++] = klass;
				} else {
					// クラスを生成できない
					assert(false);
				}
			}
		}
	}
	jclass elem_class = env->FindClass("java/lang/Class");
	jobjectArray result = env->NewObjectArray(array_index, elem_class, NULL);
	for (int i = 0; i < array_index; ++i) {
		env->SetObjectArrayElement(result, i, classes[i]);
	}
	free(classes);

	return result;
}

/*
 * Class:     com_mysaifu_jvm_io_VMConsoleInputStream
 * Method:    readNative
 * Signature: ([B)I
 */
JNIEXPORT jint JNICALL Java_com_mysaifu_jvm_io_VMConsoleInputStream_readNative(JNIEnv *env , jobject, jbyteArray buff) {
	int bufflen = env->GetArrayLength(buff);
	assert(! (bufflen & 0x01));

	jbyte* ptr = env->GetByteArrayElements(buff, NULL);
	// ptrは２バイトでアラインされていること
	assert(! ((unsigned int)ptr & 0x01));

	int textlen;
	if (ptr) {
		textlen = console_read((_TCHAR*) ptr, (bufflen / 2));
	} else {
		textlen = 0;
	}
	env->ReleaseByteArrayElements(buff, ptr, 0);
	if (textlen < 0) {
		return -1;
	}
	return textlen * 2;
}

/*
 * Class:     com_mysaifu_jvm_io_VMConsoleOutputStream
 * Method:    writeNative
 * Signature: ([BII)V
 */
JNIEXPORT void JNICALL Java_com_mysaifu_jvm_io_VMConsoleOutputStream_writeNative(JNIEnv *env, jobject, jbyteArray buff, jint off, jint length) {
	if (length <= 0) {
		return;
	}
	// 必ず長さは２の倍数であること
	assert(! (length & 0x01));

	jbyte* data = env->GetByteArrayElements(buff, NULL);
	if (data) {
		_TCHAR text[128 + 1];
		int remains = length;
		while (remains > 0) {
			int copylen = (remains <= 128) ? remains : 128;
			memcpy(text, data + off, copylen);
			text[copylen / 2] = _T('\0');
			console_write(text);
			remains -= copylen;
			off += copylen;
		}
	}
	env->ReleaseByteArrayElements(buff, data, 0);
	
}


/*
 * Class:     gnu_classpath_VMStackWalker
 * Method:    getClassContext
 * Signature: ()[Ljava/lang/Class;
 */
JNIEXPORT jobjectArray JNICALL Java_gnu_classpath_VMStackWalker_getClassContext(JNIEnv* env, jclass) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);

	// 呼び出しもとのフレームを得る
	frame* previous_frame = current_frame->previous_frame;
	jclass* classes = NULL;
	int classes_count = 0;
	int classes_count_limit = 0;

	while (previous_frame && previous_frame->current_class_file) {
		jobject static_data = get_static_data(previous_frame->current_class_file);
		jclass clazz = find_class_object(current_frame, static_data, NULL);
		assert(clazz);
		classes_count++;
		if (classes_count >= classes_count_limit) {
			classes_count_limit += 32;
			classes = (jclass*) realloc(classes, sizeof(jclass) * classes_count_limit);
			if (! classes) {
				fatal_error(FATAL_ERROR_NO_MEMORY);
			}
		}
		classes[classes_count - 1] = clazz;
		previous_frame = previous_frame->previous_frame;
	}
	jclass element_class = env->FindClass("java/lang/Class");
	if (element_class == NULL) {
		return NULL;
	}
	jobjectArray result = env->NewObjectArray(classes_count, element_class, NULL);
	if (env->ExceptionCheck()) {
		return NULL;
	}
	for (int i = 0; i < classes_count; ++i) {
		env->SetObjectArrayElement(result, i, classes[i]);
		if (env->ExceptionCheck()) {
			return NULL;
		}
	}
	free(classes);

	return result;
}

/*
 * Class:     gnu_classpath_VMStackWalker
 * Method:    getClassLoader
 * Signature: (Ljava/lang/Class;)Ljava/lang/ClassLoader;
 */
JNIEXPORT jobject JNICALL Java_gnu_classpath_VMStackWalker_getClassLoader(JNIEnv *env , jclass, jclass clazz) {
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);

	class_loader* loader = get_defining_loader(cfile);
	return get_ClassLoader(loader);
}

/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getJavaHome
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getJavaHome(JNIEnv *env, jclass) {
	const _TCHAR* java_home = get_java_home();
	return env->NewString((const jchar*) java_home, _tcslen(java_home));
}

/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getTempDir
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getTempDir(JNIEnv *env, jclass) {
	_TCHAR dir[MAX_PATH + 1];
	GetTempPath(sizeof(dir) / sizeof(*dir), dir);
	int len = _tcslen(dir);
	if (len && dir[len - 1] != _T('\\')) {
		dir[len - 1] = _T('\\');
	}
	_tcscat(dir, _T("Mysaifu JVM"));
	// ディレクトリを作成する
	CreateDirectory(dir, NULL);

	return env->NewString((const jchar*) dir, _tcslen(dir));
}

/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getJVMVersion
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getJVMVersion(JNIEnv *env, jclass) {
	LARGE_INTEGER jvm_version = get_jvm_version();
	// 16ビットごとに区切って文字列を作成する
	jstring result = NULL;
	_TCHAR buff[128];
	_sntprintf(buff,
			   sizeof(buff) / sizeof(*buff),
			  _T("%d.%d.%d.%d"),
			  HIWORD(jvm_version.HighPart),
			  LOWORD(jvm_version.HighPart),
			  HIWORD(jvm_version.LowPart),
			  LOWORD(jvm_version.LowPart));
	result = env->NewString((const jchar*) buff, _tcslen(buff));
	if (! result) {
		result = env->NewString((const jchar*) _T(""), 0);
	}
	return result;
}

/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getUserHome
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getUserHome(JNIEnv *env, jclass) {
	_TCHAR path[MAX_PATH + 1];
	SHGetDocumentsFolder(_T("\\"), path);
	return env->NewString((const jchar*) path, _tcslen(path));
}

/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getUserName
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getUserName(JNIEnv *env, jclass) {
	jstring result = NULL;
	HKEY hKey;
	long error = RegOpenKeyEx(HKEY_CURRENT_USER,
							  _T("\\ControlPanel\\Owner"),
							  0,
							  0,
							  &hKey);
    if (error == ERROR_SUCCESS) {
		// キーが存在している場合は、キーの値を読み込む
		_TCHAR owner[256];
		owner[0] = _T('\0');

		DWORD dwType;
		DWORD cbData = sizeof(owner);
		
		error = RegQueryValueEx(hKey,
								_T("Owner"),
								NULL,
								&dwType,
								(LPBYTE) owner,
								&cbData);
		// なぜか成功しても error には 234 (データがさらにある）が返ってくる
		result = env->NewString((const jchar*) owner, _tcslen(owner));
		RegCloseKey(hKey);

	}
	if (! result) {
		result = env->NewString((const jchar*) _T(""), 0);
	}
	return result;
}


/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getOSName
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getOSName(JNIEnv *env, jclass) {
	_TCHAR* name = NULL;
	OSVERSIONINFO versioninfo = {0};
	versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	GetVersionEx(&versioninfo);
	if (versioninfo.dwPlatformId == VER_PLATFORM_WIN32_CE) {
		name = _T("Windows CE");
	} else {
		name = _T("unknown");
	}
	
	return env->NewString((const jchar*) name, _tcslen(name));
}

/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getOSArch
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getOSArch(JNIEnv *env, jclass) {
	SYSTEM_INFO info = {0};
	GetSystemInfo(&info);
	_TCHAR* arch = NULL;

	// プロセッサのタイプを表す文字列を作る
	// （この組み合わせで正しい？）
	switch (info.wProcessorArchitecture) {
	case PROCESSOR_ARCHITECTURE_INTEL:
		arch = _T("x86");
		break;

	case PROCESSOR_ARCHITECTURE_MIPS:
		arch = _T("mips");
		break;

	case PROCESSOR_ARCHITECTURE_SHX:
		arch = _T("shx");
		break;

	case PROCESSOR_ARCHITECTURE_ARM:
		arch = _T("arm");
		break;
	
	case PROCESSOR_ARCHITECTURE_UNKNOWN:
	default:
		arch = _T("unknown");
		break;

	}
	return env->NewString((const jchar*) arch, _tcslen(arch));
}

/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getOSVersion
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getOSVersion(JNIEnv *env, jclass) {
	OSVERSIONINFO versioninfo ={0};
	versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	GetVersionEx(&versioninfo);
	_TCHAR result[64];
	_sntprintf(result,
			  sizeof(result) / sizeof(*result),
			  _T("%d.%d"),
			  versioninfo.dwMajorVersion,
			  versioninfo.dwMinorVersion);

	return env->NewString((const jchar*) result, _tcslen(result));
}

/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getDefaultEncoding
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getDefaultEncoding(JNIEnv *env, jclass) {
	// OEMコードページを取得する
	UINT cp = GetOEMCP();
	// 対応表は以下の場所にある
	// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcelocal/html/clrefcodepages.asp

	_TCHAR buff[256];
	_TCHAR* encoding = NULL;
	switch (cp) {
	case 708: // Arabic (ASMO 708) 
		break;
	case 709: // Arabic (ASMO 449+, BCON V4) 
		break;
	case 710: // Arabic (Transparent Arabic) 
		break;
	case 720: //  Arabic (Transparent ASMO) 
		break;

	case 437: // MicrosoftR MS-DOSR United States 
	case 737: // Greek (OEM 437G) 
	case 775: // Baltic 
	case 850: // MS-DOS Multilingual (Latin I) 
	case 852: // MS-DOS (Latin II) 
	case 855: // IBM Cyrillic (primarily Russian) 
	case 857: // IBM Turkish 
	case 858: // MS-DOS Multilingual (Latin I + Euro) 
	case 860: // MS-DOS Portuguese 
	case 861: // MS-DOS Icelandic 
	case 862: // Hebrew 
	case 863: // MS-DOS Canadian-French 
	case 864: // Arabic 
	case 865: // MS-DOS Nordic 
	case 866: // MS-DOS Russian 
	case 869: // MS-DOS Modern Greek 
	case 874: // Thai 
	case 932: // Japanese Shift-JIS
	case 949: // Korean 
	case 950: // Chinese: Taiwan; Hong Kong SAR, PRC (Traditional Big5) 
	case 1250: // MicrosoftR WindowsR 3.1 Central European
	case 1251: // Windows 3.1 Cyrillic 
	case 1252: // Windows 3.1 US  
	case 1253: // Windows 3.1 Greek 
	case 1254: // Windows 3.1 Turkish 
	case 1255: // Hebrew 
	case 1256: // Arabic 
	case 1257: // Baltic 
	case 1258: // Vietnamese
		_stprintf(buff, _T("Cp%d"), cp);
		encoding = buff;
		break;

	case 936: // Chinese: PRC; Macau SAR, PRC; Singapore (Simplified GBK)  
		break;
	case 1200: // Unicode (BMP of ISO 10646) 
		break;
	case 1361: // Korean (Johab) 
		break;
	case 20000: // CNS - Taiwan 
		break;
	case 20001: // TCA - Taiwan 
		break;
	case 20002: // E-Ten - Taiwan 
		break;
	case 20003: // IBM5550 - Taiwan 
		break;
	case 20004: // TeleText - Taiwan 
		break;
	case 20005: // Wang - Taiwan 
		break;
	case 20127: // US ASCII 
		break;
	case 20261: // T.61 
		break;
	case 20269: // ISO-6937 
		break;
	case 20866: // Ukrainian - KOI8-U 
		break;
	case 21027: // Ext Alpha Lowercase 
		break;
	case 21866: // Ukranian - KOI8 
		break;
	case 28591: // ISO 8859-1 Latin I 
		encoding = _T("8859_1");
		break;

	case 28592: // ISO 8859-2 Eastern Europe 
		encoding = _T("8859_2");
		break;

	case 28593: // ISO 8859-3 Turkish 
		encoding = _T("8859_3");
		break;

	case 28594: // ISO 8859-4 Baltic 
		encoding = _T("8859_4");
		break;

	case 28595: // ISO 8859-5 Cyrillic 
		encoding = _T("8859_5");
		break;

	case 28596: // ISO 8859-6 Arabic 
		encoding = _T("8859_6");
		break;

	case 28597: // ISO 8859-7 Greek 
		encoding = _T("8859_7");
		break;

	case 28598: // ISO 8859-8 Hebrew 
		encoding = _T("8859_8");
		break;

	case 28599: // ISO 8859-9 Latin Alphabet No.5 
		encoding = _T("8859_9");
		break;

	case 29001: // Europa 3 
		break;
	}

	if (! encoding) {
		// デフォルトのエンコーディング
		encoding = _T("8859_1");
	}
	return env->NewString((const jchar*) encoding, _tcslen(encoding));
}

/*
 * Class:     gnu_classpath_VMSystemProperties
 * Method:    getCurrentDirectory
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gnu_classpath_VMSystemProperties_getCurrentDirectory
(JNIEnv *env, jclass) {
	arguments* args = get_current_arguments();
	return env->NewString((const jchar*) args->current_directory,
						  _tcslen(args->current_directory));
}

/*
 * Class:     gnu_classpath_VMProperties
 * Method:    postInit
 * Signature: (Ljava/util/Properties;)V
 */
JNIEXPORT void JNICALL Java_gnu_classpath_VMSystemProperties_postInit(JNIEnv *env, jclass, jobject properties) {
	jclass klass = env->GetObjectClass(properties);
	jmethodID mid = env->GetMethodID(klass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
	// java.class.path を設定する
	const _TCHAR* keystr = _T("java.class.path");
	jstring key = env->NewString((const jchar*) keystr, _tcslen(keystr));
	if (key == NULL) {
		return;
	}
	const _TCHAR* valuestr = get_system_classpath(); 
	jstring value = env->NewString((const jchar*) valuestr, _tcslen(valuestr));
	if (value == NULL) {
		return;
	}
	env->CallObjectMethod(properties, mid, key, value);
	if (env->ExceptionCheck()) {
		return;
	}
	
	// java.boot.class.path を設定する（java.lang.VMClassLoaderが参照している）
	keystr = _T("java.boot.class.path");
	key = env->NewString((const  jchar*) keystr, _tcslen(keystr));
	if (key == NULL) {
		return;
	}
	valuestr = get_boot_classpath(); 
	value = env->NewString((const jchar*) valuestr, _tcslen(valuestr));
	if (value == NULL) {
		return;
	}
	env->CallObjectMethod(properties, mid, key, value);
	if (env->ExceptionCheck()) {
		return;
	}

	// カスタムシステムプロパティを設定する
	arguments* args = get_current_arguments();
	for (int i = 0; i < args->properties_count; ++i) {
		const _TCHAR* k = arguments_get_property_key(args, i);
		const _TCHAR* v = arguments_get_property(args, k);
		key = env->NewString((const jchar*) k, _tcslen(k));
		if (env->ExceptionCheck()) {
			return;
		}
		value = env->NewString((const jchar*) v, _tcslen(v));
		if (env->ExceptionCheck()) {
			return;
		}
		env->CallObjectMethod(properties, mid, key, value);
		if (env->ExceptionCheck()) {
			return;
		}
	}
}

/*
 * Class:     java_lang_VMClass
 * Method:    isInstance
 * Signature: (Ljava/lang/Class;Ljava/lang/Object;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMClass_isInstance(JNIEnv *env, jclass, jclass klass, jobject o) {
	if (o) {
		ClassFile* s = get_ClassFile(o);
		jobject static_data = get_static_data(env, klass);
		ClassFile* t = get_ClassFile(static_data);
		return (jboolean) is_assignable_from(get_current_frame(((jni_env*) env)->jni_frame), s, t);
	} else {
		return JNI_FALSE;
	}
}

/*
 * Class:     java_lang_VMClass
 * Method:    isAssignableFrom
 * Signature: (Ljava/lang/Class;Ljava/lang/Class;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMClass_isAssignableFrom(JNIEnv *env, jclass, jclass klass, jclass c) {
	jobject static_data = get_static_data(env, klass);
	ClassFile* t = get_ClassFile(static_data);
	static_data = get_static_data(env, c);
	ClassFile* s = get_ClassFile(static_data);
	return (jboolean) is_assignable_from(get_current_frame(((jni_env*) env)->jni_frame), s, t);
}

/*
 * Class:     java_lang_VMClass
 * Method:    isInterface
 * Signature: (Ljava/lang/Class;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMClass_isInterface(JNIEnv *env, jclass, jclass klass) {
	jobject static_data = get_static_data(env, klass);
	ClassFile* cfile = get_ClassFile(static_data);
	return (jboolean) is_interface(cfile->access_flags);
}

/*
 * Class:     java_lang_VMClass
 * Method:    isPrimitive
 * Signature: (Ljava/lang/Class;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMClass_isPrimitive(JNIEnv *env, jclass, jclass klass) {
	jobject static_data = get_static_data(env, klass);
	ClassFile* cfile = get_ClassFile(static_data);
	const java_utf8* class_name = cfile->this_class_name;
	if (is_primitive_class_name(class_name)) {
		return JNI_TRUE;
	} else {
		return JNI_FALSE;
	}
}

/*
 * Class:     java_lang_VMClass
 * Method:    getName
 * Signature: (Ljava/lang/Class;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_java_lang_VMClass_getName(JNIEnv *env, jclass, jclass klass) {
	jobject static_data = get_static_data(env, klass);
	if (! static_data) {
		return NULL;
	}

	ClassFile* cfile = get_ClassFile(static_data);
	const java_utf8* class_name = cfile->this_class_name;
	char* name = (char*) malloc(strlen(class_name) + 1);
	if (name == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	char* start = name;

	while (*class_name) {
		char c = *class_name;
		if (c == '/') {
			c = '.';
		}
		*name = c;
		class_name++;
		name++;
	}
	*name = '\0';

	jstring ret = env->NewStringUTF(start);
	free(start);
	return ret;
}

/*
 * Class:     java_lang_VMClass
 * Method:    getSuperclass
 * Signature: (Ljava/lang/Class;)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMClass_getSuperclass(JNIEnv *env, jclass, jclass klass) {
	jobject static_data = get_static_data(env, klass);
	ClassFile* cfile = get_ClassFile(static_data);
	if (cfile->super_class_name == NULL) {
		return NULL;
	} else {
		class_loader* l = get_defining_loader(cfile);
		return find_class(env, cfile->super_class_name, l);
	}
}

/*
 * Class:     java_lang_VMClass
 * Method:    getInterfaces
 * Signature: (Ljava/lang/Class;)[Ljava/lang/Class;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_VMClass_getInterfaces(JNIEnv *env, jclass, jclass klass) {
	jobject static_data = get_static_data(env, klass);	
	ClassFile* cfile = get_ClassFile(static_data);

	// 格納すべきインタフェースの数を数える
	u2 interfaces_count = cfile->interfaces_count;
	jsize array_size = interfaces_count;

	// 配列を確保する
	jclass elementclass = env->FindClass("java/lang/Class");
	if (elementclass == NULL) {
		return NULL;
	}
	jobjectArray array = env->NewObjectArray(array_size, elementclass, NULL);
	if (array == NULL) {
		return NULL;
	}
	
	// 配列に Class のインスタンスを格納していく
	jsize array_index = 0;
	jfieldID fid = env->GetFieldID(elementclass, "vmdata", "Ljava/lang/Object;");
	class_loader* loader = cfile->defining_loader;
	for (u2 i = 0; i < interfaces_count; ++i) {
		int idx = cfile->interfaces[i];
		const java_utf8* iname = get_CONSTANT_Class_info(cfile, idx);
		ClassFile* icfile = find_ClassFile(get_current_frame(((jni_env*) env)->jni_frame), loader, iname);
		jobject value = env->AllocObject(elementclass);
		env->SetObjectField(value, fid, get_static_data(icfile));
		env->SetObjectArrayElement(array, array_index++, value);
	}

	return array;
}

/*
 * Class:     java_lang_VMClass
 * Method:    getComponentType
 * Signature: (Ljava/lang/Class;)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMClass_getComponentType(JNIEnv *env, jclass, jclass klass) {
	jobject static_data = get_static_data(env, klass);	
	ClassFile* cfile = get_ClassFile(static_data);
	if (is_primitive_array_class(cfile)) {
		// プリミティブ配列の場合
		java_utf8* class_name = NULL;
		switch (cfile->this_class_name[1]) {
		case 'Z':	// boolean
			class_name = "boolean";
			break;
		case 'B':	// byte
			class_name = "byte";
			break;
		case 'S':	// short
			class_name = "short";
			break;
		case 'C':	// char
			class_name = "char";
			break;
		case 'I':	// int
			class_name = "int";
			break;
		case 'J':	// long
			class_name = "long";
			break;
		case 'F':	// float
			class_name = "float";
			break;
		case 'D':	// double
			class_name = "double";
			break;
		case 'V':	// void
			// void型のパラメータというのはありえないが、ここでは気にしない
			class_name = "void";
			break;
		}
		if (class_name != NULL) {
			ClassFile* cfile = load_primitive_ClassFile(get_current_frame(((jni_env*) env)->jni_frame), class_name);
			if (cfile == NULL) {
				return NULL;
			}
			jobject static_data = get_static_data(cfile);
			return (jclass) find_primitive_class_object(get_current_frame(((jni_env*) env)->jni_frame), static_data);
		} else {
			return NULL;
		}

	} else {
		// プリミティブ配列以外
		const java_utf8* compname = get_array_component_name(cfile);
		if (compname) {
			return find_class(env, compname, get_defining_loader(cfile));
		} else {
			// 配列以外の場合はnullを返す
			return NULL;
		}
	}
}

/*
 * Class:     java_lang_VMClass
 * Method:    getModifiers
 * Signature: (Ljava/lang/Class;Z)I
 */
JNIEXPORT jint JNICALL Java_java_lang_VMClass_getModifiers(JNIEnv *env, jclass, jclass klass, jboolean ignoreInnerClassesAttrib) {
	// ToDo: 引数 ignoreInnerClassesAttrib をサポートする
	jobject static_data = get_static_data(env, klass);	
	ClassFile* cfile = get_ClassFile(static_data);
	return (jint) cfile->access_flags;
}

/*
 * Class:     java_lang_VMClass
 * Method:    getDeclaringClass
 * Signature: (Ljava/lang/Class;)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMClass_getDeclaringClass(JNIEnv *env, jclass, jclass klass) {
	jobject static_data = get_static_data(env, klass);	
	ClassFile* cfile = get_ClassFile(static_data);
	InnerClasses_attribute* ic = cfile->innerclasses_attribute;
	if (ic != NULL) {
		// 定義ローダを取得する（カレントクラスは java.lang.VMClass であるため）
		class_loader* loader = get_defining_loader(cfile);
		for (int i = 0; i < ic->number_of_classes; ++i) {
			classes_info* info = &ic->classes[i];
			if (info->outer_class_info_index) {
				// 外部クラスを返す
				const java_utf8* name = get_CONSTANT_Class_info(cfile, info->outer_class_info_index);
				return find_class(env, name, loader);
			}
		}
	}
	// 外部クラスが存在しない場合はNULLを返す
	return NULL;
}

/*
 * Class:     java_lang_VMClass
 * Method:    getDeclaredClasses
 * Signature: (Ljava/lang/Class;Z)[Ljava/lang/Class;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_VMClass_getDeclaredClasses(JNIEnv* env, jclass, jclass klass, jboolean publiconly) {
	jobject static_data = get_static_data(env, klass);	
	ClassFile* cfile = get_ClassFile(static_data);
	InnerClasses_attribute* ic = cfile->innerclasses_attribute;
	jobjectArray result = NULL;
	if (ic != NULL) {
		int limit = 8;
		const java_utf8** names = (const java_utf8**) malloc(sizeof(java_utf8*) * limit);
		if (names == NULL) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}

		// 対象となるクラス名と、その数を取得する
		int num = 0;
		for (int i = 0; i < ic->number_of_classes; ++i) {
			classes_info* info = &ic->classes[i];
			if (info->inner_class_info_index) {
				if (! publiconly || (publiconly && is_public(info->inner_class_access_flags))) {
					// 内部クラスが存在し、かつ指定されたアクセス条件を満たす場合
					const java_utf8* name = get_CONSTANT_Class_info(cfile, info->inner_class_info_index);
					if (num >= limit) {
						// バッファを拡張する
						limit += 8;
						names = (const java_utf8**) realloc(names, sizeof(java_utf8*) * limit);
						if (names == NULL) {
							fatal_error(FATAL_ERROR_NO_MEMORY);
						}
					}
					names[num++] = name;
				}
			}
		}
		// 配列内の名前を持つ Class オブジェクトを作成する
		jclass element_class = env->FindClass("java/lang/Class");
		if (element_class) {
			result = env->NewObjectArray(num, element_class, NULL);
			if (result) {
				// 外部クラスの定義ローダを使用してクラスをロードする
				// （カレントクラスは java.lang.VMClass であるため）
				class_loader* loader = get_defining_loader(cfile);
				for (int i = 0; i < num; ++i) {
					jclass elem = find_class(env, names[i], loader);
					if (elem) {
						env->SetObjectArrayElement(result, i, elem);
					}
				}
			}
		}
		free(names);
	} else {
		// 内部クラス情報を持たない場合は、大きさが０の配列を作成して返す
		jclass element_class = env->FindClass("java/lang/Class");
		if (element_class) {
			result = env->NewObjectArray(0, element_class, NULL);
		}
	}
	return result;
}

/*
 * Class:     java_lang_VMClass
 * Method:    getDeclaredFields
 * Signature: (Ljava/lang/Class;Z)[Ljava/lang/reflect/Field;
 */
JNIEXPORT jobjectArray JNICALL
Java_java_lang_VMClass_getDeclaredFields(JNIEnv *env,
										 jclass,
										 jclass klass,
										 jboolean publicOnly) {
	jobject static_data = get_static_data(env, klass);	
	ClassFile* cfile = get_ClassFile(static_data);
	// ここに来た時点で、ClassFile が初期化されていない可能性があるため、
	// 再度ロード処理を行う(find_ClassFile()内で初期化が行われる）
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	cfile = find_ClassFile(current_frame,
						   get_defining_loader(cfile),
						   cfile->this_class_name);
	if (exception_occurred(current_frame)) {
		return NULL;
	}

	// 格納すべきフィールドの数を数える
	u2 fields_count = cfile->fields_count;
	jsize array_size = 0;
	for (u2 i = 0; i < fields_count; ++i) {
		field_info* finfo = &cfile->fields[i];
		if (publicOnly) {
			if (is_public(finfo->access_flags)) {
				array_size++;
			}
		} else {
			array_size++;
		}
	}

	// 配列を確保する
	jclass elementclass = env->FindClass("java/lang/reflect/Field");
	if (elementclass == NULL) {
		return NULL;
	}
	jobjectArray array = env->NewObjectArray(array_size, elementclass, NULL);
	if (array == NULL) {
		return NULL;
	}
	
	// 配列に Field のインスタンスを格納していく
	jsize array_index = 0;
	jmethodID mid = env->GetMethodID(elementclass, "<init>", "(Ljava/lang/Class;Ljava/lang/String;I)V");
	for (int i = 0; i < fields_count; ++i) {
		field_info* finfo = &cfile->fields[i];
		if (publicOnly && !is_public(finfo->access_flags)) {
			continue;
		}
		jstring namestr = env->NewStringUTF(finfo->name);
		if (namestr == NULL) {
			return NULL;
		}
		jobject value = env->NewObject(elementclass, mid, klass, namestr, i);
		if (value == NULL) {
			return NULL;
		}
		env->SetObjectArrayElement(array, array_index++, value);
		if (env->ExceptionCheck()) {
			return NULL;
		}
	}

	return array;
}

/*
 * Class:     java_lang_VMClass
 * Method:    getDeclaredMethods
 * Signature: (Ljava/lang/Class;Z)[Ljava/lang/reflect/Method;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_VMClass_getDeclaredMethods(JNIEnv *env, jclass, jclass klass, jboolean publicOnly) {
	jobject static_data = get_static_data(env, klass);	
	ClassFile* cfile = get_ClassFile(static_data);

	// ここに来た時点で、ClassFile が初期化されていない可能性があるため、
	// 再度ロード処理を行う(find_ClassFile()内で初期化が行われる）
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	cfile = find_ClassFile(current_frame,
						   get_defining_loader(cfile),
						   cfile->this_class_name);
	if (exception_occurred(current_frame)) {
		return NULL;
	}

	// <init>と<clinit>以外のメソッドを数える
	u2 methods_count = cfile->methods_count;
	jsize array_size = 0;
	for (u2 i = 0; i < methods_count; ++i) {
		method_info* minfo = &cfile->methods[i];
		if (INIT_METHOD_NAME != minfo->name && CLINIT_METHOD_NAME != minfo->name) {
			array_size++;
		}
	}

	// 配列を確保する
	jclass elementclass = env->FindClass("java/lang/reflect/Method");
	if (elementclass == NULL) {
		return NULL;
	}
	jobjectArray array = env->NewObjectArray(array_size, elementclass, NULL);
	if (array == NULL) {
		return NULL;
	}
	
	// 配列に Method のインスタンスを格納していく
	jsize array_index = 0;
	jmethodID mid = env->GetMethodID(elementclass, "<init>", "(Ljava/lang/Class;Ljava/lang/String;I)V");
	for (int i = 0; i < methods_count; ++i) {
		method_info* minfo = &cfile->methods[i];
		if (INIT_METHOD_NAME != minfo->name && CLINIT_METHOD_NAME != minfo->name) {
			jstring namestr = env->NewStringUTF(minfo->name);
			jobject value = env->NewObject(elementclass, mid, klass, namestr, i);
			if (namestr == NULL || value == NULL) {
				return NULL;
			}
			env->SetObjectArrayElement(array, array_index++, value);
			if (env->ExceptionCheck()) {
				return NULL;
			}
		}
	}

	return array;
}

/*
 * Class:     java_lang_VMClass
 * Method:    getDeclaredConstructors
 * Signature: (Ljava/lang/Class;Z)[Ljava/lang/reflect/Constructor;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_VMClass_getDeclaredConstructors(JNIEnv *env, jclass, jclass klass, jboolean publiconly) {
	jobject static_data = get_static_data(env, klass);
	ClassFile* cfile = get_ClassFile(static_data);
	// ここに来た時点で、ClassFile が初期化されていない可能性があるため、
	// 再度ロード処理を行う(find_ClassFile()内で初期化が行われる）
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	cfile = find_ClassFile(current_frame,
						   get_defining_loader(cfile),
						   cfile->this_class_name);
	if (exception_occurred(current_frame)) {
		return NULL;
	}

	// 対象となるコンストラクタの数を数える
	u2 count = 0;
	for (u2 i = 0; i < cfile->methods_count; ++i) {
		method_info* minfo = &cfile->methods[i];
		if (INIT_METHOD_NAME == minfo->name) {
			if (publiconly) {
				if (is_public(minfo->access_flags)) {
					count++;
				}
			} else {
				count++;
			}
		}
	}

	// java.lang.reflect.Constructor クラスをロードする
	jclass constructor_class = env->FindClass("java/lang/reflect/Constructor");
	if (constructor_class == NULL) {
		return NULL;
	}
	if (constructor_class == NULL) {
		return NULL;
	}

	// 配列を作成する
	jobjectArray constructors_array = env->NewObjectArray(count, constructor_class, NULL);
	if (constructors_array == NULL) {
		return NULL;
	}

	// コンストラクタ Constructor(Class declaringClass,int slot)
	jmethodID mid = env->GetMethodID(constructor_class, "<init>", "(Ljava/lang/Class;I)V");
	if (mid == NULL) {
		jclass vmerror_class = env->FindClass("java/lang/VirtualMachineError");
		if (vmerror_class != NULL) {
			env->ThrowNew(vmerror_class, "Could not get constructor method id of 'java/lang/reflect/Constructor'");
		}
		return NULL;
	}
	
	jint array_index = 0;
	for (int i = 0; i < cfile->methods_count; ++i) {
		method_info* minfo = &cfile->methods[i];
		method_info* found = NULL;
		if (INIT_METHOD_NAME == minfo->name) {
			if (publiconly) {
				if (is_public(minfo->access_flags)) {
					found = minfo;
				}
			} else {
				found = minfo;
			}
		}
		if (found != NULL) {
			// Constructorを作成する
			jobject constructor = env->NewObject(constructor_class,
				                                 mid,
												 klass,
												 i);
			if (constructor == NULL) {
				return NULL;
			}

			// 配列に格納する
			env->SetObjectArrayElement(constructors_array, array_index++, constructor);
			if (env->ExceptionCheck()) {
				return NULL;
			}
		}
	}
		
	return constructors_array;
}

/*
 * Class:     java_lang_VMClass
 * Method:    getClassLoader
 * Signature: (Ljava/lang/Class;)Ljava/lang/ClassLoader;
 */
JNIEXPORT jobject JNICALL Java_java_lang_VMClass_getClassLoader(JNIEnv *env, jclass, jclass clazz) {
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);
	return get_ClassLoader(cfile->defining_loader);
}

/*
 * Class:     java_lang_VMClass
 * Method:    forName
 * Signature: (Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMClass_forName(JNIEnv *env, jclass,
														jstring name,
														jboolean initialize,
														jobject loader) {
	const char* utf = env->GetStringUTFChars(name, NULL);
	if (env->ExceptionCheck()) {
		return NULL;
	}
	// 文字列中のピリオドをすべてスラッシュで置き換える
	char* native_name = (char*) malloc(strlen(utf) + 1);
	if (! native_name) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	strcpy(native_name, utf);
	char* work = native_name;
	while (*work) {
		if (*work == '.') {
			*work = '/';
		}
		work++;
	}
	env->ReleaseStringUTFChars(name, utf);

	if (env->ExceptionCheck()) {
		// メモリを開放してからリターンする
		free(native_name);
		return NULL;
	}
	class_loader* l = get_class_loader(loader);
	jclass clazz = find_class(env, native_name, l);
	jthrowable exp = env->ExceptionOccurred();
	if (exp) {
		// NoClassDefFoundErrorの場合は、ClassNotFoundExceptionに取り替える
		jclass ncdfe = env->FindClass("java/lang/NoClassDefFoundError");
		if (env->IsInstanceOf(exp, ncdfe)) {
			jclass cnfe = env->FindClass("java/lang/ClassNotFoundException");
			if (cnfe) {
				env->ThrowNew(cnfe, native_name);
			}
		}
	}
	free(native_name);
	return clazz;
}

/*
 * Class:     java_lang_VMClass
 * Method:    isArray
 * Signature: (Ljava/lang/Class;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMClass_isArray(JNIEnv *env, jclass, jclass klass) {
	jobject static_data = get_static_data(env, klass);
	ClassFile* cfile = get_ClassFile(static_data);
	if (cfile->this_class_name[0] == '[') {
		return JNI_TRUE;
	} else {
		return JNI_FALSE;
	}
}

/*
 * Class:     java_lang_VMClass
 * Method:    throwException
 * Signature: (Ljava/lang/Throwable;)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMClass_throwException(JNIEnv *env, jclass, jthrowable t) {
	throw_exception(get_current_frame(((jni_env*) env)->jni_frame), t);
}

/*
 * Class:     java_lang_VMClass
 * Method:    getDeclaredAnnotations
 * Signature: (Ljava/lang/Class;)[Ljava/lang/annotation/Annotation;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_VMClass_getDeclaredAnnotations(JNIEnv *env, jclass, jclass) {
	// ToDo: 実装する
	jclass clazz = env->FindClass("java/lang/annotation/Annotation");
	if (clazz) {
		return env->NewObjectArray(0, clazz, NULL);
	} else {
		return NULL;
	}
}

/*
 * Class:     java_lang_VMClass
 * Method:    getEnclosingClass
 * Signature: (Ljava/lang/Class;)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMClass_getEnclosingClass(JNIEnv *env, jclass, jclass) {
	// ToDo: 実装する
	return NULL;
}

/*
 * Class:     java_lang_VMClass
 * Method:    getEnclosingConstructor
 * Signature: (Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
 */
JNIEXPORT jobject JNICALL Java_java_lang_VMClass_getEnclosingConstructor(JNIEnv *, jclass, jclass) {
	// ToDo: 実装する
	return NULL;
}


/*
 * Class:     java_lang_VMClass
 * Method:    getEnclosingMethod
 * Signature: (Ljava/lang/Class;)Ljava/lang/reflect/Method;
 */
JNIEXPORT jobject JNICALL Java_java_lang_VMClass_getEnclosingMethod(JNIEnv *, jclass, jclass) {
	// ToDo: 実装する
	return NULL;
}


/*
 * Class:     java_lang_VMClass
 * Method:    getClassSignature
 * Signature: (Ljava/lang/Class;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_java_lang_VMClass_getClassSignature(JNIEnv *env, jclass vmclass, jclass clazz) {
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);
	u2 sig_index = cfile->signature_index;
	if (sig_index) {
		return env->NewStringUTF((const char*) cfile->constant_pool[sig_index]);
	}
	return NULL;
}

/*
 * Class:     java_lang_VMClass
 * Method:    isAnonymousClass
 * Signature: (Ljava/lang/Class;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMClass_isAnonymousClass(JNIEnv *, jclass, jclass) {
	// ToDo: 実装する
	return JNI_FALSE;
}

/*
 * Class:     java_lang_VMClass
 * Method:    isLocalClass
 * Signature: (Ljava/lang/Class;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMClass_isLocalClass(JNIEnv *, jclass, jclass) {
	// ToDo: 実装する
	return JNI_FALSE;
}

/*
 * Class:     java_lang_VMClass
 * Method:    isMemberClass
 * Signature: (Ljava/lang/Class;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMClass_isMemberClass(JNIEnv *, jclass, jclass) {
	// ToDo: 実装する
	return JNI_FALSE;
}

static jclass define_class(JNIEnv* env, const char* name, jobject cl, const jbyte* buf, int offset, int len, jobject pd) {
	class_loader* loader = get_class_loader(cl);
	int namelen = strlen(name);
	char* internal_name = (char*) malloc(namelen + 1);
	if (! internal_name) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	for (int i = 0; i < namelen; ++i) {
		char c = name[i];
		if (c == '.') {
			c = '/';
		}
		internal_name[i] = c;
	}
	internal_name[namelen] = '\0';
	ClassFile* cfile = define_ClassFile(get_current_frame(((jni_env*) env)->jni_frame),
										intern_utf8(internal_name),
										loader,
										(const char*) buf,
										offset,
										len);
	free(internal_name);
	if (cfile == NULL || exception_occurred(get_current_frame(((jni_env*) env)->jni_frame))) {
		return NULL;
	}
	jobject static_data = get_static_data(cfile);
	jclass ret = NULL;
	if (static_data != NULL) {
		ret = (jclass) find_class_object(get_current_frame(((jni_env*) env)->jni_frame), static_data, pd);
		if (ret != NULL) {
			jni_env* jenv = (jni_env*) env;
			new_local_reference(jenv->jni_frame, ret);
		}
	}

	return ret;
}


/*
 * Class:     java_lang_VMClassLoader
 * Method:    defineClass
 * Signature: (Ljava/lang/ClassLoader;Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMClassLoader_defineClass(JNIEnv *env, jclass, jobject cl, jstring name, jbyteArray data, jint offset, jint len, jobject pd) {
	const char* n = env->GetStringUTFChars(name, NULL);
	if (env->ExceptionCheck()) {
		return NULL;
	}
	jbyte* buf = env->GetByteArrayElements(data, NULL);
	if (env->ExceptionCheck()) {
		env->ReleaseStringUTFChars(name, n);
		return NULL;
	}

	jclass result = define_class(env, n, cl, buf, offset, len, pd);

	env->ReleaseStringUTFChars(name, n);
	env->ReleaseByteArrayElements(data, buf, 0);

	return result;
}

/*
 * Class:     java_lang_VMClassLoader
 * Method:    resolveClass
 * Signature: (Ljava/lang/Class;)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMClassLoader_resolveClass(JNIEnv* env, jclass, jclass) {
	// すでにresolve済み（のはず）
	return;
}	

/**
 * VMClass.forName(), VMClassLoader_loadClass()共通関数
 */
static jclass load_class(JNIEnv* env, jstring name) {
	const char* utf = env->GetStringUTFChars(name, NULL);
	if (env->ExceptionCheck()) {
		return NULL;
	}
	// 文字列中のピリオドをすべてスラッシュで置き換える
	char* p = (char*) malloc(strlen(utf) + 1);
	if (p == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	strcpy(p, utf);
	char* work = p;
	while (*work) {
		if (*work == '.') {
			*work = '/';
		}
		work++;
	}
	env->ReleaseStringUTFChars(name, utf);
	if (env->ExceptionCheck()) {
		// メモリを開放してからリターンする
		free(p);
		return NULL;
	}

	jclass result = env->FindClass(p);
	free(p);

	if (result == NULL) {
		// NoClassDefFoundErrorが発生しているかを調べる
		jthrowable exp = env->ExceptionOccurred();
		jclass error_class = env->FindClass("java/lang/NoClassDefFoundError");
		if (error_class != NULL) {
			if (env->IsInstanceOf(exp, error_class)) {
				// NoClassDefFoundErrorの場合は、例外をクリアする
				env->ExceptionClear();
			}
		}
	}

	return result;
}

/*
 * Class:     java_lang_VMClassLoader
 * Method:    loadClass
 * Signature: (Ljava/lang/String;Z)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMClassLoader_loadClass(JNIEnv *env, jclass, jstring name, jboolean) {
	return load_class(env, name);
}

/*
 * Class:     java_lang_VMClassLoader
 * Method:    getPrimitiveClass
 * Signature: (C)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMClassLoader_getPrimitiveClass(JNIEnv *env, jclass, jchar type) {
	char* classname = NULL;
	switch (type) {
	case 'Z':
		classname = "boolean";
		break;
	case 'S':
		classname = "short";
		break;
	case 'C':
		classname = "char";
		break;
	case 'I':
		classname = "int";
		break;
	case 'F':
		classname = "float";
		break;
	case 'B':
		classname = "byte";
		break;
	case 'J':
		classname = "long";
		break;
	case 'D':
		classname = "double";
		break;
	case 'V':
		classname = "void";
		break;
	default:
		assert(false);
	}
	ClassFile* cfile = load_primitive_ClassFile(get_current_frame(((jni_env*) env)->jni_frame), classname);
	if (cfile == NULL) {
		return NULL;
	}
	jobject static_data = get_static_data(cfile);

	return (jclass) find_primitive_class_object(get_current_frame(((jni_env*) env)->jni_frame), static_data);
}

/*
 * Class:     java_lang_VMClassLoader
 * Method:    defaultAssertionStatus
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMClassLoader_defaultAssertionStatus(JNIEnv *env, jclass)  {
	return g_assertion_status;
}

/*
 * Class:     java_lang_VMClassLoader
 * Method:    findLoadedClass
 * Signature: (Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMClassLoader_findLoadedClass(JNIEnv *env, jclass, jobject cl, jstring name) {
	const char* utf = env->GetStringUTFChars(name, NULL);
	if (env->ExceptionCheck()) {
		return NULL;
	}
	// 文字列中のピリオドをすべてスラッシュで置き換える
	char* native_name = (char*) malloc(strlen(utf) + 1);
	if (! native_name) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	strcpy(native_name, utf);
	char* work = native_name;
	while (*work) {
		if (*work == '.') {
			*work = '/';
		}
		work++;
	}
	env->ReleaseStringUTFChars(name, utf);
	if (env->ExceptionCheck()) {
		// メモリを開放してからリターンする
		free(native_name);
		return NULL;
	}
	class_loader* l = get_class_loader(cl);
	jclass clazz
		= find_loaded_class_object(l,
								   get_current_frame(((jni_env*) env)->jni_frame),
								   intern_utf8(native_name));
	free(native_name);
	return clazz;
}

/*
 * Class:     java_lang_VMThrowable
 * Method:    fillInStackTrace
 * Signature: (Ljava/lang/Throwable;)Ljava/lang/VMThrowable;
 */
JNIEXPORT jobject JNICALL Java_java_lang_VMThrowable_fillInStackTrace(JNIEnv *env, jclass vmtclass, jthrowable obj) {
	jni_env* jenv = (jni_env*) env;
	ClassFile* exception_cfile = get_ClassFile(obj);
	unsigned int count = 0;
	unsigned int size = 32;
	frame* trace = (frame*) malloc(sizeof(frame) * size);
	bool in_this_exception = false;
	bool fill = false;
	for (frame* current_frame = get_current_frame(jenv->jni_frame);
				current_frame->previous_frame != NULL;
				current_frame = current_frame->previous_frame) {
		ClassFile* cfile = current_frame->current_class_file;
		if (! fill) {
			if (exception_cfile->this_class_name == cfile->this_class_name) {
				// 例外生成時のフレームは読み飛ばす
				in_this_exception = true;
				continue;
			} else if (in_this_exception) {
				// 読み取り対象となるフレーム位置に到達
				fill = true;
			}
		}
		if (fill) {
			method_info* minfo = current_frame->current_method_info;

			// 内部情報を設定する
			if (count >= size) {
				// 再確保
				size += 32;
				frame* tmp = (frame*) realloc(trace, sizeof(frame) * size);
				if (! tmp) {
					// エラーとはせず、ここまでの処理で完了とする
					break;
				}
				trace = tmp;
			}
			// frameの内容を丸ごとコピー
			trace[count++] = *current_frame;
		}
	}
	// 内部情報を保存するための byte[] を生成し、内部情報をコピーする
	jobject vmtobj = NULL;
	if (count > 0) {
		int size = sizeof(frame) * count;
		jbyteArray ba = env->NewByteArray(size);
		if (ba != NULL) {
			env->SetByteArrayRegion(ba, 0, size, (jbyte*) trace);
			// VMThrowableの新しいインスタンスを作成する
			jmethodID mid = env->GetMethodID(vmtclass, "<init>", "()V");
			vmtobj = env->NewObject(vmtclass, mid);
			if (vmtobj != NULL) {
				// フィールド "internalElements" に設定する
				jfieldID internalElements = env->GetFieldID(vmtclass, "internalElements", "[B");
				env->SetObjectField(vmtobj, internalElements, ba);
			}
		}
	}
	free(trace);

	return vmtobj;
}

/*
 * Class:     java_lang_VMThrowable
 * Method:    getStackTrace
 * Signature: (Ljava/lang/Throwable;)[Ljava/lang/StackTraceElement;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_VMThrowable_getStackTrace(JNIEnv *env, jobject vmtobj, jthrowable t) {
	jobjectArray elementArray = NULL;

	// フィールド "internalElements" から、内部情報を取得する
	jclass vmtclass = env->GetObjectClass(vmtobj);
	if (vmtclass == NULL) {
		return NULL;
	}
	jfieldID fid = env->GetFieldID(vmtclass, "internalElements", "[B");
	jbyteArray ba = (jbyteArray) env->GetObjectField(vmtobj, fid);
	if (ba == NULL) {
		return NULL;
	}
	
	// スタックトレースの数を計算する
	jsize size = env->GetArrayLength(ba);
	unsigned int count = size / sizeof(frame);
	frame* trace = (frame*) env->GetByteArrayElements(ba, NULL);
	jclass elementclass = env->FindClass("java/lang/StackTraceElement");
	if (elementclass == NULL) {
		return NULL;
	}

	jobject* elements = (jobject*) malloc(sizeof(jobject) * count);
	if (elements == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}

	// StackTraceElementsオブジェクトを生成する
	for (unsigned int elem_index = 0; elem_index < count; ++elem_index) {
		frame* trc = &trace[elem_index];
		ClassFile* cfile = trc->current_class_file;
		method_info* minfo = trc->current_method_info;
		const java_utf8* methodname = minfo->name;
		bool native = is_native(minfo->access_flags);
		int linenumber = -1;
		const java_utf8* classname = cfile->this_class_name;
		Code_attribute* code = get_Code_attribute(cfile, minfo);
		const java_utf8* filename = NULL;
		if (! native && code != NULL) {
			filename = get_SourceFile(cfile);
			unsigned int pc = trc->current_pc;
			linenumber = get_LineNumber(code, pc);
		}
		if (filename == NULL) {
			filename = "Unknown source";
		}
		if (native) {
			linenumber = -2;
		}
			
		// クラス名内の / を . に置き換える
		char* modified_classname;
		{
			char* tmp = (char*) malloc(strlen(classname) + 1);
			if (tmp == NULL) {
				fatal_error(FATAL_ERROR_NO_MEMORY);
			}
			strcpy(tmp, classname);
			modified_classname = tmp;
			while (*tmp) {
				if (*tmp == '/') {
					*tmp = '.';
				}
				tmp++;
			}
		}
		jstring classname_string = env->NewStringUTF(modified_classname);
		free(modified_classname);
		if (classname_string == NULL) {
			free(elements);
			return NULL;
		}

		jstring methodname_string = env->NewStringUTF(methodname);
		if (methodname_string == NULL) {
			free(elements);
			return NULL;
		}
		jstring filename_string = NULL;
		if (! native) {
			filename_string = env->NewStringUTF(filename);
			if (filename_string == NULL) {
				free(elements);
				return NULL;
			}
		}

		if (elementclass != NULL) {
			jmethodID mid = env->GetMethodID(elementclass,
											"<init>",
											"(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Z)V");
			if (mid != NULL) {
				jobject element = env->NewObject(elementclass,
												 mid,
												 filename_string,
												 linenumber,
												 classname_string,
												 methodname_string,
												 native
												 );
				if (element == NULL) {
					free(elements);
					return NULL;
				}
				elements[elem_index] = element;
			}
		}
	}
	env->ReleaseByteArrayElements(ba, (jbyte*) trace, 0);

	// elementsの内容をThrowableに設定する
	elementArray = env->NewObjectArray(count, elementclass, NULL);
	if (elementArray == NULL) {
		free(elements);
		return NULL;
	}
	for (unsigned int i = 0; i < count; ++i) {
		env->SetObjectArrayElement(elementArray, i, elements[i]);
	}
	free(elements);
	
	return elementArray;
}

/*
 * Class:     java_lang_VMMainThread
 * Method:    openConsole
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_VMMainThread_showConsole(JNIEnv *env, jclass) {
	console_set_visible(true);
}

/*
 * Class:     java_lang_VMObject
 * Method:    getClass
 * Signature: (Ljava/lang/Object;)Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_VMObject_getClass(JNIEnv *env, jclass, jobject obj) {
	return env->GetObjectClass(obj);
}

/*
 * Class:     java_lang_VMObject
 * Method:    clone
 * Signature: (Ljava/lang/Cloneable;)Ljava/lang/Object;
 */
JNIEXPORT jobject JNICALL Java_java_lang_VMObject_clone(JNIEnv *env, jclass, jobject obj) {
	jni_env* jenv = (jni_env*) env;
	jobject result = clone(get_current_frame(jenv->jni_frame), obj);
	result = new_local_reference(jenv->jni_frame, result);
	POP_DISCARD(get_current_frame(jenv->jni_frame));

	return result;
}

/*
 * Class:     java_lang_VMObject
 * Method:    wait
 * Signature: (Ljava/lang/Object;JI)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMObject_wait(JNIEnv *env, jclass, jobject obj, jlong timeout, jint ns) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (timeout < 0) {
		throw_exception(current_frame, "java/lang/IllegalArgumentException");
		return;
	}

	switch (monitor_wait(obj, (unsigned int) timeout)) {
	case MONITOR_WAIT_NOT_A_OWNER:
		// モニタの所有者ではない
		throw_exception(current_frame, "java/lang/IllegalMonitorStateException");
		break;
	
	case MONITOR_WAIT_NOTIFIED:
		// 正常終了
		break;

	case MONITOR_WAIT_TIMEOUT:
		// タイムアウト
		break;

	case MONITOR_WAIT_INTERRUPTED:
		// 他のスレッドから割り込まれた
		// スレッドの割り込みフラグをクリアする
		set_interrupted(GetCurrentThreadId(), false);
		throw_exception(current_frame, "java/lang/InterruptedException");
		break;
	}
}

/*
 * Class:     java_lang_VMObject
 * Method:    notify
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMObject_notify(JNIEnv *env, jclass, jobject obj) {
	if (! monitor_notify(obj, false)) {
		jni_env* jenv = (jni_env*) env;
		frame* current_frame = get_current_frame(jenv->jni_frame);
		throw_exception(current_frame, "java/lang/IllegalMonitorStateException");
	}
}

/*
 * Class:     java_lang_VMObject
 * Method:    notifyAll
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMObject_notifyAll(JNIEnv *env, jclass, jobject obj) {
	if (! monitor_notify(obj, true)) {
		jni_env* jenv = (jni_env*) env;
		frame* current_frame = get_current_frame(jenv->jni_frame);
		throw_exception(current_frame, "java/lang/IllegalMonitorStateException");
	}
}

/*
 * Class:     java_lang_VMThread
 * Method:    start
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMThread_start(JNIEnv *env, jobject obj, jlong stacksize) {
	static jmethodID is_daemon_mid;
	static jfieldID thread_fid;

	jni_env* jenv = (jni_env*) env;
	if (obj == NULL) {
		throw_exception(get_current_frame(jenv->jni_frame), "java/lang/NullPointerException");
		return;
	}
	
	thread_start_parameter* param = (thread_start_parameter*) malloc(sizeof(thread_start_parameter));
	if (param == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}

	// VMThread.threadフィールドを取り出す
	if (! thread_fid) {
		thread_fid = env->GetFieldID(env->GetObjectClass(obj), "thread", "Ljava/lang/Thread;");
	}
	// isDaemon()を呼び出す
	if (! is_daemon_mid) {
		is_daemon_mid = env->GetMethodID(env->GetObjectClass(obj), "isDaemon", "()Z");
	}

	param->vmthread_reference = obj;
	param->thread_reference = env->GetObjectField(obj, thread_fid);
	param->daemon = env->CallBooleanMethod(obj, is_daemon_mid) ? true : false;

	// スレッドを作成する
	if (! create_native_thread(thread_start, param)) {
		throw_exception(get_current_frame(jenv->jni_frame), "java/lang/VirtualMachineError");
		return;
	}
}

/*
 * Class:     java_lang_VMThread
 * Method:    interrupt
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_VMThread_interrupt(JNIEnv* env, jobject vmthread) {
	DWORD thread_id = get_thread_id(vmthread);
	interrupt(thread_id);
}

/*
 * Class:     java_lang_VMThread
 * Method:    isInterrupted
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMThread_isInterrupted(JNIEnv* env, jobject vmthread) {
	DWORD thread_id = get_thread_id(vmthread);
	return is_interrupted(thread_id);
}

/*
 * Class:     java_lang_VMThread
 * Method:    suspend
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_VMThread_suspend(JNIEnv* env, jobject vmthread) {
	HANDLE hThread = get_thread_handle(vmthread);
	if (hThread != INVALID_HANDLE_VALUE) {
		SuspendThread(hThread);
	}
}

/*
 * Class:     java_lang_VMThread
 * Method:    resume
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_VMThread_resume(JNIEnv* env, jobject vmthread) {
	HANDLE hThread = get_thread_handle(vmthread);
	if (hThread != INVALID_HANDLE_VALUE) {
		ResumeThread(hThread);
	}
}

/*
 * Class:     java_lang_VMThread
 * Method:    nativeSetPriority
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMThread_nativeSetPriority(JNIEnv *env, jobject vmthread, jint priority) {
	HANDLE hThread = get_thread_handle(vmthread);
	if (hThread != INVALID_HANDLE_VALUE) {
		// Windows CEでは 248〜255までの優先順位を使用可能である (248が最高優先度）
		// 優先順位 251 が Thread.NORM_PRIORITY(5)に対応する
		// Thread.MAX_PRIORITY(10)からThread.MIN_PRIORITY(1)を適当にマッピングする
		int native_priority = 251 + 5 - priority;
		if (native_priority < 248) {
			native_priority = 248;
		} else if (native_priority > 255) {
			native_priority = 255;
		}
		CeSetThreadPriority(hThread, native_priority);
	}
}

/*
 * Class:     java_lang_VMThread
 * Method:    nativeStop
 * Signature: (Ljava/lang/Throwable;)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMThread_nativeStop(JNIEnv *env, jobject vmthread, jthrowable t) {
	// VMThread.thread フィールドを取り出す
	jclass clazz = env->GetObjectClass(vmthread);
	assert(clazz);
	jfieldID fieldID = env->GetFieldID(clazz,
									   "thread",
									   "Ljava/lang/Thread;");
	assert(fieldID);
	jobject thread = env->GetObjectField(vmthread,
										 fieldID);
	assert(thread);

	// スレッドのカレントフレームに、指定された例外を設定する
	frame* current_frame = get_current_frame_of(thread);
	if (current_frame) {
		throw_exception(current_frame, t);
		// この例外が該当スレッド側で例外が検出される
	}
}

/*
 * Class:     java_lang_VMThread
 * Method:    currentThread
 * Signature: ()Ljava/lang/Thread;
 */
JNIEXPORT jobject JNICALL Java_java_lang_VMThread_currentThread(JNIEnv *env, jclass vmclass) {
	frame* frm = get_current_frame(((jni_env*) env)->jni_frame);
	return get_current_thread(frm);
}

/*
 * Class:     java_lang_VMThread
 * Method:    yield
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_VMThread_yield(JNIEnv* env, jclass) {
	// タイムスライスを他のスレッドに譲る
	Sleep(0);
}

/*
 * Class:     java_lang_VMThread
 * Method:    interrupted
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMThread_interrupted(JNIEnv* env, jclass) {
	DWORD thread_id = GetCurrentThreadId();
	jboolean flag = is_interrupted(thread_id);
	set_interrupted(thread_id, false);	// フラグをクリアする

	return flag;
}

/*
 * Class:     java_lang_VMThread
 * Method:    holdsLock
 * Signature: (Ljava/lang/Object;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_lang_VMThread_holdsLock(JNIEnv* env, jclass, jobject obj) {
	if (obj == NULL) {
		jclass npe_class = env->FindClass("java/lang/NullPointerException");
		if (npe_class != NULL) {
			env->ThrowNew(npe_class, "");
		}
		return JNI_FALSE;
	}
	return (jboolean) is_monitor_owner(obj);
}

/*
 * Class:     java_lang_VMString
 * Method:    intern
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_java_lang_VMString_intern(JNIEnv *env, jclass, jstring str) {
	const char* utf8 = env->GetStringUTFChars(str, NULL);
	jstring s = intern_string(utf8, get_current_frame(((jni_env*) env)->jni_frame));
	env->ReleaseStringUTFChars(str, utf8);
	return s;
}

/*
 * Class:     java_lang_VMSystem
 * Method:    arraycopy
 * Signature: (Ljava/lang/Object;ILjava/lang/Object;II)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMSystem_arraycopy(JNIEnv *env, jclass, jobject src, jint srcStart, jobject dest, jint destStart, jint len) {
	// コピーする必要がない場合
	if (len == 0) {
		return;
	}

	// ヌルチェック
	if (src == NULL || dest == NULL) {
		jclass npe = env->FindClass("java/lang/NullPointerException");
		if (npe == NULL) {
			return;
		}
		env->ThrowNew(npe, NULL);
		return;
	}

	// 配列かどうかをチェックする
	ClassFile* srccfile = get_ClassFile(src);
	ClassFile* destcfile = get_ClassFile(dest);
	if (! is_array_class(srccfile)) {
		jclass arraystoreexception = env->FindClass("java/lang/ArrayStoreException");
		if (arraystoreexception == NULL) {
			return;
		}
		env->ThrowNew(arraystoreexception, "src is not an array class");
		return;
	}
	if (! is_array_class(destcfile)) {
		jclass arraystoreexception = env->FindClass("java/lang/ArrayStoreException");
		if (arraystoreexception == NULL) {
			return;
		}
		env->ThrowNew(arraystoreexception, "dest is not an array class");
		return;
	}

	// それぞれプリミティブ型の配列であった場合、同一の型かどうかを調べる
	bool srcprimitive = is_primitive_array_class(srccfile);
	bool destprimitive = is_primitive_array_class(destcfile);
	if (srcprimitive && destprimitive) {
		if (srccfile->this_class_name != destcfile->this_class_name) {
			jclass arraystoreexception = env->FindClass("java/lang/ArrayStoreException");
			if (arraystoreexception == NULL) {
				return;
			}
			const char format[] = "Primitive array type mismatch. src=%s, dest=%s";
			char* buff = (char*) malloc(strlen(srccfile->this_class_name) + strlen(destcfile->this_class_name) + strlen(format) + 1);
			if (buff == NULL) {
				fatal_error(FATAL_ERROR_NO_MEMORY);
			}
			sprintf(buff, format, srccfile->this_class_name, destcfile->this_class_name);
			env->ThrowNew(arraystoreexception, buff);
			free(buff);

			return;
		}
	}
	
	// 片方だけがプリミティブ型でないか調べる
	if (srcprimitive && (! destprimitive)) {
		jclass arraystoreexception = env->FindClass("java/lang/ArrayStoreException");
		if (arraystoreexception == NULL) {
			return;
		}
		env->ThrowNew(arraystoreexception, "src is primitive type, but dest is not.");
		return;
	}
	if ((! srcprimitive) && destprimitive) {
		jclass arraystoreexception = env->FindClass("java/lang/ArrayStoreException");
		if (arraystoreexception == NULL) {
			return;
		}
		env->ThrowNew(arraystoreexception, "dest is primitive type, but src is not.");
		return;
	}

	// 配列サイズを超えていないかチェックする
	jsize srclen = env->GetArrayLength((jarray) src);
	jsize destlen = env->GetArrayLength((jarray) dest);
	if (srcStart < 0
			|| destStart < 0
			|| srcStart + len > srclen
			|| destStart + len > destlen) {
		jclass aioobe = env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
		if (aioobe == NULL) {
			return;
		}
		env->ThrowNew(aioobe, NULL);
		return;
	}

	if (srcprimitive && destprimitive) {
		// 双方ともプリミティブ型の場合は、単純にコピーする
		array_copy(src, srcStart, dest, destStart, len);
	} else {
		// コピー先配列のクラスを取得しておく
		const char* dest_element_class_name = get_array_component_name(destcfile);
		// jclass dest_element_class = env->FindClass(dest_element_class_name);
		jclass dest_element_class = find_class(env, dest_element_class_name, get_defining_loader(destcfile));
		if (dest_element_class == NULL) {
			return;
		}
		
		// 引数 src および dest が同じ配列オブジェクトである場合、
		// まず srcPos から srcPos+length-1 までの位置にある要素を
		// 一時配列 length の要素にコピーし、次に一時配列の内容を
		// 転送先配列の destPos から destPos+length-1 にコピーします。 
		if (env->IsSameObject(src, dest)) {
			// 何でも代入できるように、java.lang.Object[] を作成する
			jclass clazz = env->FindClass("java/lang/Object");
			jobjectArray tmpArray = env->NewObjectArray(len, clazz, NULL);
			if (! tmpArray) {
				return;
			}
			for (int i = 0; i < len; ++i) {
				jobject obj = env->GetObjectArrayElement((jobjectArray) src, srcStart + i);
				// 一時配列にコピー
				env->SetObjectArrayElement((jobjectArray) tmpArray, i, obj);
				if (env->ExceptionCheck()) {
					return;
				}
			}
			// src の内容を書き換える
			src = tmpArray;
			srcStart = 0;
		}

		// コピーする
		for (int i = 0; i < len; ++i) {
			jobject obj = env->GetObjectArrayElement((jobjectArray) src, srcStart + i);
			if (env->ExceptionCheck()) {
				return;
			}
			if (obj != NULL) {
				// null以外のオブジェクトについては、代入可能かを調べる
				jclass objclass = env->GetObjectClass(obj);
				if (objclass == NULL) {
					fatal_error(_T("System.arraycopy() failed. Could not get class object."));
					return;
				}
				// 配列に代入可能かを調べる
				if (! env->IsAssignableFrom(objclass, dest_element_class)) {
					// 代入できない場合、ArrayStoreExceptionを発生させる
					jclass arraystoreexception = env->FindClass("java/lang/ArrayStoreException");
					if (arraystoreexception == NULL) {
						return;
					}
					const char format[] = "Cannot store object %s to array %s";
					ClassFile* objcfile = get_ClassFile(obj);
					char* buff = (char*) malloc(strlen(objcfile->this_class_name)
												+ strlen(dest_element_class_name)
												+ strlen(format)
												+ 1);
					if (buff == NULL) {
						fatal_error(FATAL_ERROR_NO_MEMORY);
					}
					sprintf(buff, format, objcfile->this_class_name, dest_element_class_name);
					env->ThrowNew(arraystoreexception, buff);
					free(buff);

					return;
				}
			}
			env->SetObjectArrayElement((jobjectArray) dest, destStart + i, obj);
			if (env->ExceptionCheck()) {
				// エラー発生
				return;
			}
		}
	}
}

/*
 * Class:     java_lang_VMSystem
 * Method:    identityHashCode
 * Signature: (Ljava/lang/Object;)I
 */
JNIEXPORT jint JNICALL Java_java_lang_VMSystem_identityHashCode(JNIEnv* env, jclass, jobject obj) {
	// オブジェクトの参照値をそのまま返す
	// (nullはNULLとして実装しているため、0が返る）
	return (jint) obj;
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    availableProcessors
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_java_lang_VMRuntime_availableProcessors(JNIEnv* env, jclass) {
	SYSTEM_INFO info;
	GetSystemInfo(&info);
	return (jint) info.dwNumberOfProcessors;
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    freeMemory
 * Signature: ()J
 */
JNIEXPORT jlong JNICALL Java_java_lang_VMRuntime_freeMemory(JNIEnv* env, jclass) {
	return (jlong) gc_get_free_memory();
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    totalMemory
 * Signature: ()J
 */
JNIEXPORT jlong JNICALL Java_java_lang_VMRuntime_totalMemory(JNIEnv* env, jclass) {
	return (jlong) gc_get_total_memory();
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    maxMemory
 * Signature: ()J
 */
JNIEXPORT jlong JNICALL Java_java_lang_VMRuntime_maxMemory(JNIEnv* env, jclass) {
	return (jlong) gc_get_max_memory();
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    gc
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_VMRuntime_gc(JNIEnv* env, jclass) {
	gc();
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    runFinalization
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_VMRuntime_runFinalization(JNIEnv* env, jclass) {
	// 未実装
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    runFinalizationForExit
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_VMRuntime_runFinalizationForExit(JNIEnv* env, jclass) {
	// 未実装
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    traceInstructions
 * Signature: (Z)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMRuntime_traceInstructions(JNIEnv* env, jclass, jboolean) {
	// 未実装
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    traceMethodCalls
 * Signature: (Z)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMRuntime_traceMethodCalls(JNIEnv* env, jclass, jboolean) {
	// 未実装
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    runFinalizersOnExit
 * Signature: (Z)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMRuntime_runFinalizersOnExit(JNIEnv* env, jclass, jboolean) {
	// 未実装
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    exit
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMRuntime_exit(JNIEnv* env, jclass, jint status) {
	// このメソッドがJVMの終了ポイント

	// コンソールがクローズするのを待つ
	console_wait_for_close();

	// プロセスを終了させる（exitハンドラは実行しない）
	ExitProcess(status);
}

/*
 * Class:     java_lang_VMRuntime
 * Method:    nativeLoad
 * Signature: (Ljava/lang/String;Ljava/lang/ClassLoader;)I
 */
JNIEXPORT jint JNICALL Java_java_lang_VMRuntime_nativeLoad(JNIEnv *env, jclass klass, jstring filename, jobject loader) {
	// Unicodeバッファにファイル名をコピーする
	const jchar* tmp = env->GetStringChars(filename, NULL);
	if (env->ExceptionCheck()) {
		return 0;
	}

	jsize len = env->GetStringLength(filename);
	if (env->ExceptionCheck()) {
		return 0;
	}

	_TCHAR* dllname = (_TCHAR*) malloc(sizeof(_TCHAR) * (len + 1));
	if (dllname == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	memcpy(dllname, tmp, sizeof(_TCHAR) * len);
	dllname[len] = _T('\0');
	env->ReleaseStringChars(filename, tmp);
	if (env->ExceptionCheck()) {
		free(dllname);
		return 0;
	}

	// DLLをロードする
	HMODULE hModule = LoadLibrary(dllname);
	// Unicodeバッファを開放する
	free(dllname);
	if (hModule == NULL) {
#ifdef DEBUG
		DWORD dwError = GetLastError();
#endif
		// エラーリターン
		return 0;
	}
	
	// ロックする
	EnterCriticalSection(&g_global_critical_section);

	bool found = false;
	int retcode = 1;
	for (unsigned int i = 0; i < g_dlls_count; ++i) {
		if (hModule == g_hmodules[i]) {
			found = true;
		}
	}
	if (! found) {
		g_dlls_count++;
		if (g_dlls_count >= g_dlls_limit) {
			g_dlls_limit += 32;
			g_hmodules = (HMODULE*) realloc(g_hmodules, sizeof(HMODULE) * g_dlls_limit);
		}
		if (g_hmodules != NULL) {
			g_hmodules[g_dlls_count - 1] = hModule;

			// JNI_OnLoad を呼び出す
			FARPROC onLoadProc = GetProcAddress(hModule, _T("JNI_OnLoad"));
			if (onLoadProc) {
				jint required_version = ((JNI_ON_LOAD) onLoadProc)(&g_java_vm, NULL);
				// ToDo: required_versionをチェックする
				DBG(_T("DLL has JNI_OnLoad() proc.\n"));
			} else {
				DBG(_T("DLL does not have JNI_OnLoad() proc.\n"));
			}
		} else {
			retcode = 0;
		}
	}
	// ロックを解除する
	LeaveCriticalSection(&g_global_critical_section);

	return retcode;
}

/*
 * Class:     java_lang_ref_Reference
 * Method:    setType
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_java_lang_ref_Reference_setType(JNIEnv* env, jobject o, jint type) {
	// Referenceオブジェクトのタイプを設定する
	set_reference_type(o, type);
}

/*
 * Class:     java_lang_reflect_Constructor
 * Method:    getModifiersInternal
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_java_lang_reflect_Constructor_getModifiersInternal(JNIEnv *env, jobject obj) {
	jclass constructor_class = env->GetObjectClass(obj);
	jfieldID fid = env->GetFieldID(constructor_class, "clazz", "Ljava/lang/Class;");
	if (fid == NULL) {
		jclass vme_class = env->FindClass("java/lang/VirtualMachineError");
		if (vme_class != NULL) {
			env->ThrowNew(vme_class, "Could not get field 'clazz'");
		}
		return NULL;
	}

	jclass clazz = (jclass) env->GetObjectField(obj, fid);
	if (clazz == NULL) {
		jclass npe_class = env->FindClass("java/lang/NullPointerException");
		if (npe_class != NULL) {
			env->ThrowNew(npe_class, "clazz");
		}
		return NULL;
	}

	// ClassFile構造体を取得する
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);

	// slotを取得する
	fid = env->GetFieldID(constructor_class, "slot", "I");
	if (fid == NULL) {
		jclass vme_class = env->FindClass("java/lang/VirtualMachineError");
		if (vme_class != NULL) {
			env->ThrowNew(vme_class, "Could not get field 'clazz'");
		}
		return NULL;
	}
	int slot = env->GetIntField(obj, fid);
	
	// method_info構造体の access_flagsを返す
	method_info* minfo = &cfile->methods[slot];

	return minfo->access_flags;
}

/*
 * Class:     java_lang_reflect_Constructor
 * Method:    getExceptionTypes
 * Signature: ()[Ljava/lang/Class;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_reflect_Constructor_getExceptionTypes(JNIEnv *env, jobject obj) {
	jclass clazz = env->FindClass("java/lang/VirtualMachineError");
	env->ThrowNew(clazz, "Not implemented");
	assert(false);
	return NULL;
}

/*
 * Class:     java_lang_reflect_Constructor
 * Method:    getParameterTypes
 * Signature: ()[Ljava/lang/Class;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_reflect_Constructor_getParameterTypes(JNIEnv *env, jobject obj) {
	ClassFile* cfile = NULL;
	method_info* minfo = NULL;
	get_Constructor_data(env, obj, &cfile, &minfo);
	if (cfile && minfo) {
		return create_parameter_types(env, minfo->descriptor, get_defining_loader(cfile));
	} else {
		return NULL;
	}
}

/*
 * Class:     java_lang_reflect_Constructor
 * Method:    constructNative
 * Signature: ([Ljava/lang/Object;Ljava/lang/Class;I)Ljava/lang/Object;
 */
JNIEXPORT jobject JNICALL Java_java_lang_reflect_Constructor_constructNative(JNIEnv *env, jobject obj, jobjectArray args, jclass clazz, jint slot) {
	// 引数 slot をキーにして、対象となる method_info 構造体を取得する
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);
	method_info* minfo = &cfile->methods[slot];
	
	// Ojbect[] を jvalue* に変換する
	int argc = 0;
	if (args != NULL) {
		argc = env->GetArrayLength(args);
	}
	jvalue* values = (jvalue*) malloc(sizeof(jvalue) * argc);
	if (values == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	convert_to_jvalues(env, cfile, minfo->descriptor, args, values);
	if (env->ExceptionCheck()) {
		// 例外発生
		free(values);
		return NULL;
	}

	// オブジェクトを作成する
	jmethodID mid = env->GetMethodID(clazz, minfo->name, minfo->descriptor);

	// アクセス権限をチェックする
	// ...
	//
	jobject result = env->NewObjectA(clazz, mid, values);
	free(values);

	return result;
}

/*
 * Class:     java_lang_reflect_Constructor
 * Method:    getSignature
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_java_lang_reflect_Constructor_getSignature(JNIEnv *env, jobject constructor) {
	ClassFile* cfile = NULL;
	method_info* minfo = NULL;
	
	get_Constructor_data(env, constructor, &cfile, &minfo);

	u2 sig_index = minfo->signature_index;
	if (sig_index) {
		return env->NewStringUTF((const char*) (cfile->constant_pool[sig_index]));
	}
	return NULL;
}

/*
 * Class:     java_lang_reflect_Constructor
 * Method:    getParameterAnnotations
 * Signature: ()[[Ljava/lang/annotation/Annotation;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_reflect_Constructor_getParameterAnnotations
(JNIEnv *, jobject) {
	// ToDo: implement
	return NULL;
}

/*
 * Class:     java_lang_reflect_Field
 * Method:    getModifiersInternal
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_java_lang_reflect_Field_getModifiersInternal(JNIEnv* env, jobject field) {
	// フィールド "slot" の内容を取得する
	jclass clazz = env->GetObjectClass(field);
	jfieldID slotID = env->GetFieldID(clazz, "slot", "I");
	int slot = env->GetIntField(field, slotID);
	
	// フィールド "declaringClass" の内容を取得する
	jfieldID declaringID = env->GetFieldID(clazz, "declaringClass", "Ljava/lang/Class;");
	jclass declaring = (jclass) env->GetObjectField(field, declaringID);
	ClassFile* cfile = get_ClassFile(get_static_data(env, declaring));
	field_info* finfo = &cfile->fields[slot];
	
	return (jint) finfo->access_flags;
}

/*
 * Class:     java_lang_reflect_Field
 * Method:    getType
 * Signature: ()Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_reflect_Field_getType(JNIEnv* env, jobject field) {
	jclass clazz = env->GetObjectClass(field);
	jfieldID slotID = env->GetFieldID(clazz, "slot", "I");
	int slot = env->GetIntField(field, slotID);

	jfieldID declaringID = env->GetFieldID(clazz, "declaringClass", "Ljava/lang/Class;");
	jclass declaring = (jclass) env->GetObjectField(field, declaringID);
	ClassFile* cfile = get_ClassFile(get_static_data(env, declaring));
	
	field_info* finfo = &cfile->fields[slot];
	jobjectArray array = create_parameter_types(env, finfo->descriptor, get_defining_loader(cfile));

	return (jclass) env->GetObjectArrayElement(array, 0);
}

/*
 * Class:     java_lang_reflect_Field
 * Method:    get
 * Signature: (Ljava/lang/Object;)Ljava/lang/Object;
 */
JNIEXPORT jobject JNICALL Java_java_lang_reflect_Field_get(JNIEnv *env, jobject field, jobject o) {
	jclass clazz = env->GetObjectClass(field);
	jfieldID slotID = env->GetFieldID(clazz, "slot", "I");
	int slot = env->GetIntField(field, slotID);

	jfieldID declaringID = env->GetFieldID(clazz, "declaringClass", "Ljava/lang/Class;");
	jclass declaring = (jclass) env->GetObjectField(field, declaringID);
	ClassFile* cfile = get_ClassFile(get_static_data(env, declaring));
	field_info* finfo = &cfile->fields[slot];
	jvalue result; 
	if (is_static(finfo->access_flags)) {
		jfieldID fid = env->GetStaticFieldID(declaring, finfo->name, finfo->descriptor);
		switch (finfo->descriptor[0]) {
		case 'Z':
			// boolean
			result.z = env->GetStaticBooleanField(declaring, fid);
			break;
		case 'B':
			// byte
			result.b = env->GetStaticByteField(declaring, fid);
			break;
		case 'C':
			// char
			result.c = env->GetStaticCharField(declaring, fid);
			break;
		case 'S':
			// short
			result.s = env->GetStaticShortField(declaring, fid);
			break;
		case 'I':
			// int
			result.i = env->GetStaticIntField(declaring, fid);
			break;
		case 'J':
			// long
			result.j = env->GetStaticLongField(declaring, fid);
			break;
		case 'F':
			// float
			result.f = env->GetStaticFloatField(declaring, fid);
			break;
		case 'D':
			// double
			result.d = env->GetStaticDoubleField(declaring, fid);
			break;
		default:
			// Object
			result.l = env->GetStaticObjectField(declaring, fid);
			break;
		} 
	} else {
		// 非staticフィールド
		jfieldID fid = env->GetFieldID(declaring, finfo->name, finfo->descriptor);
		switch (finfo->descriptor[0]) {
		case 'Z':
			// boolean
			result.z = env->GetBooleanField(o, fid);
			break;
		case 'B':
			// byte
			result.b = env->GetByteField(o, fid);
			break;
		case 'C':
			// char
			result.c = env->GetCharField(o, fid);
			break;
		case 'S':
			// short
			result.s = env->GetShortField(o, fid);
			break;
		case 'I':
			// int
			result.i = env->GetIntField(o, fid);
			break;
		case 'J':
			// long
			result.j = env->GetLongField(o, fid);
			break;
		case 'F':
			// float
			result.f = env->GetFloatField(o, fid);
			break;
		case 'D':
			// double
			result.d = env->GetDoubleField(o, fid);
			break;
		default:
			// oect
			result.l = env->GetObjectField(o, fid);
			break;
		} 
	}

	// 適切な型にラップして返す
	jclass wrapperclass;
	jmethodID constructor;
	jobject resultobj = NULL;
	switch (finfo->descriptor[0]) {
		case 'Z':
			// boolean
			wrapperclass = env->FindClass("java/lang/Boolean");
			if (wrapperclass != NULL) {
				constructor = env->GetMethodID(wrapperclass, "<init>", "(Z)V");
				resultobj = env->NewObject(wrapperclass, constructor, result.z);
			}
			break;

		case 'B':
			// byte
			wrapperclass = env->FindClass("java/lang/Byte");
			if (wrapperclass != NULL) {
				constructor = env->GetMethodID(wrapperclass, "<init>", "(B)V");
				resultobj = env->NewObject(wrapperclass, constructor, result.b);
			}
			break;

		case 'C':
			// char
			wrapperclass = env->FindClass("java/lang/Character");
			if (wrapperclass != NULL) {
				constructor = env->GetMethodID(wrapperclass, "<init>", "(C)V");
				resultobj = env->NewObject(wrapperclass, constructor, result.c);
			}
			break;

		case 'S':
			// short
			wrapperclass = env->FindClass("java/lang/Short");
			if (wrapperclass != NULL) {
				constructor = env->GetMethodID(wrapperclass, "<init>", "(S)V");
				resultobj = env->NewObject(wrapperclass, constructor, result.s);
			}
			break;

		case 'I':
			// int
			wrapperclass = env->FindClass("java/lang/Integer");
			if (wrapperclass != NULL) {
				constructor = env->GetMethodID(wrapperclass, "<init>", "(I)V");
				resultobj = env->NewObject(wrapperclass, constructor, result.i);
			}
			break;

		case 'J':
			// long
			wrapperclass = env->FindClass("java/lang/Long");
			if (wrapperclass != NULL) {
				constructor = env->GetMethodID(wrapperclass, "<init>", "(J)V");
				resultobj = env->NewObject(wrapperclass, constructor, result.j);
			}
			break;

		case 'F':
			// float
			wrapperclass = env->FindClass("java/lang/Float");
			if (wrapperclass != NULL) {
				constructor = env->GetMethodID(wrapperclass, "<init>", "(F)V");
				resultobj = env->NewObject(wrapperclass, constructor, result.f);
			}
			break;

		case 'D':
			// double
			wrapperclass = env->FindClass("java/lang/Double");
			if (wrapperclass != NULL) {
				constructor = env->GetMethodID(wrapperclass, "<init>", "(D)V");
				resultobj = env->NewObject(wrapperclass, constructor, result.d);
			}
			break;

		default:
			resultobj = result.l;
			break;
	} 
	return resultobj;
}

/**
 * java.lang.reflect.Field.get<primitive_type>(Object) メソッドを生成する
 * マクロ
 */
#define JAVA_LANG_REFLECT_FIELD_GET_PRIMITIVE(postfix, primitive_type) \
JNIEXPORT primitive_type JNICALL Java_java_lang_reflect_Field_get##postfix##(JNIEnv* env, jobject field, jobject obj) { \
	jclass declaring = NULL; \
	field_info* finfo = NULL; \
	jfieldID fid = NULL; \
	get_Field_data(env, field, &declaring, &finfo, &fid); \
	primitive_type result = 0; \
    if (is_static(finfo->access_flags)) { \
		result = env->GetStatic##postfix##Field(declaring, fid); \
	} else { \
		result = env->Get##postfix##Field(obj, fid); \
	} \
	return result; \
}

/*
 * Class:     java_lang_reflect_Field
 * Method:    getBoolean
 * Signature: (Ljava/lang/Object;)Z
 */
JAVA_LANG_REFLECT_FIELD_GET_PRIMITIVE(Boolean, jboolean);

/*
 * Class:     java_lang_reflect_Field
 * Method:    getByte
 * Signature: (Ljava/lang/Object;)B
 */
JAVA_LANG_REFLECT_FIELD_GET_PRIMITIVE(Byte, jbyte);

/*
 * Class:     java_lang_reflect_Field
 * Method:    getChar
 * Signature: (Ljava/lang/Object;)C
 */
JAVA_LANG_REFLECT_FIELD_GET_PRIMITIVE(Char, jchar);

/*
 * Class:     java_lang_reflect_Field
 * Method:    getShort
 * Signature: (Ljava/lang/Object;)S
 */
JAVA_LANG_REFLECT_FIELD_GET_PRIMITIVE(Short, jshort);

/*
 * Class:     java_lang_reflect_Field
 * Method:    getInt
 * Signature: (Ljava/lang/Object;)I
 */
JAVA_LANG_REFLECT_FIELD_GET_PRIMITIVE(Int, jint);

/*
 * Class:     java_lang_reflect_Field
 * Method:    getLong
 * Signature: (Ljava/lang/Object;)J
 */
JAVA_LANG_REFLECT_FIELD_GET_PRIMITIVE(Long, jlong);

/*
 * Class:     java_lang_reflect_Field
 * Method:    getFloat
 * Signature: (Ljava/lang/Object;)F
 */
JAVA_LANG_REFLECT_FIELD_GET_PRIMITIVE(Float, jfloat);

/*
 * Class:     java_lang_reflect_Field
 * Method:    getDouble
 * Signature: (Ljava/lang/Object;)D
 */
JAVA_LANG_REFLECT_FIELD_GET_PRIMITIVE(Double, jdouble);

/*
 * Class:     java_lang_reflect_Field
 * Method:    set
 * Signature: (Ljava/lang/Object;Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_java_lang_reflect_Field_set(JNIEnv* env, jobject field, jobject o, jobject value) {
	jclass declaring = NULL; \
	field_info* finfo = NULL; \
	jfieldID fid = NULL; \
	get_Field_data(env, field, &declaring, &finfo, &fid); \

	// フィールドが final かを調べる
	if (is_final(finfo->access_flags)) {
		jclass clazz = env->FindClass("java/lang/IllegalAccessException");
		if (clazz) {
			env->ThrowNew(clazz, finfo->name);
		}
		return;
	}

	// ラップ解除する
	jvalue unwrapped;
	if (! unwrap_object(env, finfo->descriptor[0], value, &unwrapped)) {
		// ラップ解除失敗
		jclass clazz = env->FindClass("java/lang/IllegalArgumentException");
		if (clazz) {
			env->ThrowNew(clazz, "value");
		}
		return;
	}
	bool static_flag = is_static(finfo->access_flags);
	switch (finfo->descriptor[0]) {
	case 'Z':	// boolean
		if (static_flag) {
			env->SetStaticBooleanField(declaring, fid, unwrapped.z);
		} else {
			env->SetBooleanField(o, fid, unwrapped.z);
		}
		break;

	case 'B':	// byte
		if (static_flag) {
			env->SetStaticByteField(declaring, fid, unwrapped.b);
		} else {
			env->SetByteField(o, fid, unwrapped.b);
		}
		break;

	case 'S':	// short
		if (static_flag) {
			env->SetStaticShortField(declaring, fid, unwrapped.s);
		} else {
			env->SetShortField(o, fid, unwrapped.s);
		}
		break;

	case 'C':	// char
		if (static_flag) {
			env->SetStaticCharField(declaring, fid, unwrapped.c);
		} else {
			env->SetCharField(o, fid, unwrapped.c);
		}
		break;

	case 'I':	// int
		if (static_flag) {
			env->SetStaticIntField(declaring, fid, unwrapped.i);
		} else {
			env->SetIntField(o, fid, unwrapped.i);
		}
		break;

	case 'J':	// long
		if (static_flag) {
			env->SetStaticLongField(declaring, fid, unwrapped.j);
		} else {
			env->SetLongField(o, fid, unwrapped.j);
		}
		break;

	case 'F':	// float
		if (static_flag) {
			env->SetStaticFloatField(declaring, fid, unwrapped.f);
		} else {
			env->SetFloatField(o, fid, unwrapped.f);
		}
		break;

	case 'D':	// double
		if (static_flag) {
			env->SetStaticDoubleField(declaring, fid, unwrapped.d);
		} else {
			env->SetDoubleField(o, fid, unwrapped.d);
		}
		break;

	default:
		if (static_flag) {
			env->SetStaticObjectField(declaring, fid, unwrapped.l);
		} else {
			env->SetObjectField(o, fid, unwrapped.l);
		}
		break;

	}
}

/**
 * java.lang.reflect.Field.set<primitive_type>(Object) メソッドを生成する
 * マクロ
 */
#define JAVA_LANG_REFLECT_FIELD_SET_PRIMITIVE(postfix, primitive_type) \
JNIEXPORT void JNICALL Java_java_lang_reflect_Field_set##postfix##(JNIEnv* env, jobject field, jobject obj, primitive_type value) { \
	jclass declaring = NULL; \
	field_info* finfo = NULL; \
	jfieldID fid = NULL; \
	get_Field_data(env, field, &declaring, &finfo, &fid); \
	if (is_final(finfo->access_flags)) { \
	    jclass clazz = env->FindClass("java/lang/IllegalAccessException"); \
        if (clazz) { \
		    env->ThrowNew(clazz, "final field"); \
            return; \
		} \
    } \
    if (is_static(finfo->access_flags)) { \
		env->SetStatic##postfix##Field(declaring, fid, value); \
	} else { \
		env->Set##postfix##Field(obj, fid, value); \
	} \
}

 /*
 * Class:     java_lang_reflect_Field
 * Method:    setBoolean
 * Signature: (Ljava/lang/Object;Z)V
 */
JAVA_LANG_REFLECT_FIELD_SET_PRIMITIVE(Boolean, jboolean);

/*
 * Class:     java_lang_reflect_Field
 * Method:    setByte
 * Signature: (Ljava/lang/Object;B)V
 */
JAVA_LANG_REFLECT_FIELD_SET_PRIMITIVE(Byte, jbyte);

/*
 * Class:     java_lang_reflect_Field
 * Method:    setChar
 * Signature: (Ljava/lang/Object;C)V
 */
JAVA_LANG_REFLECT_FIELD_SET_PRIMITIVE(Char, jchar);

/*
 * Class:     java_lang_reflect_Field
 * Method:    setShort
 * Signature: (Ljava/lang/Object;S)V
 */
JAVA_LANG_REFLECT_FIELD_SET_PRIMITIVE(Short, jshort);

/*
 * Class:     java_lang_reflect_Field
 * Method:    setInt
 * Signature: (Ljava/lang/Object;I)V
 */
JAVA_LANG_REFLECT_FIELD_SET_PRIMITIVE(Int, jint);

/*
 * Class:     java_lang_reflect_Field
 * Method:    setLong
 * Signature: (Ljava/lang/Object;J)V
 */
JAVA_LANG_REFLECT_FIELD_SET_PRIMITIVE(Long, jlong);

/*
 * Class:     java_lang_reflect_Field
 * Method:    setFloat
 * Signature: (Ljava/lang/Object;F)V
 */
JAVA_LANG_REFLECT_FIELD_SET_PRIMITIVE(Float, jfloat);

/*
 * Class:     java_lang_reflect_Field
 * Method:    setDouble
 * Signature: (Ljava/lang/Object;D)V
 */
JAVA_LANG_REFLECT_FIELD_SET_PRIMITIVE(Double, jdouble);

/*
 * Class:     java_lang_reflect_Field
 * Method:    getSignature
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_java_lang_reflect_Field_getSignature(JNIEnv *env, jobject field) {
	jclass declaring = NULL;
	field_info* finfo = NULL;
	jfieldID fid = NULL;

	get_Field_data(env, field, &declaring, &finfo, &fid);

	ClassFile* cfile = finfo->declaring_ClassFile;
	u2 sig_index = finfo->signature_index;
	if (sig_index) {
		const java_utf8* sig = (const java_utf8*) cfile->constant_pool[sig_index];
		env->NewStringUTF(sig);
	}
	return NULL;
}

/**
 * 指定された Constructor オブジェクトから、対応する情報を取り出す
 */
static void get_Constructor_data(JNIEnv* env, jobject constructor, ClassFile** pcfile, method_info** pminfo) {
	jclass constructor_class = env->GetObjectClass(constructor);
	jfieldID fid = env->GetFieldID(constructor_class, "clazz", "Ljava/lang/Class;");
	if (fid == NULL) {
		jclass vme_class = env->FindClass("java/lang/VirtualMachineError");
		if (vme_class != NULL) {
			env->ThrowNew(vme_class, "Could not get field 'clazz'");
		}
		return;
	}

	jclass clazz = (jclass) env->GetObjectField(constructor, fid);
	if (clazz == NULL) {
		jclass npe_class = env->FindClass("java/lang/NullPointerException");
		if (npe_class != NULL) {
			env->ThrowNew(npe_class, "clazz");
		}
		return;
	}

	// ClassFile構造体を取得する
	jobject static_data = get_static_data(env, clazz);
	*pcfile = get_ClassFile(static_data);

	// slotを取得する
	fid = env->GetFieldID(constructor_class, "slot", "I");
	if (fid == NULL) {
		jclass vme_class = env->FindClass("java/lang/VirtualMachineError");
		if (vme_class != NULL) {
			env->ThrowNew(vme_class, "Could not get field 'clazz'");
		}
		return;
	}
	int slot = env->GetIntField(constructor, fid);
	
	// method_info構造体を返す
	*pminfo = &(*pcfile)->methods[slot];
}

/**
 * 指定された Method オブジェクトから、対応する情報を取り出す
 */
void get_Method_data(JNIEnv* env, jobject method, ClassFile** pcfile, method_info** pminfo) {
	jclass clazz = env->GetObjectClass(method);

	// フィールド "declaringClass" の値を得る
	jfieldID class_fid = env->GetFieldID(clazz, "declaringClass", "Ljava/lang/Class;");
	if (class_fid == NULL) {
		jclass vme_class = env->FindClass("java/lang/VirtualMachineError");
		env->ThrowNew(vme_class, "Could not get field id of 'int slot'");
		return;
	}
	jclass declaring_class = (jclass) env->GetObjectField(method, class_fid);
	if (env->ExceptionCheck()) {
		return;
	}

	// フィールド "slot" の値を得る
	jfieldID slot_fid = env->GetFieldID(clazz, "slot", "I");
	if (slot_fid == NULL) {
		jclass vme_class = env->FindClass("java/lang/VirtualMachineError");
		env->ThrowNew(vme_class, "Could not get field id of 'int slot'");
		return;
	}
	int slot = env->GetIntField(method, slot_fid);
	if (env->ExceptionCheck()) {
		return;
	}
	
	// method_info 構造体を得る
	jobject static_data = get_static_data(env, declaring_class);
	ClassFile* cfile = get_ClassFile(static_data);

	if (pcfile) {
		*pcfile = cfile;
	}
	if (pminfo) {
		*pminfo = &cfile->methods[slot];
	}
}

/**
 * 指定された java.lang.reflect.Field のインスタンスから、
 * 対応する ClassFile および field_info を取得する
 */
static void get_Field_data(JNIEnv* env, jobject field, jclass* pdeclaring, field_info** pfinfo, jfieldID* pfid) {
	// Field のメンバ slot を得る
	jclass clazz = env->GetObjectClass(field);
	jfieldID slotID = env->GetFieldID(clazz, "slot", "I");
	int slot = env->GetIntField(field, slotID);
	
	// field_info* を得る
	jfieldID declaringID = env->GetFieldID(clazz, "declaringClass", "Ljava/lang/Class;");
	*pdeclaring = (jclass) env->GetObjectField(field, declaringID);
	ClassFile* cfile = get_ClassFile(get_static_data(env, *pdeclaring));
	*pfinfo = &cfile->fields[slot];

	if (is_static((*pfinfo)->access_flags)) {
		// staticフィールドの場合
		*pfid = env->GetStaticFieldID(*pdeclaring, (*pfinfo)->name, (*pfinfo)->descriptor);
	} else {
		// インスタンスフィールドの場合
		*pfid = env->GetFieldID(*pdeclaring, (*pfinfo)->name, (*pfinfo)->descriptor);
	}
}

/*
 * Class:     java_lang_reflect_Method
 * Method:    getModifiersInternal
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_java_lang_reflect_Method_getModifiersInternal(JNIEnv* env, jobject method) {
	ClassFile* cfile = NULL;
	method_info* minfo = NULL;
	get_Method_data(env, method, &cfile, &minfo);
	return (jint) minfo->access_flags;
}

/*
 * Class:     java_lang_reflect_Method
 * Method:    getReturnType
 * Signature: ()Ljava/lang/Class;
 */
JNIEXPORT jclass JNICALL Java_java_lang_reflect_Method_getReturnType(JNIEnv* env, jobject method) {
	ClassFile* cfile = NULL;
	method_info* minfo = NULL;
	get_Method_data(env, method, &cfile, &minfo);
	const char* rettype = get_return_type(minfo);
	// パラメータタイプを作成する関数で代用する
	jobjectArray array = create_parameter_types(env, get_return_type(minfo), get_defining_loader(cfile));
	return (jclass) env->GetObjectArrayElement(array, 0);
}

/*
 * Class:     java_lang_reflect_Method
 * Method:    getParameterTypes
 * Signature: ()[Ljava/lang/Class;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_reflect_Method_getParameterTypes(JNIEnv *env, jobject obj) {
	ClassFile* cfile = NULL;
	method_info* minfo = NULL;
	get_Method_data(env, obj, &cfile, &minfo);
	jobjectArray result = create_parameter_types(env, minfo->descriptor, get_defining_loader(cfile));
	assert(result != NULL);
	return result;
}

/*
 * Class:     java_lang_reflect_Method
 * Method:    getExceptionTypes
 * Signature: ()[Ljava/lang/Class;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_reflect_Method_getExceptionTypes(JNIEnv* env, jobject method) {
	method_info* minfo = NULL;
	get_Method_data(env, method, NULL, &minfo);

	jobjectArray result;
	jclass elem_class = env->FindClass("java/lang/Class");
	Exceptions_attribute* attr = minfo->exceptions_attribute;
	if (attr) {
		// 結果オブジェクトを作成する
		result = env->NewObjectArray(attr->number_of_exceptions, elem_class, NULL);
		if (result) {
			// ClassFile構造体を得る
			jclass clazz = env->GetObjectClass(method);
			jfieldID class_fid = env->GetFieldID(clazz, "declaringClass", "Ljava/lang/Class;");
			if (class_fid == NULL) {
				jclass vme_class = env->FindClass("java/lang/VirtualMachineError");
				env->ThrowNew(vme_class, "Could not get field id of 'int slot'");
				return NULL;
			}
			jclass declaring_class = (jclass) env->GetObjectField(method, class_fid);
			if (env->ExceptionCheck()) {
				return NULL;
			}
			jobject static_data = get_static_data(env, declaring_class);
			ClassFile* cfile = get_ClassFile(static_data);

			// 定義クラスローダを得る
			class_loader* loader = get_defining_loader(cfile);

			for (int i = 0; i < attr->number_of_exceptions; ++i) {
				u2 index = attr->exception_index_table[i];
				if (index) {
					// 例外クラス名を得る
					const java_utf8* name = get_CONSTANT_Class_info(cfile, index);
					jclass elem = find_class(env, name, loader);
					if (elem) {
						env->SetObjectArrayElement(result, i, elem);
					}
				}
			}
		}
	} else {
		// 大きさが0の配列を返す
		result = env->NewObjectArray(0, elem_class, NULL);
	}
	return result;
}

/*
 * Class:     java_lang_reflect_Method
 * Method:    invokeNative
 * Signature: (Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;I)Ljava/lang/Object;
 */
JNIEXPORT jobject JNICALL
Java_java_lang_reflect_Method_invokeNative(JNIEnv *env,
										   jobject thisobj,
										   jobject o,
										   jobjectArray args,
										   jclass declaringClass,
										   jint slot) {
	jobject static_data = get_static_data(env, declaringClass);
	ClassFile* cfile = get_ClassFile(static_data);
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);

	method_info* minfo = &cfile->methods[slot];
	if (! is_static(minfo->access_flags)) {
		if (! o) {
			throw_exception(current_frame, "java/lang/NullPointerException");
			return NULL;
		}
		if (! is_private(minfo->access_flags)) {
			// privateメソッドでない場合は、「実際の」クラスファイルから
			// メソッドを検索する
			cfile = get_ClassFile(o);
			assert(cfile);
		}

		jmethodID mid = get_method_id(cfile, minfo->name, minfo->descriptor); 
		assert(mid);
		minfo = get_method_info(cfile, mid);
		assert(minfo);
		cfile = minfo->declaring_ClassFile;
		assert(cfile);
	}

	// AbstractMethodError
	if (is_abstract(minfo->access_flags)) {
		throw_exception(current_frame, "java/lang/AbstractMethodError", minfo->name);
		return NULL;
	}

	// thisをpush
	if (! is_static(minfo->access_flags)) {
		PUSH_OBJECT(current_frame, o);
	}
	// Ojbect[] を jvalue* に変換する
	jvalue* values = NULL;
	if (args != NULL) {
		jsize arraylength = env->GetArrayLength(args);
		values = (jvalue*) malloc(sizeof(jvalue) * arraylength);
		if (values == NULL) {
			fatal_error(FATAL_ERROR_NO_MEMORY);
		}
		convert_to_jvalues(env, cfile, minfo->descriptor, args, values);
		if (env->ExceptionCheck()) {
			// 例外発生
			free(values);
			return NULL;
		}
		push_parameters(minfo->descriptor, current_frame, values);
		free(values);
	}
	
	// メソッドを起動する
	invoke_method(current_frame, o, cfile, minfo);

	jobject ret = NULL;
	const java_utf8* return_type = get_return_type(minfo);

	// 戻り値の型に応じて適切なクラスでラップする
	jmethodID mid;
	jclass retclass;
	switch (*return_type) {
	case '[':
	case 'L':
		{
			jobject o;
			POP_OBJECT(current_frame, o);
			ret = o;
		}
		break;

	case 'V':
		// void
		// 何もする必要なし
		break;

	case 'Z':
		// boolean
		{
			jboolean z;
			POP_INT(current_frame, z);

			// java.lang.Boolean でラップする
			retclass = env->FindClass("java/lang/Boolean");
			mid = env->GetStaticMethodID(retclass, "valueOf", "(Z)Ljava/lang/Boolean;");
			ret = env->CallStaticObjectMethod(retclass, mid, z);
		}
		break;

	case 'B':
		// byte
		{
			jbyte b;
			POP_INT(current_frame, b);
			// java.lang.Byte でラップする
			retclass = env->FindClass("java/lang/Byte");
			mid = env->GetStaticMethodID(retclass, "valueOf", "(B)Ljava/lang/Byte;");
			ret = env->CallStaticObjectMethod(retclass, mid, b);
		}
		break;

	case 'C':
		// char
		{
			jchar c;
			POP_INT(current_frame, c);
			// java.lang.Character でラップする
			retclass = env->FindClass("java/lang/Character");
			mid = env->GetStaticMethodID(retclass, "valueOf", "(C)Ljava/lang/Character;");
			ret = env->CallStaticObjectMethod(retclass, mid, c);
		}
		break;

	case 'S':
		// short
		{
			short s;
			POP_INT(current_frame, s);
			// java.lang.Short でラップする
			retclass = env->FindClass("java/lang/Short");
			mid = env->GetStaticMethodID(retclass, "valueOf", "(S)Ljava/lang/Short;");
			ret = env->CallStaticObjectMethod(retclass, mid, s);
		}
		break;

	case 'I':
		// int
		{
			jint i;
			POP_INT(current_frame, (__int32) i);
			// java.lang.Integer でラップする
			retclass = env->FindClass("java/lang/Integer");
			mid = env->GetStaticMethodID(retclass, "valueOf", "(I)Ljava/lang/Integer;");
			ret = env->CallStaticObjectMethod(retclass, mid, i);
		}
		break;

	case 'J':
		// long
		{
			jlong j;
			POP_LONG(current_frame, (__int64) j);
			// java.lang.Longでラップする
			retclass = env->FindClass("java/lang/Long");
			mid = env->GetStaticMethodID(retclass, "valueOf", "(J)Ljava/lang/Long;");
			ret = env->CallStaticObjectMethod(retclass, mid, j);
		}
		break;

	case 'F':
		// float
		{
			jfloat f;
			POP_FLOAT(current_frame, f);

			// java.lang.Floatでラップする
			retclass = env->FindClass("java/lang/Float");
			mid = env->GetStaticMethodID(retclass, "valueOf", "(F)Ljava/lang/Float;");
			ret = env->CallStaticObjectMethod(retclass, mid, f);
		}
		break;

	case 'D':
		// double
		{
			jdouble d;
			POP_DOUBLE(current_frame, d);
		
			// java.lang.Doubleでラップする
			retclass = env->FindClass("java/lang/Double");
			mid = env->GetStaticMethodID(retclass, "valueOf", "(D)Ljava/lang/Double;");
			ret = env->CallStaticObjectMethod(retclass, mid, d);
		}
		break;

	default:
		assert(false);	// デバッグ用
		env->FatalError("Unknown return type");
	}

	return ret;
}

/*
 * Class:     java_lang_reflect_Method
 * Method:    getSignature
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_java_lang_reflect_Method_getSignature(JNIEnv *env, jobject method) {
	ClassFile* cfile = NULL;
	method_info* minfo = NULL;
	get_Method_data(env, method, &cfile, &minfo);
	
	u2 sig_index = minfo->signature_index;
	if (sig_index) {
		const java_utf8* sig = (const java_utf8*) cfile->constant_pool[sig_index];
		return env->NewStringUTF((const char*) sig);
	}
	return NULL;
}

/*
 * Class:     java_lang_reflect_Method
 * Method:    getDefaultValue
 * Signature: ()Ljava/lang/Object;
 */
JNIEXPORT jobject JNICALL Java_java_lang_reflect_Method_getDefaultValue
(JNIEnv *, jobject) {
	// ToDo:implement
	return NULL;
}
/*
 * Class:     java_lang_reflect_Method
 * Method:    getParameterAnnotations
 * Signature: ()[[Ljava/lang/annotation/Annotation;
 */
JNIEXPORT jobjectArray JNICALL Java_java_lang_reflect_Method_getParameterAnnotations
(JNIEnv *, jobject) {
	// ToDo: implement
	return NULL;
}
/*
 * Class:     java_lang_VMFinalizeThread
 * Method:    run
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_java_lang_VMFinalizeThread_run(JNIEnv* env, jobject) {
	finalizer* f = get_finalizer();
	gc_run_finalizer(get_current_frame(((jni_env*) env)->jni_frame),
					 f);
}

/*
 * Class:     java_util_concurrent_atomic_AtomicLong
 * Method:    VMSupportsCS8
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8(JNIEnv *env, jclass clazz) {
	// 64ビット変数のアトミック操作はサポートしていない
	return JNI_FALSE;
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    objectFieldOffset
 * Signature: (Ljava/lang/reflect/Field;)J
 */
JNIEXPORT jlong JNICALL Java_sun_misc_Unsafe_objectFieldOffset(JNIEnv *env, jobject unsafe, jobject field) {
	jclass clazz = NULL;
	field_info* finfo = NULL;
	jfieldID fid = NULL;
	get_Field_data(env, field, &clazz, &finfo, &fid);

	return finfo->position;
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    compareAndSwapInt
 * Signature: (Ljava/lang/Object;JII)Z
 */
JNIEXPORT jboolean JNICALL Java_sun_misc_Unsafe_compareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint expect, jint update) {
	java_object* jobj = lock_address(obj);
	char* body = (char*) get_body_ptr(jobj);
	jboolean result = (InterlockedCompareExchange((LPLONG) (body + offset), update, expect) == expect) ? JNI_TRUE : JNI_FALSE;
	unlock_address(obj);
	return result;
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    compareAndSwapLong
 * Signature: (Ljava/lang/Object;JJJ)Z
 */
JNIEXPORT jboolean JNICALL Java_sun_misc_Unsafe_compareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong expect, jlong update) {
	// 64ビット長変数のアトミック操作はできないので、Unsafeをロックしてしまう
	env->MonitorEnter(unsafe);

	java_object* jobj = lock_address(obj);
	char* body = (char*) get_body_ptr(jobj);
	jlong* value = (jlong*) (body + offset);
	jboolean result = (*value == expect) ? JNI_TRUE : JNI_FALSE;
	if (result) {
		*value = update;
	}
	env->MonitorExit(unsafe);

	return result;
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    compareAndSwapObject
 * Signature: (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
 */
JNIEXPORT jboolean JNICALL Java_sun_misc_Unsafe_compareAndSwapObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject expect, jobject update) {
	java_object* jobj = lock_address(obj);
	char* body = (char*) get_body_ptr(jobj);
	jboolean result = (InterlockedCompareExchange((LPLONG) (body + offset), (LONG) update, (LONG) expect) == (LONG) expect) ? JNI_TRUE : JNI_FALSE;
	unlock_address(obj);
	return result;
}

/**
 * getXXX用のテンプレート関数
 */
template <typename T>
static inline T getValue(jobject obj, jlong offset) {
	java_object* jobj = lock_address(obj);
	char* body = (char*) get_body_ptr(jobj);
	T result = *(T*) (body + offset);
	unlock_address(obj);
	return result;
}

/**
 * putXXX用のテンプレート関数
 */
template <typename T>
static inline void putValue(jobject obj, jlong offset, T value) {
	java_object* jobj = lock_address(obj);
	char* body = (char*) get_body_ptr(jobj);
	T* p = (T*) (body + offset);
	*p = value;
	unlock_address(obj);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    putOrderedInt
 * Signature: (Ljava/lang/Object;JI)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putOrderedInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint value) {
	putValue(obj, offset, value);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    putOrderedLong
 * Signature: (Ljava/lang/Object;JJ)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putOrderedLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong value) {
	putValue(obj, offset, value);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    putOrderedObject
 * Signature: (Ljava/lang/Object;JLjava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putOrderedObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject value) {
	putValue(obj, offset, value);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    putIntVolatile
 * Signature: (Ljava/lang/Object;JI)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putIntVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint value){
	putValue(obj, offset, value);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    getIntVolatile
 * Signature: (Ljava/lang/Object;J)I
 */
JNIEXPORT jint JNICALL Java_sun_misc_Unsafe_getIntVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset) {
	return getValue<jint>(obj, offset);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    putLongVolatile
 * Signature: (Ljava/lang/Object;JJ)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putLongVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong value) {
	putValue(obj, offset, value);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    putLong
 * Signature: (Ljava/lang/Object;JJ)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong value) {
	putValue(obj, offset, value);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    getLongVolatile
 * Signature: (Ljava/lang/Object;J)J
 */
JNIEXPORT jlong JNICALL Java_sun_misc_Unsafe_getLongVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset) {
	return getValue<jlong>(obj, offset);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    getLong
 * Signature: (Ljava/lang/Object;J)J
 */
JNIEXPORT jlong JNICALL Java_sun_misc_Unsafe_getLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset) {
	return getValue<jlong>(obj, offset);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    putObjectVolatile
 * Signature: (Ljava/lang/Object;JLjava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putObjectVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject value) {
	putValue(obj, offset, value);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    putObject
 * Signature: (Ljava/lang/Object;JLjava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject value) {
	putValue(obj, offset, value);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    getObjectVolatile
 * Signature: (Ljava/lang/Object;J)Ljava/lang/Object;
 */
JNIEXPORT jobject JNICALL Java_sun_misc_Unsafe_getObjectVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset) {
	return getValue<jobject>(obj, offset);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    arrayBaseOffset
 * Signature: (Ljava/lang/Class;)I
 */
JNIEXPORT jint JNICALL Java_sun_misc_Unsafe_arrayBaseOffset(JNIEnv *, jobject, jclass) {
	return 0;
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    arrayIndexScale
 * Signature: (Ljava/lang/Class;)I
 */
JNIEXPORT jint JNICALL Java_sun_misc_Unsafe_arrayIndexScale(JNIEnv *env, jobject unsafe, jclass arrayClass) {
	jobject static_data = get_static_data(env, arrayClass);
	ClassFile* cfile = get_ClassFile(static_data);
	if (cfile->this_class_name[0] != '[') {
		return 0;
	}
	return get_array_component_size(cfile->this_class_name);
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    unpark
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_unpark(JNIEnv *env, jobject unsafe, jobject thread) {
	// ToDo: 実装する
}

/*
 * Class:     sun_misc_Unsafe
 * Method:    park
 * Signature: (ZJ)V
 */
JNIEXPORT void JNICALL Java_sun_misc_Unsafe_park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time) {
	// ToDo:正しく実装する
	Sleep(1);
}


/**
 * ネイティブメソッドを追加する
 */
static bool add_native_method_info(class_loader* loader,
								   const char* class_name,
								   const JNINativeMethod* method) {
	native_method_info* info = (native_method_info*) malloc(sizeof(native_method_info));
	if (info == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	info->loader = loader;
	info->class_name = intern_utf8(class_name);
	info->native_method = (JNINativeMethod*) malloc(sizeof(JNINativeMethod));
	if (info->native_method == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	// 「インターン」した結果を格納しておく
	info->native_method->name = const_cast<char*>(intern_utf8(method->name));
	info->native_method->signature = const_cast<char*>(intern_utf8(method->signature));
	info->native_method->fnPtr = method->fnPtr;

	g_native_methods_count++;
	if (g_native_methods_count >= g_native_methods_limit) {
		g_native_methods_limit += 64;
		g_native_methods
			= (native_method_info**) realloc(g_native_methods,
										  sizeof(native_method_info) * g_native_methods_limit);
	}
	bool result;
	if (g_native_methods != NULL) {
		g_native_methods[g_native_methods_count - 1] = info;
		result = true;
	} else {
		result = false;
	}
	return result;
}

/**
 * ネイティブメソッド情報を検索する
 * 引数 class_name は intern_utf8() されていることを前提とする
 */
static native_method_info* find_native_method_info(class_loader* loader, const java_utf8* class_name, const JNINativeMethod* method) {
	// intern_utf8()されていることを前提とする
	//	class_name = intern_utf8(class_name);

	native_method_info* result = NULL;
	for (int i = 0; i < g_native_methods_count; ++i) {
		native_method_info* info = g_native_methods[i];
		JNINativeMethod* m = info->native_method;
		assert(intern_utf8(info->class_name) == info->class_name);
		const java_utf8* info_classname = info->class_name;	// すでに「インターン」済み
		const java_utf8* info_name = m->name;		// すでに「インターン」済み
		const java_utf8* info_sig = m->signature;	// すでに「インターン」済み

		class_loader* cmploader;
		if (info->loader == NULL) {
			cmploader = loader;
		} else {
			cmploader = info->loader;
		}
		if (cmploader == loader
				&& info_classname == class_name
				&& info_name == method->name
				&& info_sig == method->signature) {
			result = info;
			break;
		}
	}
	return result;
}

/**
 * メソッドディスクリプタの内容に従い、指定された va_list の内容をスタックにpushする
 */
static void push_parameters(const java_utf8* method_desc, frame* current_frame, va_list args) {
	const java_utf8* bytes = method_desc;
	u4 length = strlen(method_desc);
	for (u4 pos = 0; pos < length; ++pos) {
		switch (bytes[pos]) {
		case 'Z':	// boolean
			{
				jboolean z = va_arg(args, jboolean);
				PUSH_INT(current_frame, z);
			}
			break;

		case 'B':	// byte
			{
				jbyte b = va_arg(args, jbyte);
				PUSH_INT(current_frame, b);
			}
			break;

		case 'C':	// char
			{
				jchar c = va_arg(args, jchar);
				PUSH_INT(current_frame, c);
			}
			break;

		case 'S':	// short
			{
				jshort s = va_arg(args, jshort);
				PUSH_INT(current_frame, s);
			}
			break;

		case 'I':	// int
			{
				jint i = va_arg(args, jint);
				PUSH_INT(current_frame, i);
			}
			break;

		case 'J':	// long
			{
				jlong l = va_arg(args, jlong);
				PUSH_LONG(current_frame, l);
			}
			break;

		case 'F':	// float
			{
				jfloat f = va_arg(args, jfloat);
				PUSH_FLOAT(current_frame, f);
			}
			break;

		case 'D':	// double
			{
				jdouble d = va_arg(args, jdouble);
				PUSH_DOUBLE(current_frame, d);
			}
			break;
		
		case 'L':	// オブジェクト参照
			{
				jobject o = va_arg(args, jobject);
				PUSH_OBJECT(current_frame, o);
				while (bytes[++pos] != ';') {
					if (pos >= length) {
						// 不正なシグニチャ
						assert(false);
						break;
					}
				}
			}
			break;
		
		case '[':	// 配列
			{
				jobject o = va_arg(args, jobject);
				PUSH_OBJECT(current_frame, o);
				// シグニチャを読み飛ばす
				if (bytes[pos + 1] == 'L') {
					// オブジェクトの配列
					while (bytes[++pos] != ';') {
						if (pos >= length) {
							// 不正なシグニチャ
							assert(false);
							break;
						}
					}
				} else {
					// プリミティブ配列
					pos++;
				}
			}
			break;

		case '(':
			break;

		case ')':	// 引数終了
			return;

		default:	// 想定外の事態
			assert(false);
		}
	}
}

/**
 * 指定されたメソッドディスクリプタの内容に従い、jvalue* の内容をスタックにpushする
 */
static void push_parameters(const java_utf8* method_desc, frame* current_frame, jvalue* args) {
	const java_utf8* bytes = method_desc;
	bytes++;		// 先頭を読み飛ばす
	while (*bytes && *bytes != ')') {
		switch (*bytes++) {
		case 'Z':	// boolean
			PUSH_INT(current_frame, args->z);
			break;

		case 'B':	// byte
			PUSH_INT(current_frame, args->b);
			break;

		case 'C':	// char
			PUSH_INT(current_frame, args->c);
			break;

		case 'S':	// short
			PUSH_INT(current_frame, args->s);
			break;

		case 'I':	// int
			PUSH_INT(current_frame, args->i);
			break;

		case 'J':	// long
			PUSH_LONG(current_frame, args->j);
			break;

		case 'F':	// float
			PUSH_FLOAT(current_frame, args->f);
			break;

		case 'D':	// double
			PUSH_DOUBLE(current_frame, args->d);
			break;
		
		case 'L':	// オブジェクト参照
			PUSH_OBJECT(current_frame, args->l);
			while (*bytes != ';') {
				bytes++;
			}
			bytes++;
			break;
		
		case '[':	// 配列
			{
				PUSH_OBJECT(current_frame, args->l);
				char ch = *bytes++;
				if (ch == 'L') {
					do {
						ch = *bytes++;
					} while (ch && ch != ';');
				} else {
					assert(ch == 'Z'
							|| ch == 'B'
							|| ch == 'C'
							|| ch == 'S'
							|| ch == 'I'
							|| ch == 'J'
							|| ch == 'F'
							|| ch == 'D');
				}
			}
			break;

		default:
			assert(false);
		}
		args++;
	}
}

/**
 * メソッドディスクリプタの内容に従い、指定されたObject[]の内容をjavlue*に変換する
 * プリミティブ型の場合にはラップ解除を行う
 */
static void convert_to_jvalues(JNIEnv* env, ClassFile* target_cfile, const java_utf8* method_desc, jobjectArray args, jvalue* values) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	// 引数の数が一致しているかを調べる
	u2 params_count = parse_parameters_count(method_desc);
	int argc = 0;
	if (args != NULL) {
		argc = get_array_length(args);
	}
	if (params_count != argc) {
		jclass jae_class = env->FindClass("java/lang/IllegalArgumentException");
		if (env->ExceptionCheck()) {
			return;
		}
		env->ThrowNew(jae_class, NULL);
		return;
	}

	// パラメータ無し
	if (params_count == 0) {
		return;
	}
	
	// ラップ解除用メソッドのメソッドID
	jmethodID boolean_mid = NULL;
	jmethodID byte_mid = NULL;
	jmethodID short_mid = NULL;
	jmethodID char_mid = NULL;
	jmethodID int_mid = NULL;
	jmethodID long_mid = NULL;
	jmethodID float_mid = NULL;
	jmethodID double_mid = NULL;
	
	// ラッパクラス
	jclass boolean_class = NULL;
	jclass byte_class = NULL;
	jclass short_class = NULL;
	jclass char_class = NULL;
	jclass int_class = NULL;
	jclass long_class = NULL;
	jclass float_class = NULL;
	jclass double_class = NULL;

	bool error = false;
	int parameter_index = 0;
	const java_utf8* bytes = method_desc + 1;
	while (*bytes && *bytes != ')') {
		char c = *bytes++;
		switch (c) {
		case 'Z':	// boolean
			{
				if (boolean_mid == NULL) {
					boolean_class = env->FindClass("java/lang/Boolean");
					if (boolean_class == NULL) {
						error = true;
						break;
					}
					boolean_mid = env->GetMethodID(boolean_class, "booleanValue", "()Z");
					if (boolean_mid == NULL) {
						error = true;
						break;
					}
				}
				// 代入可能かを調べる
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				if (arg != NULL && ! env->IsInstanceOf(arg, boolean_class)) {
					error = true;
					break;
				}

				// ラップ解除し、値を格納する
				values->z = env->CallBooleanMethod(arg, boolean_mid);
				values++;
			}
			break;

		case 'B':	// byte
			{
				if (byte_mid == NULL) {
					byte_class = env->FindClass("java/lang/Byte");
					if (byte_class == NULL) {
						error = true;
						break;
					}
					byte_mid = env->GetMethodID(byte_class, "byteValue", "()B");
					if (byte_mid == NULL) {
						error = true;
						break;
					}
				}
				// 代入可能かを調べる
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				if (arg != NULL && ! env->IsInstanceOf(arg, byte_class)) {
					error = true;
					break;
				}
				// ラップ解除し、値を格納する
				values->b = env->CallByteMethod(arg, byte_mid);
				values++;
			}
			break;

		case 'C':	// char
			{
				if (char_mid == NULL) {
					char_class = env->FindClass("java/lang/Character");
					if (char_class == NULL) {
						error = true;
						break;
					}
					char_mid = env->GetMethodID(char_class, "charValue", "()C");
					if (char_mid == NULL) {
						error = true;
						break;
					}
				}
				// 代入可能かを調べる
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				if (arg != NULL && ! env->IsInstanceOf(arg, char_class)) {
					error = true;
					break;
				}
				// ラップ解除し、値を格納する
				values->c = env->CallCharMethod(arg, char_mid);
				values++;
			}
			break;

		case 'S':	// short
			{
				if (short_mid == NULL) {
					short_class = env->FindClass("java/lang/Short");
					if (short_class == NULL) {
						error = true;
						break;
					}
					short_mid = env->GetMethodID(short_class, "shortValue", "()S");
					if (short_mid == NULL) {
						error = true;
						break;
					}
				}
				// 代入可能かを調べる
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				if (arg != NULL && ! env->IsInstanceOf(arg, short_class)) {
					error = true;
					break;
				}
				// ラップ解除し、値を格納する
				values->s = env->CallShortMethod(arg, short_mid);
				values++;
			}
			break;

		case 'I':	// int
			{
				if (int_mid == NULL) {
					int_class = env->FindClass("java/lang/Integer");
					if (int_class == NULL) {
						error = true;
						break;
					}
					int_mid = env->GetMethodID(int_class, "intValue", "()I");
					if (int_mid == NULL) {
						error = true;
						break;
					}
				}
				// 代入可能かを調べる
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				if (arg != NULL && ! env->IsInstanceOf(arg, int_class)) {
					error = true;
					break;
				}
				// ラップ解除し、値を格納する
				values->i = env->CallIntMethod(arg, int_mid);
				values++;
			}
			break;

		case 'J':	// long
			{
				if (long_mid == NULL) {
					long_class = env->FindClass("java/lang/Long");
					if (long_class == NULL) {
						error = true;
						break;
					}
					long_mid = env->GetMethodID(long_class, "longValue", "()J");
					if (long_mid == NULL) {
						error = true;
						break;
					}
				}
				// 代入可能かを調べる
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				if (arg != NULL && ! env->IsInstanceOf(arg, long_class)) {
					error = true;
					break;
				}
				// ラップ解除し、値を格納する
				values->j = env->CallLongMethod(arg, long_mid);
				values++;
			}
			break;

		case 'F':	// float
			{
				if (float_mid == NULL) {
					float_class = env->FindClass("java/lang/Float");
					if (float_class == NULL) {
						error = true;
						break;
					}
					float_mid = env->GetMethodID(float_class, "floatValue", "()F");
					if (float_mid == NULL) {
						error = true;
						break;
					}
				}
				// 代入可能かを調べる
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				if (arg != NULL && ! env->IsInstanceOf(arg, float_class)) {
					error = true;
					break;
				}
				// ラップ解除し、値を格納する
				values->f = env->CallFloatMethod(arg, float_mid);
				values++;
			}
			break;

		case 'D':	// double
			{
				if (double_mid == NULL) {
					double_class = env->FindClass("java/lang/Double");
					if (double_class == NULL) {
						error = true;
						break;
					}
					double_mid = env->GetMethodID(double_class, "doubleValue", "()D");
					if (double_mid == NULL) {
						error = true;
						break;
					}
				}
				// 代入可能かを調べる
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				if (arg != NULL && ! env->IsInstanceOf(arg, double_class)) {
					error = true;
					break;
				}
				// ラップ解除し、値を格納する
				values->d = env->CallDoubleMethod(arg, double_mid);
				values++;
			}
			break;
		
		case '[':	// 配列
			{
				char ch = *bytes++;
				if (ch == 'L') {
					// オブジェクトの配列
					do {
						ch = *bytes++;
					} while (ch && ch != ';');
				} else if (! (ch == 'Z'
							|| ch == 'B'
							|| ch == 'S'
							|| ch == 'C'
							|| ch == 'I'
							|| ch == 'J'
							|| ch == 'F'
							|| ch == 'D')) {
					error = true;
				}
				// 値を格納する
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				values->l = arg;
				values++;
			}
			break;

		case 'L':	// オブジェクト参照
			{
				// クラス名を取得する
				const char* start = bytes;
				while (*bytes && *bytes != ';') {
					bytes++;
				}
				int len = bytes - start;
				if (*bytes) {
					bytes++;
				}
				char* class_name = (char*) malloc(len + 1);
				if (class_name == NULL) {
					fatal_error(FATAL_ERROR_NO_MEMORY);
				}
				memcpy(class_name, start, len);
				class_name[len] = '\0';

				// クラスオブジェクトをロードする
				jclass arg_class = find_class(env, class_name, get_defining_loader(target_cfile));
				free(class_name);
				if (arg_class == NULL) {
					error = true;
					break;
				}
				// 代入可能かを調べる
				jobject arg = env->GetObjectArrayElement(args, parameter_index++);
				if (arg != NULL && ! env->IsInstanceOf(arg, arg_class)) {
					error = true;
					break;
				}

				// 値を格納する
				values->l = arg;
				values++;
			}
			break;
		}
		if (error) {
			// うまくいかなかった場合には IllegalArgumentExceptionをthrowする
			jclass jae_class = env->FindClass("java/lang/IllegalArgumentException");
			if (env->ExceptionCheck()) {
				return;
			}
			env->ThrowNew(jae_class, NULL);
			break;
		}
	}
}


void call_native_function(void* func_ptr, frame* caller_frame, ClassFile* target_class_file, method_info* target_method_info) {
	// パラメータを初期化する
	u2 parameters_count = get_parameters_count(target_method_info);
	if (parameters_count > MAX_PARAMETERS_COUNT) {
		// パラメータ数オーバー（現在の実装の限界）
		throw_exception(caller_frame,
						"java/lang/VirtualMachineError",
						"Too many native method parameters");

		return;
	}

	// ネイティブメソッド用のフレームを作成する
	frame* new_frame = enter_method(caller_frame, target_class_file, target_method_info);
	if (! new_frame) {
		return;
	}
	
	// jni_envを初期化する
	frame new_local_frame;
	jni_env* env = get_jni_env(new_frame, &new_local_frame);
	if (! env) {
		// フレームを元に戻す
		leave_method(new_frame);
		return;
	}

	frame* current_frame = get_current_frame(env->jni_frame);

	// staticメソッドの場合とインスタンスメソッドの場合とでは、渡すオブジェクトと引数の
	// 位置が異なる
	int local_pos;
	jobject obj;
	if (is_static(target_method_info->access_flags)) {
		// jclassが引数になる
		jobject static_data = get_static_data(target_class_file);
		if (static_data == NULL) {
			throw_OutOfMemoryError(current_frame);
			release_jni_env(env);
			// フレームを元に戻す
			leave_method(new_frame);
			return;
		}
		jclass clazz = (jclass) find_class_object(current_frame, static_data, NULL);
		if (clazz == NULL) {
			throw_OutOfMemoryError(current_frame);
			release_jni_env(env);
			// フレームを元に戻す
			leave_method(new_frame);
			return;
		}
		obj = clazz;
		local_pos = 0;
	} else {
		//  jobject (this)
		obj = (jobject) get_int(current_frame, 0);
		local_pos = 1;
	}

	if (parameters_count <= 2) {
		// レジスタ渡し(ARMプロセッサの場合）
		__int32 param1 = (parameters_count >= 1) ? get_int(current_frame, local_pos)     : 0;
		__int32 param2 = (parameters_count >= 2) ? get_int(current_frame, local_pos + 1) : 0;
		switch (*get_return_type(target_method_info)) {
		case 'V':	// 戻り値なし
			{
				VOID_REGISTER_FUNC func = (VOID_REGISTER_FUNC) func_ptr;
				func(env, obj, param1, param2);
			}
			break;

		case 'Z':	// boolean
			{
				BOOLEAN_REGISTER_FUNC func = (BOOLEAN_REGISTER_FUNC) func_ptr;
				__int32 result = func(env, obj, param1, param2);
				PUSH_INT(new_frame, result);
			}
			break;
		
		case 'B':	// byte
			{
				BYTE_REGISTER_FUNC func = (BYTE_REGISTER_FUNC) func_ptr;
				__int32 result = func(env, obj, param1, param2);
				PUSH_INT(new_frame, result);
			}
			break;
		
		case 'S':	// short
			{
				SHORT_REGISTER_FUNC func = (SHORT_REGISTER_FUNC) func_ptr;
				__int32 result = func(env, obj, param1, param2);
				PUSH_INT(new_frame, result);
			}
			break;

		case 'C':	// char
			{
				CHAR_REGISTER_FUNC func = (CHAR_REGISTER_FUNC) func_ptr;
				unsigned __int32 result = func(env, obj, param1, param2);
				PUSH_INT(new_frame, result);
			}
			break;

		case 'I':	// int
			{
				INT_REGISTER_FUNC func = (INT_REGISTER_FUNC) func_ptr;
				__int32 result = func(env, obj, param1, param2);
				PUSH_INT(new_frame, result);
			}
			break;

		case 'J':	// long
			{
				LONG_REGISTER_FUNC func = (LONG_REGISTER_FUNC) func_ptr;
				__int64 result = func(env, obj, param1, param2);
				PUSH_LONG(new_frame, result);
			}
			break;

		case 'F':	// float
			{
				FLOAT_REGISTER_FUNC func = (FLOAT_REGISTER_FUNC) func_ptr;
				float result = func(env, obj, param1, param2);
				PUSH_FLOAT(new_frame, result);
			}
			break;

		case 'D':	// double
			{
				DOUBLE_REGISTER_FUNC func = (DOUBLE_REGISTER_FUNC) func_ptr;
				double result = func(env, obj, param1, param2);
				PUSH_DOUBLE(new_frame, result);
			}
			break;

		case '[':	// 配列
		case 'L':	// オブジェクト(Lオブジェクト名;)
			{
				OBJECT_REGISTER_FUNC func = (OBJECT_REGISTER_FUNC) func_ptr;
				jobject result = func(env, obj, param1, param2);
				PUSH_OBJECT(new_frame, result);
			}
			break;

		default:	// ここにはこないはず
			assert(false);
		}

	} else if (parameters_count <= MAX_PARAMETERS_COUNT) {
		native_method_parameters params;
		// 0番目のパラメータは JNIEnv*
		params.values[0] = (__int32) env;
		// 1番目のパラメータ
		params.values[1] = (__int32) obj;

		// 2番目以降は、メソッドのパラメータを渡す
		for (unsigned int pos = 1; pos <= parameters_count; ++pos) {
			params.values[pos + 1] = get_int(current_frame, local_pos++);
		}

		switch (*get_return_type(target_method_info)) {
		case 'V':	// 戻り値なし
			{
				VOID_FUNC func = (VOID_FUNC) func_ptr;
				func(params);
			}
			break;

		case 'Z':	// boolean
			{
				BOOLEAN_FUNC func = (BOOLEAN_FUNC) func_ptr;
				__int32 result = func(params);
				PUSH_INT(new_frame, result);
			}
			break;
		
		case 'B':	// byte
			{
				BYTE_FUNC func = (BYTE_FUNC) func_ptr;
				__int32 result = func(params);
				PUSH_INT(new_frame, result);
			}
			break;
		
		case 'S':	// short
			{
				SHORT_FUNC func = (SHORT_FUNC) func_ptr;
				__int32 result = func(params);
				PUSH_INT(new_frame, result);
			}
			break;

		case 'C':	// char
			{
				CHAR_FUNC func = (CHAR_FUNC) func_ptr;
				unsigned __int32 result = func(params);
				PUSH_INT(new_frame, result);
			}
			break;

		case 'I':	// int
			{
				INT_FUNC func = (INT_FUNC) func_ptr;
				__int32 result = func(params);
				PUSH_INT(new_frame, result);
			}
			break;

		case 'J':	// long
			{
				LONG_FUNC func = (LONG_FUNC) func_ptr;
				__int64 result = func(params);
				PUSH_LONG(new_frame, result);
			}
			break;

		case 'F':	// float
			{
				FLOAT_FUNC func = (FLOAT_FUNC) func_ptr;
				float result = func(params);
				PUSH_FLOAT(new_frame, result);
			}
			break;

		case 'D':	// double
			{
				DOUBLE_FUNC func = (DOUBLE_FUNC) func_ptr;
				double result = func(params);
				PUSH_DOUBLE(new_frame, result);
			}
			break;

		case '[':	// 配列
		case 'L':	// オブジェクト(Lオブジェクト名;)
			{
				OBJECT_FUNC func = (OBJECT_FUNC) func_ptr;
				jobject result = func(params);
				PUSH_OBJECT(new_frame, result);
			}
			break;

		default:	// ここにはこないはず
			assert(false);
		}
	} else {
		assert(false);
	}

	// フレームを元に戻す
	leave_method(new_frame);

	// jni_envを削除する
	release_jni_env(env);
}


/**
 * 指定されたネイティブメソッドに対応する関数のポインタを返す
 */
void* find_native_proc_ptr(ClassFile* cfile, method_info* minfo) {
	assert(is_native(minfo->access_flags));

	JNINativeMethod method;
	method.name = const_cast<char*>(minfo->name);
	method.signature = const_cast<char*>(minfo->descriptor);
	class_loader* loader = get_defining_loader(cfile);
	
	void* ptr = static_cast<void*>(get_Code_attribute(cfile, minfo));
	if (ptr == NULL) {
		// 内部テーブルから情報を検索する
		native_method_info* info = find_native_method_info(loader, cfile->this_class_name, &method);
		if (info != NULL) {
			ptr = info->native_method->fnPtr;
		} else {
			// ロード済みDLLから目的関数のポインタを取得する。
			const java_utf8* mname = minfo->name;
			const java_utf8* mdesc = minfo->descriptor;
			
			TCHAR* class_name = (TCHAR*) alloca(sizeof(TCHAR) * (strlen(cfile->this_class_name) * 2));
			TCHAR* method_name = (TCHAR*) alloca(sizeof(TCHAR) * (strlen(mname) * 2));
			TCHAR* method_desc = (TCHAR*) alloca(sizeof(TCHAR) * (strlen(mdesc) * 2));
			
			convert_to_TCHAR(cfile->this_class_name, class_name, strlen(cfile->this_class_name) * 2);
			convert_to_TCHAR(mname, method_name, strlen(mname) * 2);
			convert_to_TCHAR(mdesc, method_desc, strlen(mdesc) * 2);

			int bufflen = _tcslen(class_name) + _tcslen(method_name) + _tcslen(method_desc);
			bufflen *= 6;	// 最大で6倍の長さになる
			TCHAR* buff = (TCHAR*) alloca(sizeof(TCHAR) * bufflen);
			const TCHAR* buffstart = buff;

			_tcscpy(buff, _T("Java_"));
			buff += 5;

			int namelen = create_native_method_name(class_name, buff);
			buff += namelen;

			*buff++ = _T('_');

			namelen = create_native_method_name(method_name, buff);
			for (unsigned int i = 0; i < g_dlls_count; ++i) {
				HMODULE hModule = g_hmodules[i];
				ptr = GetProcAddress(hModule, buffstart);
				if (ptr != NULL) {
					break;
				}
			}
			if (! ptr) {
				// メソッドディスクリプタを追加して、長い名前を作る
				buff += namelen;

				*buff++ = _T('_');
				*buff++ = _T('_');

				namelen = create_native_method_name(method_desc, buff);
				for (unsigned int i = 0; i < g_dlls_count; ++i) {
					HMODULE hModule = g_hmodules[i];
					ptr = GetProcAddress(hModule, buffstart);
					if (ptr != NULL) {
						break;
					}
				}
			}
		}
		// method_info内に値をキャッシュしておく
		minfo->code_attribute = static_cast<Code_attribute*>(ptr);
	}
	
	return ptr;
}


/**
 * ネイティブ関数の名称を作成する
 *
 * @param	java_name	Java上の名前
 * @param	buff		変換後の名前が入るバッファ
 * @param	bufflen		バッファのサイズ
 */
static int create_native_method_name(const _TCHAR* java_name, _TCHAR* buff) {
	const _TCHAR* start_address = buff;
	while (*java_name) {
		const _TCHAR c = *java_name++;
		if ((c >= _T('A') && c <= _T('Z'))
				|| (c >= _T('a') && c <= _T('z'))
				|| (c >= _T('0') && c <= _T('9'))) {
			// 英数字はそのまま格納
			*buff++ = c;

		} else if (c == _T('/')) {
			// '/' は '_' に変換
			*buff++ = _T('_');

		} else if (c == _T('_')) {
			// '_' は "_1" に変換
			*buff++ = _T('_');
			*buff++ = _T('1');

		} else if (c == _T(';')) {
			// ';' は "_2"に変換
			*buff++ = _T('_');
			*buff++ = _T('2');

		} else if (c == _T('[')) {
			// '[' は "_3"に変換
			*buff++ = _T('_');
			*buff++ = _T('3');

		} else if (c == ')') {
			// メソッドディスクリプタ中の引数定義の終わり
			break;

		} else if (c == '(') {
			// メソッドディスクリプタの始まり
			continue;

		} else {
			// 上記以外の文字は "_0xxxx" (xxxxは文字コードの16進数表記) であらわす
			_stprintf(buff, _T("_0%04x"), c);
			buff += 6;
		}
	}
	// 文字列末尾
	*buff = _T('\0');

	// バッファに追加した文字数を返す
	return (int) (buff - start_address);
}

/**
 * jni_env構造体を取得する
 * @param current_frame カレントフレーム
 * @param new_local_frame ローカル参照用のframeへのポインタ
 */
static jni_env* get_jni_env(frame* current_frame, frame* new_local_frame) {
	JNIEnv* env = get_JNIEnv(current_frame);
	jni_env* jenv = (jni_env*) env;
	if (! jenv) {
		// 新たにjni_envを確保する
		jenv = (jni_env*) calloc(sizeof(jni_env), 1);
		jenv->env.p = &g_functions;

		// フレームを設定する
		// ToDo:この部分の処理はおかしい気がする。確認する
		frame* prev = current_frame->previous_frame;
		if (! prev) {
			jenv->jni_frame = current_frame;
		} else {
			while (prev->previous_frame) {
				prev = prev->previous_frame;
			}
			jenv->jni_frame = prev;
		}

		set_JNIEnv(current_frame, (JNIEnv*) jenv);
	}
	if (push_local_frame(jenv->jni_frame,
					 new_local_frame,
					 DEFAULT_LOCAL_REFERENCES_COUNT) < 0) {
		return NULL;
	}
	return jenv;
}

/**
 * jni_env構造体を開放する。
 */
static void release_jni_env(jni_env* env) {
	// ローカルフレームをpopする
	pop_local_frame(env->jni_frame, NULL);
}


// これ以降はJNI関数の本体

/**
 * GetVersion本体
 */
jint jni_get_version(JNIEnv *env) {
	return JNI_VERSION_1_4;	// JNI1.4
}

/**
 * DefineClassの本体
 */
jclass JNICALL jni_define_class(JNIEnv *env, const char *name, jobject cl, const jbyte *buf, jsize len) {
	return define_class(env, name, cl, buf, 0, len, NULL);
}

/**
 * 指定された名前のクラスを、指定されたクラスローダを用いて検索する
 */
static jclass find_class(JNIEnv* env, const char* name, class_loader* loader) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	ClassFile* target_class_file = find_ClassFile(current_frame, loader, intern_utf8(name));
	if (target_class_file == NULL) {
		if (env->ExceptionCheck()) {
			return NULL;
		}
		jclass ncdfe = env->FindClass("java/lang/NoClassDefFoundError");
		if (ncdfe != NULL) {
			env->ThrowNew(ncdfe, name);
		}
		return NULL;
	}

	jobject static_data = get_static_data(target_class_file); 
	jclass ret = NULL;
	if (static_data != NULL) {
		ret = (jclass) find_class_object(get_current_frame(((jni_env*) env)->jni_frame), static_data, NULL);
		if (ret != NULL) {
			ret = (jclass) new_local_reference(((jni_env*) env)->jni_frame, ret);
		}
	}
	return ret;
}


/**
 * FindClass本体
 */
jclass JNICALL jni_find_class(JNIEnv *env, const char *name) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	ClassFile* cfile = current_frame->current_class_file;
	class_loader* loader = get_defining_loader(cfile);

	// Bug #12354 に対する暫定対処
	// 現在の実装では、カレントメソッドが VMRuntime.nativeLoad() の場合にのみクラスローダに対して
	// 特別な処理を行っている。
	// ToDo: GetEnv()経由で取得されたJNIEnv* の場合にのみこの処理を実行するように修正する

	static java_utf8* vmRuntime;
	static java_utf8* nativeLoad;
	if (! vmRuntime && ! nativeLoad) {
		vmRuntime = intern_utf8("java/lang/VMRuntime");
		nativeLoad = intern_utf8("nativeLoad");
	}

	jclass result = find_class(env, name, loader);
	if (! result && cfile->this_class_name == vmRuntime && current_frame->current_method_info->name == nativeLoad) {
		// ClassLoader.getSystemClassLoader()を呼び出す
		jclass clazz = find_class(env, "java/lang/ClassLoader", loader);
		jmethodID mid = env->GetStaticMethodID(clazz, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
		jobject cl = env->CallStaticObjectMethod(clazz, mid);
		if (env->ExceptionCheck()) {
			// 例外発生は無視
			env->ExceptionClear();
		} else {
			loader = get_class_loader(cl);
			result = find_class(env, name, loader);
		}
	}
	return result;
}

/**
 * FromReflectedMethod本体
 */
jmethodID JNICALL jni_from_reflected_method(JNIEnv* env, jobject method) {
	jmethodID result = NULL;
	ClassFile* cfile = NULL;
	method_info* minfo = NULL;

	// 引数 method は、java.lang.reflect.Method または java.lang.reflect.Constructor
	jclass clazz = env->FindClass("java/lang/reflect/Constructor");
	if (env->IsInstanceOf(method, clazz)) {
		// 引数 method は java.lang.reflect.Constructor である
		get_Constructor_data(env, method, &cfile, &minfo);
	} else {
		clazz = env->FindClass("java/lang/reflect/Method");
		if (env->IsInstanceOf(method, clazz)) {
			// 引数 method は java.lang.reflect.Method である
			get_Method_data(env, method, &cfile, &minfo);
		}
	}
	if (cfile && minfo) {
		result = get_method_id(cfile, minfo->name, minfo->descriptor);
	}
	return result;
}

/**
 * FromReflectedField本体
 */
jfieldID JNICALL jni_from_reflected_field(JNIEnv* env, jobject field) {
	jclass declaring = NULL;
	field_info* finfo = NULL;
	jfieldID fid = NULL;
	get_Field_data(env, field, &declaring, &finfo, &fid);
	return fid;
}

/**
 * GetSuperClassの本体
 */
jclass JNICALL jni_get_superclass(JNIEnv *env, jclass sub) {
	ClassFile* cfile = get_ClassFile(get_static_data(env, sub));
//	class_loader* loader = get_defining_loader(cfile);
//	if (cfile->super_class_name == NULL) {
//		return NULL;
//	}
//	ClassFile* super_class_file = find_ClassFile(get_current_frame(((jni_env*)env)->jni_frame),
//												loader,
//												cfile->super_class_name);
	ClassFile* super_class_file = get_superclass_ClassFile(cfile);
	if (! super_class_file) {
		return NULL;
	}

	jobject static_data = get_static_data(super_class_file);
	jclass ret = NULL;
	if (static_data != NULL) {
		ret = (jclass) find_class_object(get_current_frame(((jni_env*) env)->jni_frame), static_data, NULL);
		if (ret != NULL) {
			ret = (jclass) new_local_reference(((jni_env*) env)->jni_frame, ret);
		}
	}
	return ret;
}

/**
 * IsAssignableFromの本体
 */
jboolean JNICALL jni_is_assignable_from(JNIEnv *env, jclass sub, jclass sup) {
	jobject subsd = get_static_data(env, sub);
	jobject supsd = get_static_data(env, sup);
	return is_assignable_from(get_current_frame(((jni_env*) env)->jni_frame), get_ClassFile(subsd), get_ClassFile(supsd));
}

/**
 * Throw本体
 */
jint JNICALL jni_throw(JNIEnv *env, jthrowable obj) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	return throw_exception(current_frame, obj);
}

/**
 * ThrowNew本体
 */
jint JNICALL jni_throw_new(JNIEnv *env, jclass clazz, const char *msg) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (! clazz) {
		return throw_exception(current_frame, "java/lang/NullPointerException");
	}
	jobject static_data = get_static_data(env, clazz);
	return throw_exception(current_frame, get_ClassFile(static_data), (java_utf8*)msg);
}

/**
 * ExceptionOccurred本体
 */
jthrowable JNICALL jni_exception_occurred(JNIEnv *env) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	return (jthrowable) new_local_reference(jenv->jni_frame, exception_occurred(current_frame));
}

/**
 * ExceptionDescribe本体
 */
void JNICALL jni_exception_describe(JNIEnv *env) {
	// 現在の実装では何もしない
}

/**
 * ExceptionClear本体
 */
void JNICALL jni_exception_clear(JNIEnv *env) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	exception_clear(current_frame);
}

/**
 * FatalError本体
 */
void JNICALL jni_fatal_error(JNIEnv *env, const char *msg) {
	fatal_error(NULL);
}

/**
 * NewGlobalRef本体
 */
jobject JNICALL jni_new_global_ref(JNIEnv *env, jobject lobj) {
	return new_global_reference(lobj);
}

/**
 * DeleteGlobalRef本体
 */
void JNICALL jni_delete_global_ref(JNIEnv *env, jobject gref) {
	delete_global_reference(gref);
}

/**
 * DeleteLocalRef本体
 */
void JNICALL jni_delete_local_ref(JNIEnv *env, jobject obj) {
	jni_env* jenv = (jni_env*) env;
	delete_local_reference(jenv->jni_frame, obj);
}

/**
 * IsSameObject本体
 */
jboolean JNICALL jni_is_same_object(JNIEnv *env, jobject obj1, jobject obj2) {
	// 現在の実装ではローカル参照もグローバル参照も jobject の値としては
	// すべて同一となる
	return obj1 == obj2;
}

/**
 * NewLocalRef本体
 */
jobject JNICALL jni_new_local_ref(JNIEnv *env, jobject ref) {
	jni_env* jenv = (jni_env*) env;
	return new_local_reference(jenv->jni_frame, ref);
}

/**
 * EnsureLocalCapacity本体
 */
jint JNICALL jni_ensure_local_capacity(JNIEnv *env, jint capacity) {
	// 現在の実装では何もしない。
	return 0;
}

/**
 * AllocObject本体
 */
jobject JNICALL jni_alloc_object(JNIEnv *env, jclass clazz) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);
	jobject ref = alloc_object(cfile, current_frame);
	if (ref == NULL) {
		return NULL;
	}
	
	// ローカル参照として登録する
	ref = new_local_reference(jenv->jni_frame, ref);

	// スタックからpopする
	POP_DISCARD(current_frame);
	
	return ref;
}

/**
 * NewObject本体
 */
jobject JNICALL jni_new_object(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jobject obj = jni_new_object_v(env, clazz, methodID, args);
	va_end(args);
	
	return obj;
}

/**
 * NewObjectV本体
 */
jobject JNICALL jni_new_object_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	return jni_new_object_common(env, clazz, methodID, VA_LIST, args);
}

/**
 * NewObjectA本体
 */
jobject JNICALL jni_new_object_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) {
	return jni_new_object_common(env, clazz, methodID, JVALUE, args);
}

/**
 * NewObjectV/NewObjectA共通関数
 */
static jobject jni_new_object_common(JNIEnv *env, jclass clazz, jmethodID methodID, native_argtype_t argtype, const void* args) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);
	// オブジェクトを作成する（と同時にスタックにpushされる）
	jobject ref = alloc_object(cfile, current_frame);
	if (ref == NULL) {
		return NULL;
	}

	// パラメータをスタックに積む
	method_info* minfo = get_method_info(cfile, methodID);
	const java_utf8* desc = minfo->descriptor;
	
	if (argtype == VA_LIST) {
		push_parameters(desc, current_frame, (va_list) args);
	} else {
		push_parameters(desc, current_frame, (jvalue*) args);
	}

	// ローカル参照として登録する
	ref = new_local_reference(jenv->jni_frame, ref);

	// 指定されたメソッド（コンストラクタ）を起動する
	// この関数からリターンした時点で current_frame 内のオブジェクトはpopされる。
	invoke_method(current_frame, ref, cfile, minfo);

	return ref;
}

/**
 * GetObjectClass本体
 */
jclass JNICALL jni_get_object_class(JNIEnv *env, jobject obj) {
	ClassFile* cfile = get_ClassFile(obj);
	jobject static_data = get_static_data(cfile);
	jclass ret = find_class_object(get_current_frame(((jni_env*) env)->jni_frame), static_data, NULL);
	
	// ローカル参照への登録は不要（クラスオブジェクトはガベージコレクトの対象にならないため）
//	if (ret != NULL) {
//		jni_env* jenv = (jni_env*) env;
//		ret = (jclass) new_local_reference(jenv->jni_frame, ret);
//	}
	return ret;
}

/**
 * IsInstanceOf本体
 */
jboolean JNICALL jni_is_instance_of(JNIEnv *env, jobject obj, jclass clazz) {
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);
	return (jboolean) is_assignable_from(get_current_frame(((jni_env*) env)->jni_frame), get_ClassFile(obj), cfile);
}

/**
 * GetMethodID本体
 */
jmethodID JNICALL jni_get_method_id(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	jmethodID mid = get_method_id(cfile, intern_utf8(name), intern_utf8(sig));
	if (mid != NULL) {
		// 本当にインスタンスメソッドかを調べる
		method_info* minfo = get_method_info(cfile, mid);
		if (is_static(minfo->access_flags)) {
			mid = NULL;
		}
	}

	if (! mid) {
		// NoSuchMethodErrorをthrowする
		jclass nsme = env->FindClass("java/lang/NoSuchMethodError");
		if (nsme) {
			env->ThrowNew(nsme, name);
		}
	}

	return mid;
}

/**
 * メソッド呼び出し汎用関数
 */
static inline void jni_call_method_common(JNIEnv *env, jobject obj, jmethodID methodID, native_argtype_t args_type, const void* args) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	// method_infoを取得する
	ClassFile* cfile = get_ClassFile(obj);
	method_info* minfo = get_method_info(cfile, methodID);
	ClassFile* declaring = minfo->declaring_ClassFile;
	const java_utf8* desc = minfo->descriptor;

	// thisをpushする
	PUSH_OBJECT(current_frame, obj);

	// パラメータをpushする
	if (args_type == VA_LIST) {
		push_parameters(desc, current_frame, (va_list) args);
	} else {
		push_parameters(desc, current_frame, (jvalue*) args);
	}

	// メソッドを呼び出す
	invoke_method(current_frame, obj, declaring, minfo);
}

/**
 * Non-virtualメソッド呼び出し汎用関数
 */
inline static void jni_call_nonvirtual_method_common(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, native_argtype_t args_type, const void* args) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	// method_infoを取得する
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	method_info* minfo = get_method_info(cfile, methodID);
	ClassFile* declaring = minfo->declaring_ClassFile;
	const java_utf8* desc = minfo->descriptor;

	// thisをpushする
	PUSH_OBJECT(current_frame, obj);

	// パラメータをpushする
	if (args_type == VA_LIST) {
		push_parameters(desc, current_frame, (va_list) args);
	} else {
		push_parameters(desc, current_frame, (jvalue*) args);
	}

	// メソッドを呼び出す
	invoke_method(current_frame, obj, declaring, minfo);
}

/**
 * 戻り値が32ビット値のメソッドを呼び出す汎用関数
 */
static inline __int32 jni_call_int32_method(JNIEnv *env, jobject obj, jmethodID methodID, native_argtype_t type, const void* args) {
	// メソッドを呼び出す
	jni_call_method_common(env, obj, methodID, type, args);

	// 例外発生
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (exception_occurred(current_frame)) {
		// 例外発生
		return 0;
	}
	
	// 戻り値をpopする
	__int32 retvalue;
	POP_INT(current_frame, retvalue);

	return retvalue;
}

/**
 * 戻り値が64ビット値のメソッドを呼び出す汎用関数
 */
static __int64 JNICALL jni_call_int64_method(JNIEnv *env, jobject obj, jmethodID methodID, native_argtype_t type, const void* args) {
	// メソッドを呼び出す
	jni_call_method_common(env, obj, methodID, type, args);

	// 例外発生
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (exception_occurred(current_frame)) {
		// 例外発生
		return 0;
	}
	
	// 戻り値をpopする
	__int64 retvalue;
	POP_LONG(current_frame, retvalue);

	return retvalue;
}

/**
 * CallObjectMethod本体
 */
jobject JNICALL jni_call_object_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jobject ret = jni_call_object_method_v(env, obj, methodID, args);
	va_end(args);

	return ret;
}

/**
 * CallObjectMethodV本体
 */
jobject JNICALL jni_call_object_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	// メソッドを呼び出す
	jni_call_method_common(env, obj, methodID, VA_LIST, args);
	
	// 例外発生
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (exception_occurred(current_frame)) {
		// 例外発生
		return 0;
	}

	// 戻り値はスタックトップにあるので、この値をローカル参照として登録した後、popする
	jobject value;
	get_stack_data(current_frame, 0, (__int32*) &value);
	value = new_local_reference(jenv->jni_frame, value);
	POP_DISCARD(current_frame);

	return value;
}

/**
 * CallObjectMethodA本体
 */
jobject JNICALL jni_call_object_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args) {
	// メソッドを呼び出す
	jni_call_method_common(env, obj, methodID, JVALUE, args);
	
	// 例外発生
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (exception_occurred(current_frame)) {
		// 例外発生
		return 0;
	}

	// 戻り値はスタックトップにあるので、この値をローカル参照として登録した後、popする
	jobject value;
	get_stack_data(current_frame, 0, (__int32*) &value);
	value = new_local_reference(jenv->jni_frame, value);
	POP_DISCARD(current_frame);

	return (jobject) value;

}

/**
 * CallBooleanMethod本体
 */
jboolean JNICALL jni_call_boolean_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jboolean ret = jni_call_boolean_method_v(env, obj, methodID, args);
	va_end(args);
	return ret;
}

/**
 * CallBooleanMethodV本体
 */
jboolean JNICALL jni_call_boolean_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	return (jboolean) jni_call_int32_method(env, obj, methodID, VA_LIST, args);
}

/**
 * CallBooleanMethodA本体
 */
jboolean JNICALL jni_call_boolean_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args) {
	return (jboolean) jni_call_int32_method(env, obj, methodID, JVALUE, args);
}

/**
 * CallByteMethod本体
 */
jbyte JNICALL jni_call_byte_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jbyte ret = jni_call_byte_method_v(env, obj, methodID, args);
	va_end(args);
	return ret;
}

/**
 * CallByteMethodV本体
 */
jbyte JNICALL jni_call_byte_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	return (jbyte) jni_call_int32_method(env, obj, methodID, VA_LIST, args);
}

/**
 * CallBooleanMethodA本体
 */
jbyte JNICALL jni_call_byte_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args) {
	return (jbyte) jni_call_int32_method(env, obj, methodID, JVALUE, args);
}

/**
 * CallCharMethod本体
 */
jchar JNICALL jni_call_char_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jchar ret = jni_call_char_method_v(env, obj, methodID, args);
	va_end(args);
	return ret;
}

/**
 * CallCharMethodV本体
 */
jchar JNICALL jni_call_char_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	return (jchar) jni_call_int32_method(env, obj, methodID, VA_LIST, args);
}

/**
 * CallCharMethodA本体
 */
jchar JNICALL jni_call_char_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args) {
	return (jchar) jni_call_int32_method(env, obj, methodID, JVALUE, args);
}

/**
 * CallShortMethod本体
 */
jshort JNICALL jni_call_short_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jshort ret = jni_call_short_method_v(env, obj, methodID, args);
	va_end(args);
	return ret;
}

/**
 * CallShortMethodV本体
 */
jshort JNICALL jni_call_short_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	return (jshort) jni_call_int32_method(env, obj, methodID, VA_LIST, args);
}

/**
 * CallShortMethodA本体
 */
jshort JNICALL jni_call_short_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args) {
	return (jshort) jni_call_int32_method(env, obj, methodID, JVALUE, args);
}

/**
 * CallIntMethod本体
 */
jint JNICALL jni_call_int_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jint ret = jni_call_int_method_v(env, obj, methodID, args);
	va_end(args);
	return ret;
}

/**
 * CallIntMethodV本体
 */
jint JNICALL jni_call_int_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	return (jint) jni_call_int32_method(env, obj, methodID, VA_LIST, args);
}

/**
 * CallIntMethodA本体
 */
jint JNICALL jni_call_int_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args) {
	return (jint) jni_call_int32_method(env, obj, methodID, JVALUE, args);
}

/**
 * CallLongMethod本体
 */
jlong JNICALL jni_call_long_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jlong ret = jni_call_long_method_v(env, obj, methodID, args);
	va_end(args);
	return ret;
}

/**
 * CallLongMethodV本体
 */
jlong JNICALL jni_call_long_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	return (jlong) jni_call_int64_method(env, obj, methodID, VA_LIST, args);
}

/**
 * CallLongMethodA本体
 */
jlong JNICALL jni_call_long_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args) {
	return (jlong) jni_call_int64_method(env, obj, methodID, JVALUE, (const void*) args);
}

/**
 * CallFloatMethod本体
 */
jfloat JNICALL jni_call_float_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jfloat ret = jni_call_float_method_v(env, obj, methodID, args);
	va_end(args);
	return ret;
}

/**
 * CallFloatMethodV本体
 */
jfloat JNICALL jni_call_float_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	__int32 tmp = jni_call_int32_method(env, obj, methodID, VA_LIST, args);
	return *(jfloat*) &tmp; 
}

/**
 * CallFloatMethodA本体
 */
jfloat JNICALL jni_call_float_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args) {
	__int32 tmp = jni_call_int32_method(env, obj, methodID, JVALUE, args);
	return *(jfloat*) &tmp; 
}

/**
 * CallDoubleMethod本体
 */
jdouble JNICALL jni_call_double_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jdouble ret = jni_call_double_method_v(env, obj, methodID, args);
	va_end(args);
	return ret;
}

/**
 * CallDoubleMethodV本体
 */
jdouble JNICALL jni_call_double_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	__int64 tmp = jni_call_int64_method(env, obj, methodID, VA_LIST, args);
	return *(jdouble*) &tmp; 
}

/**
 * CallDoubleMethodA本体
 */
jdouble JNICALL jni_call_double_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue* args) {
	__int64 tmp = jni_call_int64_method(env, obj, methodID, JVALUE, args);
	return *(jdouble*) &tmp; 
}

/**
 * CallVoidMethod本体
 */
void jni_call_void_method(JNIEnv *env, jobject obj, jmethodID methodID, ...) {
	va_list list;
	va_start(list, methodID);
	jni_call_void_method_v(env, obj, methodID, list);
	va_end(list);
}

void jni_call_void_method_v(JNIEnv *env, jobject obj, jmethodID methodID, va_list args) {
	// メソッドを呼び出す
	jni_call_method_common(env, obj, methodID, VA_LIST, args);
}

void jni_call_void_method_a(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args) {
	// メソッドを呼び出す
	jni_call_method_common(env, obj, methodID, JVALUE, args);
}

/**
 * CallNonvirtualVoidMethod本体
 */
void jni_call_nonvirtual_void_method(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...) {
	va_list list;
	va_start(list, methodID);
	jni_call_nonvirtual_void_method_v(env, obj, clazz, methodID, list);
	va_end(list);
}

void jni_call_nonvirtual_void_method_v(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args) {
	// メソッドを呼び出す
	jni_call_nonvirtual_method_common(env, obj, clazz, methodID, VA_LIST, args);
}

void jni_call_nonvirtual_void_method_a(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) {
	// メソッドを呼び出す
	jni_call_nonvirtual_method_common(env, obj, clazz, methodID, JVALUE, args);
}

/**
 * RegisterNatives本体
 */
jint jni_register_natives(JNIEnv* env, jclass clazz, const JNINativeMethod *methods, jint nMethods) {
	EnterCriticalSection(&g_global_critical_section);

	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	class_loader* loader = get_defining_loader(cfile);
	int retvalue = -1;
	for (int i = 0; i < nMethods; ++i) {
		const JNINativeMethod* method = &methods[i];
		native_method_info* info = find_native_method_info(loader, cfile->this_class_name, method);
		if (info == NULL) {
			if (! add_native_method_info(loader, cfile->this_class_name, method)) {
				retvalue = -1;
				break;
			}
		}
	}
	LeaveCriticalSection(&g_global_critical_section);
	return retvalue;
}

/**
 * GetFieldID本体
 */
jfieldID jni_get_field_id(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	jfieldID fid = get_field_id(cfile, intern_utf8(name), intern_utf8(sig));
	if (! fid) {
		// NoSuchFieldErrorをthrowする
		jclass nsfe = env->FindClass("java/lang/NoSuchFieldError");
		if (nsfe) {
			env->ThrowNew(nsfe, name);
		}
	}
	return fid;
}

/**
 * GetObjectField本体
 */
jobject JNICALL jni_get_object_field(JNIEnv *env, jobject obj, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, obj);
	jclass clazz = env->GetObjectClass(obj);
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	if (get_field(current_frame, cfile, fieldID)) {
		jobject tmp;
		// ローカル参照として登録する
		get_stack_data(current_frame, 0, (__int32*)&tmp);
		jobject result = new_local_reference(((jni_env*) env)->jni_frame, tmp);
		POP_DISCARD(current_frame);
		return result;
	}
	return JNI_FALSE;
}

/**
 * GetBooleanField本体
 */
jboolean JNICALL jni_get_boolean_field(JNIEnv *env, jobject obj, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, obj);
	jclass clazz = env->GetObjectClass(obj);
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	if (get_field(current_frame, cfile, fieldID)) {
		__int32 result;
		POP_INT(current_frame, result);
		return (jboolean) result;
	}
	return JNI_FALSE;
}

/**
 * GetByteField本体
 */
jbyte JNICALL jni_get_byte_field(JNIEnv *env, jobject obj, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, obj);
	jclass clazz = env->GetObjectClass(obj);
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	if (get_field(current_frame, cfile, fieldID)) {
		__int32 result;
		POP_INT(current_frame, result);
		return (jbyte) result;
	}
	return 0;
}

/**
 * GetShortField本体
 */
jshort JNICALL jni_get_short_field(JNIEnv *env, jobject obj, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, obj);
	jclass clazz = env->GetObjectClass(obj);
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	if (get_field(current_frame, cfile, fieldID)) {
		__int32 result;
		POP_INT(current_frame, result);
		return (jshort) result;
	}
	return 0;
}

/**
 * GetCharField本体
 */
jchar JNICALL jni_get_char_field(JNIEnv *env, jobject obj, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, obj);
	jclass clazz = env->GetObjectClass(obj);
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	if (get_field(current_frame, cfile, fieldID)) {
		__int32 result;
		POP_INT(current_frame, result);
		return (jchar) result;
	}
	return 0;
}

/**
 * GetIntField本体
 */
jint JNICALL jni_get_int_field(JNIEnv *env, jobject obj, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, obj);
	jclass clazz = env->GetObjectClass(obj);
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	if (get_field(current_frame, cfile, fieldID)) {
		__int32 result;
		POP_INT(current_frame, result);
		return (jint) result;
	}
	return 0;
}

/**
 * GetLongField本体
 */
jlong JNICALL jni_get_long_field(JNIEnv *env, jobject obj, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, obj);
	jclass clazz = env->GetObjectClass(obj);
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	if (get_field(current_frame, cfile, fieldID)) {
		__int64 result;
		POP_LONG(current_frame, result);
		return (jlong) result;
	}
	return 0;
}

/**
 * GetFloatField本体
 */
jfloat JNICALL jni_get_float_field(JNIEnv *env, jobject obj, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, obj);
	jclass clazz = env->GetObjectClass(obj);
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	if (get_field(current_frame, cfile, fieldID)) {
		float result;
		POP_FLOAT(current_frame, result);
		return (jfloat) result;
	}
	return 0;
}

/**
 * GetDoubleField本体
 */
jdouble JNICALL jni_get_double_field(JNIEnv *env, jobject obj, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, obj);
	jclass clazz = env->GetObjectClass(obj);
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	if (get_field(current_frame, cfile,  fieldID)) {
		double result;
		POP_DOUBLE(current_frame, result);
		return (jdouble) result;
	}
	return 0;
}



/**
 * Set<type>Field本体
 */
void JNICALL jni_set_object_field(JNIEnv *env, jobject obj, jfieldID fieldID, jobject val) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	// ToDo: 代入可能か調べる

	PUSH_OBJECT(get_current_frame(jenv->jni_frame), obj);
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), val);

	put_field(current_frame, get_ClassFile(obj), fieldID);

}

void JNICALL jni_set_boolean_field(JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), obj);
	PUSH_INT(get_current_frame(jenv->jni_frame), val);

	put_field(current_frame, get_ClassFile(obj), fieldID);
}

void JNICALL jni_set_byte_field(JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), obj);
	PUSH_INT(get_current_frame(jenv->jni_frame), val);

	put_field(current_frame, get_ClassFile(obj), fieldID);
}

void JNICALL jni_set_short_field(JNIEnv *env, jobject obj, jfieldID fieldID, jshort val) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), obj);
	PUSH_INT(get_current_frame(jenv->jni_frame), val);

	put_field(current_frame, get_ClassFile(obj), fieldID);
}

void JNICALL jni_set_char_field(JNIEnv *env, jobject obj, jfieldID fieldID, jchar val) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), obj);
	PUSH_INT(get_current_frame(jenv->jni_frame), val);

	put_field(current_frame, get_ClassFile(obj), fieldID);
}

void JNICALL jni_set_int_field(JNIEnv *env, jobject obj, jfieldID fieldID, jint val) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), obj);
	PUSH_INT(get_current_frame(jenv->jni_frame), val);

	put_field(current_frame, get_ClassFile(obj), fieldID);
}

void JNICALL jni_set_long_field(JNIEnv *env, jobject obj, jfieldID fieldID, jlong val) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), obj);
	PUSH_LONG(get_current_frame(jenv->jni_frame), val);

	put_field(current_frame, get_ClassFile(obj), fieldID);
}

void JNICALL jni_set_float_field(JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), obj);
	PUSH_FLOAT(get_current_frame(jenv->jni_frame), val);

	put_field(current_frame, get_ClassFile(obj), fieldID);
}

void JNICALL jni_set_double_field(JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), obj);
	PUSH_DOUBLE(get_current_frame(jenv->jni_frame), val);

	put_field(current_frame, get_ClassFile(obj), fieldID);
}

/**
 * GetStaticMethodID本体
 */
jmethodID JNICALL jni_get_static_method_id(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);
	
	jmethodID mid = get_method_id(cfile, intern_utf8(name), intern_utf8(sig));

	if (mid) {
		// 本当にstaticメソッドかを調べる
		method_info* minfo = get_method_info(cfile, mid);
		if (! is_static(minfo->access_flags)) {
			mid = NULL;
		}
	}

	if (! mid) {
		// NoSuchMethodErrorをthrowする
		jclass nsme = env->FindClass("java/lang/NoSuchMethodError");
		if (nsme) {
			env->ThrowNew(nsme, name);
		}
	}

	return mid;
}

/**
 * staticメソッド呼び出し汎用関数
 */
static void jni_call_static_method_common(JNIEnv *env, jclass clazz, jmethodID methodID, native_argtype_t args_type, const void* args) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	// method_infoを取得する
	jobject static_data = get_static_data(env, clazz);
	ClassFile* cfile = get_ClassFile(static_data);
	method_info* minfo = get_method_info(cfile, methodID);
	ClassFile* declaring = minfo->declaring_ClassFile;
	const java_utf8* desc = minfo->descriptor;

	// パラメータをpushする
	if (args_type == VA_LIST) {
		push_parameters(desc, current_frame, (va_list) args);
	} else {
		push_parameters(desc, current_frame, (jvalue*) args);
	}

	// メソッドを呼び出す
	invoke_method(current_frame, static_data, declaring, minfo);
}

/**
 * 戻り値が32ビット値のstaticメソッドを呼び出す汎用関数
 */
static __int32 JNICALL jni_call_static_int32_method(JNIEnv *env, jclass clazz, jmethodID methodID, native_argtype_t type, const void* args) {
	// メソッドを呼び出す
	jni_call_static_method_common(env, clazz, methodID, type, args);

	// 例外発生
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (exception_occurred(current_frame)) {
		// 例外発生
		return 0;
	}
	
	// 戻り値をpopする
	__int32 retvalue;
	POP_INT(current_frame, retvalue);

	return retvalue;
}

/**
 * 戻り値が64ビット値のstaticメソッドを呼び出す汎用関数
 */
static __int64 JNICALL jni_call_static_int64_method(JNIEnv *env, jclass clazz, jmethodID methodID, native_argtype_t type, const void* args) {
	// メソッドを呼び出す
	jni_call_static_method_common(env, clazz, methodID, type, args);

	// 例外発生
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (exception_occurred(current_frame)) {
		// 例外発生
		return 0;
	}
	
	// 戻り値をpopする
	__int64 retvalue;
	POP_LONG(current_frame, retvalue);

	return retvalue;
}

/**
 * CallStaticObjectMethod本体
 */
jobject JNICALL jni_call_static_object_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jobject ret = jni_call_static_object_method_v(env, clazz, methodID, args);
	va_end(args);

	return ret;
}

jobject JNICALL jni_call_static_object_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	// メソッドを呼び出す
	jni_call_static_method_common(env, clazz, methodID, VA_LIST, args);
	
	// 例外発生
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (exception_occurred(current_frame)) {
		// 例外発生
		return 0;
	}

	// 戻り値はスタックトップにあるので、この値をローカル参照として登録した後、popする
	jobject value;
	get_stack_data(current_frame, 0, (__int32*) &value);
	value = new_local_reference(jenv->jni_frame, value);
	POP_DISCARD(current_frame);

	return value;
}

jobject JNICALL jni_call_static_object_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue * args) {
	// メソッドを呼び出す
	jni_call_static_method_common(env, clazz, methodID, JVALUE, (const void*) args);
	
	// 例外発生
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	if (exception_occurred(current_frame)) {
		// 例外発生
		return 0;
	}

	// 戻り値はスタックトップにあるので、この値をローカル参照として登録した後、popする
	jobject value;
	get_stack_data(current_frame, 0, (__int32*) &value);
	value = new_local_reference(jenv->jni_frame, value);
	POP_DISCARD(current_frame);

	return value;

}

/**
 * CallStaticBooleanMethod本体
 */
jboolean JNICALL jni_call_static_boolean_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jboolean ret = jni_call_static_boolean_method_v(env, clazz, methodID, args);
	va_end(args);
	return ret;
}

jboolean JNICALL jni_call_static_boolean_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	return (jboolean) jni_call_static_int32_method(env, clazz, methodID, VA_LIST, args);
}

jboolean JNICALL jni_call_static_boolean_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue* args) {
	return (jboolean) jni_call_static_int32_method(env, clazz, methodID, JVALUE, args);
}

/**
 * CallStaticByteMethod本体
 */
jbyte JNICALL jni_call_static_byte_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jbyte ret = jni_call_static_byte_method_v(env, clazz, methodID, args);
	va_end(args);
	return ret;
}

jbyte JNICALL jni_call_static_byte_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	return (jbyte) jni_call_static_int32_method(env, clazz, methodID, VA_LIST, args);
}

jbyte JNICALL jni_call_static_byte_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue* args) {
	return (jbyte) jni_call_static_int32_method(env, clazz, methodID, JVALUE, args);
}

/**
 * CallStaticCharMethod本体
 */
jchar JNICALL jni_call_static_char_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jchar ret = jni_call_static_char_method_v(env, clazz, methodID, args);
	va_end(args);
	return ret;
}

jchar JNICALL jni_call_static_char_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	return (jchar) jni_call_static_int32_method(env, clazz, methodID, VA_LIST, args);
}

jchar JNICALL jni_call_static_char_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue* args) {
	return (jchar) jni_call_static_int32_method(env, clazz, methodID, JVALUE, args);
}

/**
 * CallStaticShortMethod本体
 */
jshort JNICALL jni_call_static_short_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jshort ret = jni_call_static_short_method_v(env, clazz, methodID, args);
	va_end(args);
	return ret;
}

jshort JNICALL jni_call_static_short_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	return (jshort) jni_call_static_int32_method(env, clazz, methodID, VA_LIST, args);
}

jshort JNICALL jni_call_static_short_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue* args) {
	return (jshort) jni_call_static_int32_method(env, clazz, methodID, JVALUE, args);
}

/**
 * CallStaticIntMethod本体
 */
jint JNICALL jni_call_static_int_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jint ret = jni_call_static_int_method_v(env, clazz, methodID, args);
	va_end(args);
	return ret;
}

jint JNICALL jni_call_static_int_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	return (jint) jni_call_static_int32_method(env, clazz, methodID, VA_LIST, args);
}

jint JNICALL jni_call_static_int_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue* args) {
	return (jint) jni_call_static_int32_method(env, clazz, methodID, JVALUE, args);
}

/**
 * CallStaticLongMethod本体
 */
jlong JNICALL jni_call_static_long_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jlong ret = jni_call_static_long_method_v(env, clazz, methodID, args);
	va_end(args);
	return ret;
}

jlong JNICALL jni_call_static_long_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	return (jlong) jni_call_static_int64_method(env, clazz, methodID, VA_LIST, args);
}

jlong JNICALL jni_call_static_long_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue* args) {
	return (jlong) jni_call_static_int64_method(env, clazz, methodID, JVALUE, (const void*) args);
}

/**
 * CallStaticFloatMethod本体
 */
jfloat JNICALL jni_call_static_float_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jfloat ret = jni_call_static_float_method_v(env, clazz, methodID, args);
	va_end(args);
	return ret;
}

jfloat JNICALL jni_call_static_float_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	__int32 tmp = jni_call_static_int32_method(env, clazz, methodID, VA_LIST, args);
	return *(jfloat*) &tmp; 
}

jfloat JNICALL jni_call_static_float_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue* args) {
	__int32 tmp = jni_call_static_int32_method(env, clazz, methodID, JVALUE, args);
	return *(jfloat*) &tmp; 
}

/**
 * CallStaticDoubleMethod本体
 */
jdouble JNICALL jni_call_static_double_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list args;
	va_start(args, methodID);
	jdouble ret = jni_call_static_double_method_v(env, clazz, methodID, args);
	va_end(args);
	return ret;
}

jdouble JNICALL jni_call_static_double_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	__int64 tmp = jni_call_static_int64_method(env, clazz, methodID, VA_LIST, args);
	return *(jdouble*) &tmp; 
}

jdouble JNICALL jni_call_static_double_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue* args) {
	__int64 tmp = jni_call_static_int64_method(env, clazz, methodID, JVALUE, args);
	return *(jdouble*) &tmp; 
}

/**
 * CallStaticVoidMethod本体
 */
void jni_call_static_void_method(JNIEnv *env, jclass clazz, jmethodID methodID, ...) {
	va_list list;
	va_start(list, methodID);
	jni_call_static_void_method_v(env, clazz, methodID, list);
	va_end(list);
}

void jni_call_static_void_method_v(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args) {
	// メソッドを呼び出す
	jni_call_static_method_common(env, clazz, methodID, VA_LIST, args);
}

void jni_call_static_void_method_a(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue * args) {
	// メソッドを呼び出す
	jni_call_static_method_common(env, clazz, methodID, JVALUE, args);
}

/**
 * GetStaticFieldID本体
 */
jfieldID JNICALL jni_get_static_field_id(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
	ClassFile* cfile = get_ClassFile(get_static_data(env, clazz));
	jfieldID fid = get_static_field_id(cfile, intern_utf8(name), intern_utf8(sig));
	if (! fid) {
		// NoSuchFieldErrorをthrowする
		jclass nsfe = env->FindClass("java/lang/NoSuchFieldError");
		if (nsfe) {
			env->ThrowNew(nsfe, name);
		}
	}
	return fid;
}

/**
 * GetStaticBooleanField本体
 */
jboolean jni_get_static_boolean_field(JNIEnv *env, jclass clazz, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	get_static_field(current_frame, get_static_data(env, clazz), fieldID);
	jint result;
	POP_INT(current_frame, result);
	return (jboolean) result;
}

/**
 * GetStaticByteField本体
 */
jbyte jni_get_static_byte_field(JNIEnv *env, jclass clazz, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	get_static_field(current_frame, get_static_data(env, clazz), fieldID);
	jint result;
	POP_INT(current_frame, result);
	return (jbyte) result;
}

/**
 * GetStaticCharField本体
 */
jchar jni_get_static_char_field(JNIEnv *env, jclass clazz, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	get_static_field(current_frame, get_static_data(env, clazz), fieldID);
	jint result;
	POP_INT(current_frame, result);
	return (jchar) result;
}

/**
 * GetStaticShortField本体
 */
jshort jni_get_static_short_field(JNIEnv *env, jclass clazz, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	get_static_field(current_frame, get_static_data(env, clazz), fieldID);
	jint result;
	POP_INT(current_frame, result);
	return (jshort) result;
}

/**
 * GetStaticIntField本体
 */
jint jni_get_static_int_field(JNIEnv *env, jclass clazz, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	get_static_field(current_frame, get_static_data(env, clazz), fieldID);
	jint result;
	POP_INT(current_frame, result);
	return result;
}

/**
 * GetStaticLongField本体
 */
jlong jni_get_static_long_field(JNIEnv *env, jclass clazz, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	get_static_field(current_frame, get_static_data(env, clazz), fieldID);
	jlong result;
	POP_LONG(current_frame, result);
	return result;
}

/**
 * GetStaticFloatField本体
 */
jfloat jni_get_static_float_field(JNIEnv *env, jclass clazz, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	get_static_field(current_frame, get_static_data(env, clazz), fieldID);
	jfloat result;
	POP_FLOAT(current_frame, result);
	return result;
}

/**
 * GetStaticDoubleField本体
 */
jdouble jni_get_static_double_field(JNIEnv *env, jclass clazz, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	get_static_field(current_frame, get_static_data(env, clazz), fieldID);
	jdouble result;
	POP_DOUBLE(current_frame, result);
	return result;
}

/**
 * GetStaticObjectField本体
 */
jobject jni_get_static_object_field(JNIEnv *env, jclass clazz, jfieldID fieldID) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	get_static_field(current_frame, get_static_data(env, clazz), fieldID);
	jobject result;
	POP_OBJECT(current_frame, result);
	return result;
}

/**
 * SetStaticBooleanField本体
 */
void jni_set_static_boolean_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_INT(current_frame, value);
	put_static_field(current_frame, get_static_data(env, clazz), fieldID);
}

/**
 * SetStaticByteField本体
 */
void jni_set_static_byte_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_INT(current_frame, value);
	put_static_field(current_frame, get_static_data(env, clazz), fieldID);
}

/**
 * SetStaticCharField本体
 */
void jni_set_static_char_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_INT(current_frame, value);
	put_static_field(current_frame, get_static_data(env, clazz), fieldID);
}

/**
 * SetStaticShortField本体
 */
void jni_set_static_short_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_INT(current_frame, value);
	put_static_field(current_frame, get_static_data(env, clazz), fieldID);
}

/**
 * SetStaticIntField本体
 */
void jni_set_static_int_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jint value) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_INT(current_frame, value);
	put_static_field(current_frame, get_static_data(env, clazz), fieldID);
}

/**
 * SetStaticLongField本体
 */
void jni_set_static_long_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_LONG(current_frame, value);
	put_static_field(current_frame, get_static_data(env, clazz), fieldID);
}

/**
 * SetStaticFloatField本体
 */
void jni_set_static_float_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_FLOAT(current_frame, value);
	put_static_field(current_frame, get_static_data(env, clazz), fieldID);
}

/**
 * SetStaticIntField本体
 */
void jni_set_static_double_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_DOUBLE(current_frame, value);
	put_static_field(current_frame, get_static_data(env, clazz), fieldID);
}

/**
 * SetStaticObjectField本体
 */
void jni_set_static_object_field(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value) {
	frame* current_frame = get_current_frame(((jni_env*) env)->jni_frame);
	PUSH_OBJECT(current_frame, value);
	put_static_field(current_frame, get_static_data(env, clazz), fieldID);
}

/**
 * GetArrayLength本体
 */
jsize jni_get_array_length(JNIEnv *env, jarray array) {
	if (array == NULL) {
		// NullPointerExceptionをthrowする
		jclass npe_class = env->FindClass("java/lang/NullPointerException");
		if (npe_class != NULL) {
			env->ThrowNew(npe_class, NULL);
		}
		return 0;
	}
	__int32 length = get_array_length(array);

	return (jsize) length;
}

/**
 * NewObjectArray本体
 */
jobjectArray jni_new_object_array(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement) {
	jni_env* jenv = (jni_env*) env;
	// 配列クラス名を作成する
	ClassFile* cfile = get_ClassFile(get_static_data(env, elementClass));
	char* array_class_name = (char*) malloc(strlen(cfile->this_class_name) + 3 + 1);
	if (array_class_name == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	if (cfile->this_class_name[0] != '[') {
		sprintf(array_class_name, "[L%s;", cfile->this_class_name);
	} else {
		sprintf(array_class_name, "[%s", cfile->this_class_name);
	}
#if 0
	char* tmp = array_class_name;
	*tmp++ = '[';
	if (cfile->this_class_name[0] != '[') {
		*tmp++ = 'L';
	}
	strcpy(tmp, cfile->this_class_name);
	if (cfile->this_class_name[0] != '[') {
		strcat(array_class_name, ";");
	}
#endif

	ClassFile* array_cfile = find_ClassFile(get_current_frame(jenv->jni_frame),
											get_defining_loader(cfile),
											intern_utf8(array_class_name));
	free(array_class_name);

	jarray array = alloc_array(array_cfile, length, get_current_frame(jenv->jni_frame));
	if (array == NULL) {
		return NULL;
	}
	
	// 配列の要素を設定する
	if (initialElement != NULL) {
		for (jsize i = 0; i < length; ++i) {
			store_object_array(array, (__int32) i, initialElement);
		}
	}
	
	// ローカル参照として登録する
	jobject result = new_local_reference(jenv->jni_frame, array);

	// 参照をpopする
	POP_DISCARD(get_current_frame(jenv->jni_frame));

	return (jobjectArray) result;

}

/**
 * SetObjectArray本体
 */
jobject JNICALL jni_get_object_array_element(JNIEnv *env, jobjectArray array, jsize index) {
	jobject ref;
	if (! load_object_array(array, (__int32) index, &ref)) {
		jclass aioobe_class = env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
		if (aioobe_class != NULL) {
			env->ThrowNew(aioobe_class, NULL);
		}
	}
	return ref;
}

/**
 * SetObjectArrayElement本体
 */
void JNICALL jni_set_object_array_element(JNIEnv *env, jobjectArray array, jsize index, jobject val) {
	if (! store_object_array(array, (__int32) index, val)) {
		jclass aioobe_class = env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
		if (aioobe_class != NULL) {
			env->ThrowNew(aioobe_class, NULL);
		}
	}
}

/**
 * New<PrimitiveType>Array本体
 */
#define NEW_ARRAY(ClassName, PrimitiveType, ArrayType) \
ArrayType JNICALL jni_new_##PrimitiveType##_array(JNIEnv *env, jsize length) { \
	jni_env* jenv = (jni_env*) env; \
	ClassFile* cfile = find_ClassFile(get_current_frame(jenv->jni_frame), \
									  ##ClassName##_ARRAY_CLASS_NAME); \
	return (ArrayType) alloc_array(cfile, length, get_current_frame(jenv->jni_frame)); \
}

NEW_ARRAY(BOOLEAN, boolean, jbooleanArray)
NEW_ARRAY(BYTE,    byte,    jbyteArray)
NEW_ARRAY(CHAR,    char,    jcharArray)
NEW_ARRAY(SHORT,   short,   jshortArray)
NEW_ARRAY(INT,     int,     jintArray)
NEW_ARRAY(LONG,    long,    jlongArray)
NEW_ARRAY(FLOAT,   float,   jfloatArray)
NEW_ARRAY(DOUBLE,  double,  jdoubleArray)

/**
 * Get<PrimitiveType>ArrayElements本体
 */
#define GET_ARRAY_ELEMENTS(ClassName, PrimitiveType, ArrayType, NativeType) \
NativeType * JNICALL jni_get_##PrimitiveType##_array_elements(JNIEnv *env, ArrayType array, jboolean* isCopy) { \
	/* 型チェックを行う */ \
	ClassFile* cfile = get_ClassFile(array); \
	if (cfile->this_class_name != ##ClassName##_ARRAY_CLASS_NAME) { \
		jclass clazz = env->FindClass("java/lang/IllegalArgumentException"); \
		if (clazz != NULL) { \
			env->ThrowNew(clazz, "array type mismatch"); \
		} \
		return NULL; \
	} \
	if (isCopy != NULL) { \
		*isCopy = JNI_FALSE; \
	} \
	return (NativeType *) pin_object(array); \
}

GET_ARRAY_ELEMENTS(BOOLEAN, boolean, jbooleanArray, jboolean)
GET_ARRAY_ELEMENTS(BYTE,    byte,    jbyteArray,    jbyte)
GET_ARRAY_ELEMENTS(SHORT,   short,   jshortArray,   jshort)
GET_ARRAY_ELEMENTS(CHAR,    char,    jcharArray,    jchar)
GET_ARRAY_ELEMENTS(INT,     int,     jintArray,     jint)
GET_ARRAY_ELEMENTS(LONG,    long,    jlongArray,    jlong)
GET_ARRAY_ELEMENTS(FLOAT,   float,   jfloatArray,   jfloat)
GET_ARRAY_ELEMENTS(DOUBLE,  double,  jdoubleArray,  jdouble)

/**
 * Release<PrimitiveType>ArrayElements本体
 */
#define RELEASE_ARRAY_ELEMENTS(PrimitiveType, ArrayType, NativeType) \
void JNICALL jni_release_##PrimitiveType##_array_elements(JNIEnv *env, ArrayType array, NativeType *elems, jint mode) {\
	unpin_object(elems); \
}

RELEASE_ARRAY_ELEMENTS(boolean, jbooleanArray, jboolean)
RELEASE_ARRAY_ELEMENTS(byte,    jbyteArray,    jbyte)
RELEASE_ARRAY_ELEMENTS(short,   jshortArray,   jshort)
RELEASE_ARRAY_ELEMENTS(char,    jcharArray,    jchar)
RELEASE_ARRAY_ELEMENTS(int,     jintArray,     jint)
RELEASE_ARRAY_ELEMENTS(long,    jlongArray,    jlong)
RELEASE_ARRAY_ELEMENTS(float,   jfloatArray,   jfloat)
RELEASE_ARRAY_ELEMENTS(double,  jdoubleArray,  jdouble)

/**
 * Get<PrimitiveType>ArrayRegion 本体
 */
#define GET_ARRAY_REGION(PrimitiveType, ArrayType, NativeType) \
void JNICALL jni_get_##PrimitiveType##_array_region(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf) { \
	/* 配列の境界を越えていないか調べる */ \
	const jsize arraylength = env->GetArrayLength(array); \
	if (start < 0 || len < 0 || (start + len) > arraylength) { \
		jclass clazz = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); \
		env->ThrowNew(clazz, NULL); \
	} \
	/* データ領域のポインタを得る */ \
	NativeType* data = (NativeType *) pin_object(array); \
	/* データをコピーする */ \
	memcpy(buf, data + start, sizeof(NativeType) * len); \
	unpin_object(data); \
} 

GET_ARRAY_REGION(boolean, jbooleanArray, jboolean)
GET_ARRAY_REGION(byte,    jbyteArray,    jbyte)
GET_ARRAY_REGION(char,    jcharArray,    jchar)
GET_ARRAY_REGION(short,   jshortArray,   jshort)
GET_ARRAY_REGION(int,     jintArray,     jint)
GET_ARRAY_REGION(long,    jlongArray,    jlong)
GET_ARRAY_REGION(float,   jfloatArray,   jfloat)
GET_ARRAY_REGION(double,  jdoubleArray,  jdouble)
 
/**
 * Set<PrimitiveType>ArrayRegion 本体
 */
#define SET_ARRAY_REGION(PrimitiveType, ArrayType, NativeType) \
void JNICALL jni_set_##PrimitiveType##_array_region(JNIEnv *env, ArrayType array, jsize start, jsize len, const NativeType *buf) { \
	/* 配列の境界を越えていないか調べる */ \
	const jsize arraylength = env->GetArrayLength(array); \
	if (start < 0 || len < 0 || (start + len) > arraylength) { \
		jclass clazz = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); \
		env->ThrowNew(clazz, NULL); \
	} \
	/* データ領域のポインタを得る */ \
	NativeType* data = (NativeType *) pin_object(array); \
	/* データをコピーする */ \
	memcpy(data + start, buf, sizeof(NativeType) * len); \
	unpin_object(data); \
} 

SET_ARRAY_REGION(boolean, jbooleanArray, jboolean)
SET_ARRAY_REGION(byte,    jbyteArray,    jbyte)
SET_ARRAY_REGION(char,    jcharArray,    jchar)
SET_ARRAY_REGION(short,   jshortArray,   jshort)
SET_ARRAY_REGION(int,     jintArray,     jint)
SET_ARRAY_REGION(long,    jlongArray,    jlong)
SET_ARRAY_REGION(float,   jfloatArray,   jfloat)
SET_ARRAY_REGION(double,  jdoubleArray,  jdouble)

/**
 * GetPrimitiveArrayCritical()本体
 */
void * JNICALL jni_get_primitive_array_critical(JNIEnv *env, jarray array, jboolean *isCopy) {
	if (isCopy != NULL) {
		*isCopy = JNI_FALSE;
	}
	return pin_object(array);
}

/**
 * ReleasePrimitiveArrayCritical()本体
 */
void JNICALL jni_release_primitive_array_critical(JNIEnv *env, jarray array, void* carray, jint mode) {
	unpin_object(carray);
}

/**
 * NewString本体
 */
jstring JNICALL jni_new_string(JNIEnv *env, const jchar *unicode, jsize len) {
	static const java_utf8* constructor_descriptor = intern_utf8("([C)V");
	static method_info* minfo;

	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);

	// Stringオブジェクトを作成し、スタックに積む
	ClassFile* string_class_file = find_ClassFile(current_frame, STRING_CLASS_NAME);
	if (! string_class_file) {
		return NULL;
	}

	// メモリを割り当てる
	jobject string_reference = alloc_object(string_class_file, current_frame);
	if (string_reference == NULL) {
		return NULL;
	}
	
	// コンストラクタに渡す char[] を作成する
	ClassFile* char_array_class_file = find_ClassFile(current_frame, CHAR_ARRAY_CLASS_NAME);
	if (! char_array_class_file) {
		return NULL;
	}
	jobject char_array = alloc_char_array(char_array_class_file, (_TCHAR*) unicode, len, current_frame);
	if (char_array == NULL) {
		return NULL;
	}
	
	// ローカル参照を登録する
	string_reference = new_local_reference(jenv->jni_frame, string_reference);

	// コンストラクタを呼び出す
	if (! minfo) {
		jmethodID mid = get_method_id(string_class_file,
									  INIT_METHOD_NAME,
									  constructor_descriptor);
		minfo = get_method_info(string_class_file, mid);
		if (! minfo) {
			fatal_error(_T("Failed to get constructor of String class"));
		}
	}
	invoke_method(current_frame, string_reference, string_class_file, minfo);

	return (jstring) string_reference;
}

/**
 * GetStringLength本体
 */
jsize JNICALL jni_get_string_length(JNIEnv *env, jstring str) {
	static const java_utf8* length_method_name = intern_utf8("length");
	static const java_utf8* length_method_desc = intern_utf8("()I");
	static method_info* minfo;
	if (str == NULL) {
		jclass npe = env->FindClass("java/lang/NullPointerException");
		if (npe) {
			env->ThrowNew(npe, "str");
		}
		return 0;
	}

	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	
	jobject string_reference = str;
	ClassFile* string_class_file = get_ClassFile(string_reference);
	if (! minfo) {
		jmethodID mid = get_method_id(string_class_file,
							  length_method_name,
							  length_method_desc);
		if (! mid) {
			fatal_error(_T("Failed to get method id of String.length()"));
		}
		minfo = get_method_info(string_class_file, mid);
		if (! minfo) {
			fatal_error(_T("Failed to get method_info of String.length()"));
		}
	}
	
	// length() メソッドを呼び出す
	PUSH_OBJECT(current_frame, string_reference);
	invoke_method(current_frame, string_reference, string_class_file, minfo);
	
	if (exception_occurred(current_frame)) {
		return 0;
	}

	// オペランドスタックから length() メソッドの戻り値をpopする
	__int32 length;
	POP_INT(current_frame, length);
	return length;
}

/**
 * GetStringChars本体
 */
const jchar* JNICALL jni_get_string_chars(JNIEnv *env, jstring str, jboolean* isCopy) {
	static const java_utf8* offset_field_name = intern_utf8("offset");
	static const java_utf8* offset_field_desc = intern_utf8("I");
	static const java_utf8* value_field_name = intern_utf8("value");
	static const java_utf8* value_field_desc = intern_utf8("[C");
	static jfieldID value_fid;
	static jfieldID offset_fid;

	if (! str) {
		jclass clazz = env->FindClass("java/lang/NullPointerException");
		if (clazz) {
			env->ThrowNew(clazz, NULL);
		}
		return NULL;
	}
	jni_env* jenv = (jni_env*) env;
	jobject string_reference = str;

	ClassFile* cfile = get_ClassFile(string_reference);

	// Stringのプライベートフィールド char[] value を取得する
	if (! value_fid) {
		value_fid = get_field_id(cfile, value_field_name, value_field_desc);
	}
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), (jobject) str);
	get_field(get_current_frame(jenv->jni_frame), cfile, value_fid);
	jobject value_reference;
	POP_OBJECT(get_current_frame(jenv->jni_frame), value_reference);

	// Stringのプライベートフィールド offset を取得する
	if (! offset_fid) {
		offset_fid = get_field_id(cfile, offset_field_name, offset_field_desc);
	}
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), (jobject) str);
	get_field(get_current_frame(jenv->jni_frame), cfile, offset_fid);
	jint offset;
	POP_INT(get_current_frame(jenv->jni_frame), offset);
	
	if (isCopy != NULL) {
		*isCopy = JNI_FALSE;
	}
	// valueのデータ本体へのポインタを返す
	return (jchar*) pin_object(value_reference) + offset;
}

/**
 * ReleaseStringChars本体
 */
void jni_release_string_chars(JNIEnv *env, jstring str, const jchar *chars) {
	static const java_utf8* offset_field_name = intern_utf8("offset");
	static const java_utf8* offset_field_desc = intern_utf8("I");

	if (! str) {
		jclass clazz = env->FindClass("java/lang/NullPointerException");
		if (clazz) {
			env->ThrowNew(clazz, NULL);
		}
		return;
	}
	// 実質的に現在の実装では何もしていないので、以下の部分はコメントアウトしている
#if 0
	jni_env* jenv = (jni_env*) env;
	ClassFile* cfile = get_ClassFile(str);

	// Stringのプライベートフィールド offset を取得する
	jfieldID fid = get_field_id(cfile, offset_field_name, offset_field_desc);
	PUSH_OBJECT(get_current_frame(jenv->jni_frame), (jobject) str);
	get_field(get_current_frame(jenv->jni_frame), cfile, fid);
	jint offset;
	POP_INT(get_current_frame(jenv->jni_frame), offset);

	// ピンをはずす
	chars -= offset;
	unpin_object((void*) chars);
#endif
}

/**
 * NewStringUTF本体
 */
jstring jni_new_string_utf(JNIEnv *env, const char *bytes) {
	int len = strlen(bytes) + 1;
	int bufflen = len * 2;
	_TCHAR* buff = (_TCHAR*) malloc(sizeof(_TCHAR) * bufflen);
	if (buff == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	convert_to_TCHAR(bytes, buff, bufflen);
	jstring result = jni_new_string(env, (const jchar*) buff, _tcslen(buff));
	free(buff);
	return result;
}

/**
 * GetStringUTFLength本体
 */
jsize JNICALL jni_get_string_utf_length(JNIEnv *env, jstring str) {
	if (! str) {
		jclass clazz = env->FindClass("java/lang/NullPointerException");
		if (clazz) {
			env->ThrowNew(clazz, NULL);
		}
		return NULL;
	}
	const jchar* tmp = env->GetStringChars(str, JNI_FALSE);
	jsize strlen = env->GetStringLength(str);
	jsize result = get_utf8_length_of((const _TCHAR*) tmp, strlen);
	env->ReleaseStringChars(str, tmp);
	return result;
}

/**
 * GetStringUTFChars本体
 */
const char* JNICALL jni_get_string_utf_chars(JNIEnv *env, jstring str, jboolean* isCopy) {
	if (! str) {
		jclass clazz = env->FindClass("java/lang/NullPointerException");
		if (clazz) {
			env->ThrowNew(clazz, NULL);
		}
		return NULL;
	}
	// Unicodeバッファに格納する
	const jchar* tmp = env->GetStringChars(str, NULL);
	if (env->ExceptionCheck()) {
		return 0;
	}
	jsize len = env->GetStringLength(str);
	if (env->ExceptionCheck()) {
		return 0;
	}
	// UTF-8文字列に変換する
	char* utf8 = (char*) malloc(len * 3 + 1);	// 最大で3倍の長さになる
	if (utf8 == NULL) {
		fatal_error(FATAL_ERROR_NO_MEMORY);
	}
	convert_to_utf8((const _TCHAR*) tmp, len, utf8, len * 3 + 1);
	if (isCopy != NULL) {
		// 常にコピーを返す
		*isCopy = JNI_TRUE;
	}
#ifdef DEBUG
	{
		// もう一度元に戻せるか？
		_TCHAR* test = (_TCHAR*) malloc(strlen(utf8) * 2 + 1);
		convert_to_TCHAR(utf8, test, strlen(utf8) * 2 + 1);
		if (_tcsncmp((const _TCHAR*) tmp, test, len) || len != _tcslen(test)) {
			fatal_error(_T("jni_get_utf_chars() error:"));
		}
		int result = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, test, strlen(utf8) * 2 + 1);
		if (!result || (_tcsncmp((const _TCHAR*) tmp, test, len) || len != _tcslen(test))) {
			fatal_error(_T("jni_get_utf_chars() error:"));
		}

	}
#endif
	env->ReleaseStringChars(str, tmp);
	if (env->ExceptionCheck()) {
		free(utf8);
		return 0;
	}

	return utf8;
}

/**
 * ReleaseStringUTFChars本体
 */
void jni_release_string_utf_chars(JNIEnv *env, jstring string, const char *chars) {
	free((void*) chars);	// メモリを解放する
}

/**
 * MonitorEnter本体
 */
jint jni_monitor_enter(JNIEnv* env, jobject obj) {
	if (monitor_enter(obj)) {
		return 0;
	} else {
		return -1;
	}
}

/**
 * MonitorExit本体
 */
jint jni_monitor_exit(JNIEnv* env, jobject obj) {
	if (monitor_exit(obj)) {
		return 0;
	} else {
		return -1;
	}
}

/**
 * ExceptionCheck本体
 */
jboolean JNICALL jni_exception_check(JNIEnv *env) {
	jni_env* jenv = (jni_env*) env;
	frame* current_frame = get_current_frame(jenv->jni_frame);
	return current_frame->body->current_exception != NULL ? JNI_TRUE : JNI_FALSE;
}

/**
 * NewDirectByteBuffer本体
 */
jobject JNICALL jni_new_direct_byte_buffer(JNIEnv* env, void* address, jlong capacity) {
	jclass clazz;
	jmethodID mid;
	jobject pointer;
	jobject result;

	if (sizeof(void*) == 4) {
		clazz = env->FindClass("gnu/classpath/Pointer32");
		mid = env->GetMethodID(clazz, "<init>", "(I)V");
	} else if (sizeof(void*) == 8) {
		clazz = env->FindClass("gnu/classpath/Pointer64");
		mid = env->GetMethodID(clazz, "<init>", "(J)V");
	}
	pointer = env->NewObject(clazz, mid, address);

	clazz = env->FindClass("java/nio/DirectByteBufferImpl$ReadWrite");
	mid = env->GetMethodID(clazz, "<init>", "(Ljava/lang/Object;Lgnu/classpath/Pointer;III)V");
	
	result = env->NewObject(clazz,
						  mid,
						  NULL,					// Object owner
						  pointer,				// Pointer address
						  (jint) capacity,		// int capacity
						  (jint) capacity,		// int limit
						  0						// int position
						  );
	return result;
}

/**
 * GetDirectBufferAddress 本体
 */
void* JNICALL jni_get_direct_buffer_address(JNIEnv* env, jobject buf) {
	// buf が "java.nio.DirectByteBufferImpl" かを調べる
	jclass dbbi_class = env->FindClass("java/nio/DirectByteBufferImpl");
	if (dbbi_class) {
		if (env->IsInstanceOf(buf, dbbi_class)) {
			// フィールド "address" を取り出す
			jfieldID fid = env->GetFieldID(dbbi_class, "address", "Lgnu/classpath/Pointer;");
			jobject pointer = env->GetObjectField(buf, fid);
			
			// Pointer.data を取り出す
			jclass pointer_class = env->GetObjectClass(pointer);
			fid = env->GetFieldID(pointer_class, "data", sizeof(void*) == 4 ? "I" : "J");
			return (sizeof(void*) == 4)
					? (void*) (env->GetIntField(pointer, fid))
					: (void*) (env->GetLongField(pointer, fid));
		}
	}
	return NULL;
}

/**
 * GetDirectBufferCapacity本体
 */
jlong JNICALL jni_get_direct_buffer_capacity(JNIEnv* env, jobject buf) {
	// 現在サポートしていない
	return -1L;
}


/**
 * GetJavaVM本体
 */
jint JNICALL jni_get_java_vm(JNIEnv *env, JavaVM **vm) {
	*vm = &g_java_vm;
	return 0;
}

/**
 * AttachCurrentThread()本体
 */
jint JNICALL jni_attach_current_thread(JavaVM *vm, void **p_env, void *thr_args) {
	if (JNI_OK == vm->GetEnv(p_env, JNI_VERSION_1_2)) {
		return 0;
	} else {
		// ToDo: 実装する
		*p_env = NULL;
		return -1;
	}
}

/**
 * AttachCurrentThreadAsDaemon()本体
 */
jint JNICALL jni_attach_current_thread_as_daemon(JavaVM* vm, void** penv, void* args) {
	// ToDo: 実装する
	*penv = NULL;
	return -1;
}

/**
 * DetachCurrentThreadDaemon()本体
 */
jint JNICALL jni_detach_current_thread(JavaVM *vm) {
	// ToDo: 実装する
	return -1;
}

/**
 * GetEnv()本体
 */
jint JNICALL jni_get_env(JavaVM *vm, void **env, jint version) {
	// カレントスレッドに対応するThreadオブジェクトを得る
	jobject thread = current_Thread();
	if (thread) {
		// スレッドのカレントフレームを得る
		frame* current_frame = get_current_frame_of(thread);
		if (current_frame) {
			JNIEnv* e = get_JNIEnv(current_frame);
			*env = e;
			return JNI_OK;
		}
	}
	*env = NULL;
	return JNI_EDETACHED;
}

/**
 * ClassLoader.loadClass(String)メソッドを呼び出し、ClassFile構造体を
 * 返す。
 */
ClassFile* call_loadClass(frame* current_frame, jobject cl, const char* name) {
	frame new_local_frame;
	jni_env* jenv = get_jni_env(current_frame, &new_local_frame);
	if (! jenv) {
		return NULL;
	}
	JNIEnv* env = (JNIEnv*) jenv;
	jclass clazz = env->GetObjectClass(cl);
	jmethodID mid = env->GetMethodID(clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
	jstring str = env->NewStringUTF(name);
	if (str == NULL) {
		release_jni_env(jenv);
		return NULL;
	}
	jclass result = (jclass) env->CallObjectMethod(cl, mid, str);
	ClassFile* cfile = NULL;
	if (! env->ExceptionCheck()) {
		jobject static_data = get_static_data(env, result);
		cfile = get_ClassFile(static_data);
	}
	release_jni_env(jenv);

	return cfile;
}

/**
 * assertionの有効／無効を設定する
 */
void set_assertion_status(bool enabled) {
	g_assertion_status = enabled;
}

/**
 * JAVA_HOMEを返す。
 */
const _TCHAR* get_java_home() {
	if (! g_java_home) {
		// 実行ファイルのパス名の２つ上の階層を得る
		_TCHAR* module_path = (_TCHAR*) malloc(sizeof(_TCHAR) * (MAX_PATH + 1));
		GetModuleFileName(GetModuleHandle(NULL), module_path, MAX_PATH);
		int len = _tcslen(module_path);
		int count = 0;
		int i;
		for (i = len - 1; i > 0; --i) {
			if (module_path[i] == _T('\\')) {
				if (++count >= 3) {
					break;
				}
			}
		}
		module_path[i] = _T('\0');
		
		// JAVA_HOMEを設定する
		g_java_home = module_path;
	}

	return g_java_home;
}

/**
 * 現在のargumentsを得る
 */
arguments* get_current_arguments() {
	return g_current_arguments;
}

/**
 * 現在のargumentsを設定する。
 * （2回目以降の呼び出しは無視される）
 */
void set_current_arguments(arguments* args) {
	g_current_arguments = args;
}


/**
 * 指定されたオブジェクトをラップ解除する
 */
static bool unwrap_object(JNIEnv* env, char sig, jobject obj, jvalue* unwrapped) {
	bool result = false;
	switch (sig) {
	case 'Z':	// boolean
		{
			jclass clazz = env->FindClass("java/lang/Boolean");
			if (clazz == NULL) {
				break;
			}
			jmethodID mid = env->GetMethodID(clazz, "booleanValue", "()Z");
			if (! mid) {
				break;
			}
			// ラップ解除し、値を格納する
			unwrapped->z = env->CallBooleanMethod(obj, mid);
			if (env->ExceptionCheck()) {
				break;
			}
			result = true;
		}
		break;

	case 'B':	// byte
		{
			jclass clazz = env->FindClass("java/lang/Byte");
			if (clazz == NULL) {
				break;
			}
			jmethodID mid = env->GetMethodID(clazz, "byteValue", "()B");
			if (! mid) {
				break;
			}
			// ラップ解除し、値を格納する
			unwrapped->b = env->CallByteMethod(obj, mid);
			if (env->ExceptionCheck()) {
				break;
			}
			result = true;
		}
		break;

	case 'S':	// short
		{
			jclass clazz = env->FindClass("java/lang/Short");
			if (clazz == NULL) {
				break;
			}
			jmethodID mid = env->GetMethodID(clazz, "shortValue", "()S");
			if (! mid) {
				break;
			}
			// ラップ解除し、値を格納する
			unwrapped->s = env->CallShortMethod(obj, mid);
			if (env->ExceptionCheck()) {
				break;
			}
			result = true;
		}
		break;

	case 'C':	// char
		{
			jclass clazz = env->FindClass("java/lang/Char");
			if (clazz == NULL) {
				break;
			}
			jmethodID mid = env->GetMethodID(clazz, "charValue", "()C");
			if (! mid) {
				break;
			}
			// ラップ解除し、値を格納する
			unwrapped->c = env->CallCharMethod(obj, mid);
			if (env->ExceptionCheck()) {
				break;
			}
			result = true;
		}
		break;
	case 'I':	// int
		{
			jclass clazz = env->FindClass("java/lang/Integer");
			if (clazz == NULL) {
				break;
			}
			jmethodID mid = env->GetMethodID(clazz, "intValue", "()I");
			if (! mid) {
				break;
			}
			// ラップ解除し、値を格納する
			unwrapped->i = env->CallIntMethod(obj, mid);
			if (env->ExceptionCheck()) {
				break;
			}
			result = true;
		}
		break;
	case 'J':	// long
		{
			jclass clazz = env->FindClass("java/lang/Long");
			if (clazz == NULL) {
				break;
			}
			jmethodID mid = env->GetMethodID(clazz, "longValue", "()J");
			if (! mid) {
				break;
			}
			// ラップ解除し、値を格納する
			unwrapped->j = env->CallLongMethod(obj, mid);
			if (env->ExceptionCheck()) {
				break;
			}
			result = true;
		}
		break;
	case 'F':	// float
		{
			jclass clazz = env->FindClass("java/lang/Float");
			if (clazz == NULL) {
				break;
			}
			jmethodID mid = env->GetMethodID(clazz, "floatValue", "()F");
			if (! mid) {
				break;
			}
			// ラップ解除し、値を格納する
			unwrapped->f = env->CallFloatMethod(obj, mid);
			if (env->ExceptionCheck()) {
				break;
			}
			result = true;
		}
		break;

	case 'D':	// double
		{
			jclass clazz = env->FindClass("java/lang/Double");
			if (clazz == NULL) {
				break;
			}
			jmethodID mid = env->GetMethodID(clazz, "doubleValue", "()D");
			if (! mid) {
				break;
			}
			// ラップ解除し、値を格納する
			unwrapped->d = env->CallDoubleMethod(obj, mid);
			if (env->ExceptionCheck()) {
				break;
			}
			result = true;
		}
		break;

	default:
		unwrapped->l = obj;
		result = true;
		break;
	}

	return result;
}

/**
 * Javaに渡す引数を解析し、String[] を作成する
 */
static jobjectArray create_argv(JNIEnv* env, const _TCHAR* commandline) {
	int argc = 0;
	jstring argv[256];	// 最大256個のパラメータを渡すことができる

	if (commandline) {
		while(*commandline) {
			jstring str = NULL;
			switch (*commandline) {
			case _T(' '):
				// 空白は読み飛ばす
				while (*commandline == _T(' ')) {
					commandline++;
				}
				break;

			case _T('"'):
			case _T('\''):
				// クオート文字
				{
					_TCHAR quote = *commandline;
					commandline++;
					const _TCHAR* start = commandline;
					while (*commandline && *commandline != quote) {
						// 次にクオート文字が現れるまでを１要素とする
						commandline++;
					}
					int len = commandline - start;
					str = env->NewString((const jchar*) start, len);
					if (*commandline) {
						commandline++;
					}
				}
				break;

			default:
				// 通常の文字
				{
					const _TCHAR* start = commandline;
					// 空白が現れるまでが１要素
					while (*commandline && *commandline != _T(' ')) {
						commandline++;
					}
					int len = commandline - start;
					str = env->NewString((const jchar*) start, len);
				}
				break;
			}
			if (str != NULL) {
				argv[argc] = str;
				argc++;
				str = NULL;
			}
		}
	}

	jclass clazz = env->FindClass("java/lang/String");
	jobjectArray args_array = env->NewObjectArray(argc, clazz, NULL);
	for (int i = 0; i < argc; ++i) {
		env->SetObjectArrayElement(args_array, i, argv[i]);
	}
	return args_array;
}




	
