/* 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 "resource.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_security_AccessControlContext.h>
#include <java_security_VMAccessController.h>
#include <java_util_VMTimeZone.h>

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

#define NULLPOINTEREXCEPTION_CLASS_NAME "java/lang/NullPointerException"

/**
 * 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	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 DEFAULT_LOCAL_REFERENCES_COUNT	16

 /**
 * 今までにロードしたDLLの数
 */
static unsigned int g_dlls_count;
static unsigned int g_dlls_limit;

/**
 * native libraries HMODULEs.
 */
struct loaded_module
{
  HMODULE hModule;
  class_loader* c_loader;
#ifdef DEBUG
  _TCHAR moduleName[MAX_PATH];
#endif
};

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

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

// ------------------------
// static関数宣言
// ------------------------

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_jclass (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);

static u4 parse_annotations (JNIEnv * env, ClassFile * cfile,
			     const char *data, u4 size,
			     jobjectArray * result);
static u4 parse_annotation (JNIEnv * env, ClassFile * cfile, const char *data,
			    u4 size, jobject * result);
static u4 parse_element_value (JNIEnv * env, ClassFile * cfile,
			       jclass annotationType, jstring elementName,
			       const char *data, u4 size, jobject * result);
static u4 parse_element_value_array (JNIEnv * env, ClassFile * cfile,
				     jclass annotationType,
				     jstring elementName, const char *data,
				     u4 size, const char *array_type,
				     jobject * result);
static java_utf8 *get_element_value_type (JNIEnv * env, jclass annotationType,
					  jstring elementName);

static bool get_stack(JNIEnv* env, jobjectArray* pClassArray, jobjectArray* pMethodNameArray);

// ---------------------------
// static関数宣言　ここまで
// ---------------------------

// 特別扱いするメソッド（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/VMMainThread", "showSplashScreen", "(Ljava/lang/String;)V",
   Java_java_lang_VMMainThread_showSplashScreen},

  {"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/VMThread", "getState", "()Ljava/lang/String;",
   Java_java_lang_VMThread_getState},


  {"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/security/AccessControlContext", "allowedByUserNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
   Java_java_security_AccessControlContext_allowedByUserNative},

  {"java/security/VMAccessController", "getStack", "()[[Ljava/lang/Object;",
   Java_java_security_VMAccessController_getStack},

  {"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", "getParameterAnnotations",
   "()[[Ljava/lang/annotation/Annotation;",
   Java_java_lang_reflect_Constructor_getParameterAnnotations},
  {"java/lang/reflect/Constructor", "getDeclaredAnnotations",
   "()[Ljava/lang/annotation/Annotation;",
   Java_java_lang_reflect_Constructor_getDeclaredAnnotations},

  {"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/Field", "getDeclaredAnnotations",
   "()[Ljava/lang/annotation/Annotation;",
   Java_java_lang_reflect_Field_getDeclaredAnnotations},

  {"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", "getParameterAnnotations",
   "()[[Ljava/lang/annotation/Annotation;",
   Java_java_lang_reflect_Method_getParameterAnnotations},
  {"java/lang/reflect/Method", "getDeclaredAnnotations",
   "()[Ljava/lang/annotation/Annotation;",
   Java_java_lang_reflect_Method_getDeclaredAnnotations},

  {"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},

};

/**
 * 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);

/*
 * in jvm.cpp
 */
extern HWND get_JVM_controller_window ();

/**
 * ネイティブメソッド呼び出し用関数ポインタ
 */
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_jclass (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_jclass (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);

}


//-----------------------------------------------------
// java.security.AccessControlContext
//-----------------------------------------------------

#ifdef DEBUG
static _TCHAR ACCESS_CONTROL_LOG_FILE[MAX_PATH+1];
#endif


/**
 * AccessControl dialog proc.
 */
#ifdef DEBUG
LRESULT CALLBACK
AccessControlDialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
    {
    case WM_INITDIALOG:
      {
	// Maximize dialog box.
	SHINITDLGINFO shidi = { 0 };
	shidi.dwMask = SHIDIM_FLAGS;
	shidi.dwFlags = SHIDIF_SIPDOWN |
                        SHIDIF_SIZEDLGFULLSCREEN |
	                SHIDIF_EMPTYMENU;
	shidi.hDlg = hDlg;
	SHInitDialog (&shidi);

        _TCHAR** param = (_TCHAR**) lParam;
        const _TCHAR* permChars = param[0];
        const _TCHAR* exeptionChars = param[1];

        SetWindowText(GetDlgItem(hDlg, IDC_PERMISSION), permChars);
        SetWindowText(GetDlgItem(hDlg, IDC_STACKTRACE), exeptionChars);

      }
      return TRUE;

    case WM_COMMAND:
      switch (LOWORD (wParam))
	{
      case IDYES:
      case IDNO:
	  {
	    // ダイアログを終了する
	    EndDialog (hDlg, LOWORD (wParam));
	    return TRUE;
	  }
	}
    }
  return FALSE;
}
#endif

/*
 * Class:     java_security_AccessControlContext
 * Method:    allowedByUserNative
 * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
 */
JNIEXPORT jboolean JNICALL Java_java_security_AccessControlContext_allowedByUserNative
  (JNIEnv *env, jobject, jstring permissionString, jstring exceptionString)
{
#ifdef DEBUG
  jsize permStringLen = env->GetStringLength(permissionString);
  jsize expStringLen = env->GetStringLength(exceptionString);
  _TCHAR* permChars = (_TCHAR*) malloc(sizeof(_TCHAR) * (permStringLen + 1));
  _TCHAR* expChars = (_TCHAR*) malloc(sizeof(_TCHAR) * (expStringLen + 1));
  
  assert(permChars && expChars);
  
  const jchar* permTemp = env->GetStringChars(permissionString, NULL);
  const jchar* expTemp = env->GetStringChars(exceptionString, NULL);

  _tcsncpy(permChars, (const wchar_t*) permTemp, permStringLen);
  permChars[permStringLen] = _T('\0');

  _tcsncpy(expChars, (const wchar_t*) expTemp, expStringLen);
  expChars[expStringLen] = _T('\0');

  env->ReleaseStringChars(permissionString, permTemp);
  env->ReleaseStringChars(exceptionString, expTemp);

  OutputDebugString(permChars);
  OutputDebugString(expChars);

  if (ACCESS_CONTROL_LOG_FILE[0] == _T('\0'))
  {
    // ToDo: output to log file.
  }
  _TCHAR* param[2] = { permChars, expChars };

  int result =
    DialogBoxParam(GetModuleHandle(NULL),
                  MAKEINTRESOURCE(IDD_ACCESSCONTROL),
                  get_JVM_controller_window(),
                  (DLGPROC) AccessControlDialogProc,
                  (LPARAM) param);
  free(permChars);
  free(expChars);

  switch (result)
  {
  case IDYES:
    return true;    // allow access
  case IDNO:
    return false;   // disallow access
  default:
    return false;
  }
#else
  return false; // throw exception.
#endif
}


/**
 * get java stack data.
 *
 * @param pClassArray [out] pointer of class array.
 * @param pMethodNameArray [out] pointer of method name array. if NULL, no method name returned.
 * @return  true: success, false: failed.
 */
static bool get_stack(JNIEnv* env, jobjectArray* pClassArray, jobjectArray* pMethodNameArray)
{
  jni_env *jenv = (jni_env *) env;
  frame *current_frame = get_current_frame (jenv->jni_frame);

  // get caller frame.
  frame *previous_frame = current_frame->previous_frame;
  jclass *classes = NULL;
  java_utf8** methods = NULL;

  int classes_count = 0;
  int classes_count_limit = 0;

  while (previous_frame && previous_frame->current_class_file && previous_frame->current_method_info)
    {
      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++;

      // expands temp array.
      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);
	    }
          if (pMethodNameArray)
          {
            // expand method name array.
            methods = (java_utf8**) realloc(methods,
                                       sizeof (java_utf8*) * classes_count_limit);
            if (! methods)
            {
              fatal_error(FATAL_ERROR_NO_MEMORY);
            }
          }
	}
      classes[classes_count - 1] = clazz;
      if (pMethodNameArray)
      {
        methods[classes_count - 1] = previous_frame->current_method_info->name;
      }
      previous_frame = previous_frame->previous_frame;
    }
  jclass element_class = env->FindClass ("java/lang/Class");
  if (element_class == NULL)
    {
      free(classes);
      return false;
    }

  jobjectArray classResult =
    env->NewObjectArray (classes_count, element_class, NULL);
  jobjectArray methodResult = NULL;
  if (pMethodNameArray)
  {
    methodResult = env->NewObjectArray(classes_count, env->FindClass("java/lang/String"), NULL);
  }
  if (env->ExceptionCheck ())
    {
      free(classes);
      free(methods);
      return false;
    }
  for (int i = 0; i < classes_count; ++i)
    {
      env->SetObjectArrayElement (classResult, i, classes[i]);
      if (methodResult)
      {
        env->SetObjectArrayElement (methodResult, i, env->NewStringUTF(methods[i]));
      }
      if (env->ExceptionCheck ())
	{
          free(classes);
          free(methods);
	  return false;
	}
    }
  free (classes);
  free (methods);

  *pClassArray = classResult;
  if (pMethodNameArray)
  {
    *pMethodNameArray = methodResult;
  }
  return true;
}


/*
 * Class:     java_security_VMAccessController
 * Method:    getStack
 * Signature: ()[[Ljava/lang/Object;
 */
JNIEXPORT jobjectArray JNICALL Java_java_security_VMAccessController_getStack
  (JNIEnv * env, jclass)
{
  jobjectArray classes;
  jobjectArray methods;

  if (get_stack(env, &classes, &methods))
  {
    jobjectArray result = env->NewObjectArray(2, env->FindClass("[Ljava/lang/Object;"), NULL);
    if (env->ExceptionCheck())
    {
      return NULL;
    }
    env->SetObjectArrayElement(result, 0, classes);
    env->SetObjectArrayElement(result, 1, methods);

    return result;
  }
  return NULL;
}

/*
 * Class:     gnu_classpath_VMStackWalker
 * Method:    getClassContext
 * Signature: ()[Ljava/lang/Class;
 */
JNIEXPORT jobjectArray JNICALL
Java_gnu_classpath_VMStackWalker_getClassContext (JNIEnv * env, jclass)
{
  jobjectArray classArray;
  if (get_stack(env, &classArray, NULL))
  {
    return classArray;
  }
  return NULL;
}

/*
 * 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)
{
  const _TCHAR* dir = get_temp_dir();
  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);
  if (!static_data)
    {
      return NULL;
    }
  ClassFile *cfile = get_ClassFile (static_data);
  if (!cfile)
    {
      return NULL;
    }
  if (cfile->super_class_name == NULL || is_interface (cfile->access_flags))
    {
      return NULL;
    }
  else
    {
      class_loader *l = get_defining_loader (cfile);
      return find_jclass (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);

  // count interfaces.
  u2 interfaces_count = cfile->interfaces_count;
  jsize array_size = interfaces_count;

  // allocate an array.
  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;
    }

  // set Class instances to the array.
  jsize array_index = 0;
  class_loader *loader = cfile->defining_loader;
  for (u2 i = 0; i < interfaces_count; ++i)
    {
      // load ClassFile.
      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);

      // find jclass object and set to the array.
      jclass value = find_jclass(env, icfile->this_class_name, loader);
      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_jclass (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_jclass (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_jclass (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_jclass (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 target)
{
  ClassFile *cfile = get_ClassFile (get_static_data (env, target));
  raw_annotations_data *raw_annotations = cfile->annotations;
  if (raw_annotations)
    {
      jobjectArray result = NULL;
      u4 bytes_read =
	parse_annotations (env, cfile, raw_annotations->data,
			   raw_annotations->length, &result);
      return result;
    }

  // cannot found annotations.
  jclass clazz = env->FindClass ("java/lang/annotation/Annotation");
  return env->NewObjectArray (0, clazz, 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)
    {
      static jmethodID vmThrowable_constructor_mid;
      int size = sizeof (frame) * count;
      jbyteArray ba = env->NewByteArray (size);
      if (ba != NULL)
	{
	  env->SetByteArrayRegion (ba, 0, size, (jbyte *) trace);
	  // VMThrowableの新しいインスタンスを作成する
          if (! vmThrowable_constructor_mid)
          {
	    vmThrowable_constructor_mid = env->GetMethodID (vmtclass, "<init>", "()V");
          }
	  vmtobj = env->NewObject (vmtclass, vmThrowable_constructor_mid);
	  if (vmtobj != NULL)
	    {
              static jfieldID internalElements;
	      // フィールド "internalElements" に設定する
              if (! internalElements)
              {
	        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]);

    }
  if (env->ExceptionCheck())
    {
      // まずいことになっている
      assert(false);
    }
  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_VMMainThread
 * Method:    showSplashScreen
 * Signature: (Ljava/io/InputStream;)V
 */
JNIEXPORT void JNICALL Java_java_lang_VMMainThread_showSplashScreen
  (JNIEnv *env, jclass, jobject ins)
{
  typedef void (*MYSAIFU_SHOWSPLASHSCREEN) (JNIEnv* env, jobject ins);
  MYSAIFU_SHOWSPLASHSCREEN showSplash;

  // Load "wcepeer" to show image file
  HINSTANCE hDLL = ::LoadLibrary(_T("wcepeer"));
  if (! hDLL)
  {
    // Cannot load.
    return;
  }

  showSplash = (MYSAIFU_SHOWSPLASHSCREEN) ::GetProcAddress(hDLL, _T("Mysaifu_showSplashScreen"));
  if (! showSplash)
  {
    // Not found.
    return;
  }

  // Show splash screen
  showSplash(env, ins);
}

/*
 * 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;
    }

  // スレッドのステータスを設定する
  int thread_state_flags = JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_IN_OBJECT_WAIT;
  thread_state_flags |= (timeout || ns) ? JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT : JVMTI_THREAD_STATE_WAITING_INDEFINITELY;
  update_thread_state_bits(current_frame->body, thread_state_flags, JVMTI_THREAD_STATE_RUNNABLE);

  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;
    }

  update_thread_state_bits(current_frame->body, JVMTI_THREAD_STATE_RUNNABLE, thread_state_flags);
}

/*
 * 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),
		       NULLPOINTEREXCEPTION_CLASS_NAME);
      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)
{
  interrupt (vmthread);
}

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

/*
 * 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)
{
  jboolean flag = is_interrupted (NULL);
  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 (NULLPOINTEREXCEPTION_CLASS_NAME);
      if (npe_class != NULL)
	{
	  env->ThrowNew (npe_class, "");
	}
      return JNI_FALSE;
    }
  return (jboolean) is_monitor_owner (obj);
}

/*
 * Class:     java_lang_VMThread
 * Method:    getState
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_java_lang_VMThread_getState
  (JNIEnv *env, jobject self)
{
  jclass clazz = env->GetObjectClass(self);
  assert(clazz);
  jfieldID fid = env->GetFieldID(clazz, "nativePointer", "I");
  assert(fid);
  jint pointer = env->GetIntField(self, fid);
  
  const _TCHAR* stat;
  if (! pointer)
  {
    // Still in Thread.start()
    stat = _T("RUNNABLE");
  }
  else
  {
    frame_body* fb = (frame_body*) pointer;
    const int state_flags = get_thread_state_bits(fb);

    // Convert JVMTI_THREAD_STATE_XXX flags to Thread.State
    stat = get_thread_state_from_jvmti_bits(state_flags);
  }
  return env->NewString((const jchar*) stat, _tcslen(stat));
}

/*
 * 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 (NULLPOINTEREXCEPTION_CLASS_NAME);
      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_jclass (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))
	{
	  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.cpp 内で宣言されている
  extern HWND get_JVM_controller_window ();
  extern void notify_VMRuntime_exit ();

  // このメソッドがJVMの終了ポイント
  // このメソッドが呼び出された時点で、Runtime.addShutdownHook()で登録された
  // シャットダウンフックスレッドの実行はすべて完了している（はず）

  // 待機しているVMスレッドに対して、終了を通知する
  notify_VMRuntime_exit ();

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

  // コントローラウインドウに WM_CLOSE を送る
  PostMessage (get_JVM_controller_window (), WM_CLOSE, 0, 0);
}

/*
 * 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;
    }

  const 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].hModule)
	{
          if (g_hmodules[i].c_loader != get_class_loader(loader))
          {
            // Native Library "dllname" already loaded in another classloader.
            const char* format = "Native Library %s already loaded in another classloader";
            char* buff = (char*) malloc(strlen(format) + len + 1);
            if (buff)
            {
              const char* name = env->GetStringUTFChars(filename, NULL);
              if (name)
              {
                sprintf(buff, format, name);
                env->ReleaseStringUTFChars(filename, name);
              }
            }
            // throw UnsatisfiedLinkError to abort search DLL.
            env->ThrowNew(env->FindClass("java/lang/UnsatisfiedLinkError"), (const char*) buff);
            free(buff);

            return 0;
          }
	  found = true;
	}
    }


  if (!found)
    {
      g_dlls_count++;
      if (g_dlls_count >= g_dlls_limit)
	{
	  g_dlls_limit += 32;
	  g_hmodules =
	    (loaded_module *) realloc (g_hmodules, sizeof (loaded_module) * g_dlls_limit);
	}
      if (g_hmodules != NULL)
	{
          g_hmodules[g_dlls_count - 1].hModule = hModule;
          g_hmodules[g_dlls_count - 1].c_loader = get_class_loader(loader);
#ifdef DEBUG
          {
            jsize len = env->GetStringLength(filename);
            const jchar* tmp = env->GetStringChars(filename, NULL);
            _tcsncpy(g_hmodules[g_dlls_count - 1].moduleName, reinterpret_cast<const _TCHAR*>(tmp), len);
            env->ReleaseStringChars(filename, tmp);
            g_hmodules[g_dlls_count-1].moduleName[len] = _T('\0');
          }
#endif
	  // 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 (NULLPOINTEREXCEPTION_CLASS_NAME);
      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 * env,
							      jobject
							      constructor)
{
  jobjectArray result = NULL;
  jclass clazz = env->FindClass ("[Ljava/lang/annotation/Annotation;");

  ClassFile *cfile = NULL;
  method_info *minfo = NULL;
  get_Constructor_data (env, constructor, &cfile, &minfo);

  raw_annotations_data_of_method_info *raw_data = minfo->annotations;
  if (raw_data)
    {
      raw_annotations_data *ann = raw_data->parameter_annotations;
      if (ann)
	{
	  u2 num_parameters = load_u1 (ann->data);
	  u4 bytes_read = sizeof (u2);
	  result = env->NewObjectArray ((jsize) num_parameters, clazz, NULL);
	  for (u2 i = 0; i < num_parameters; ++i)
	    {
	      jobjectArray comp = NULL;
	      bytes_read +=
		parse_annotations (env, cfile, ann->data + bytes_read,
				   ann->length - bytes_read, &comp);
	      env->SetObjectArrayElement (result, i, comp);
	    }
	}
    }
  if (!result)
    {
      jobject comp =
	env->NewObjectArray (0,
			     env->
			     FindClass ("java/lang/annotation/Annotation"),
			     NULL);
      jobjectArray paramTypes =
	create_parameter_types (env, minfo->descriptor,
				get_defining_loader (cfile));
      result =
	env->NewObjectArray (env->GetArrayLength (paramTypes), clazz, comp);
    }
  return result;
}

/*
 * Class:     java_lang_reflect_Constructor
 * Method:    getDeclaredAnnotations
 * Signature: ()[Ljava/lang/annotation/Annotation;
 */
JNIEXPORT jobjectArray JNICALL
  Java_java_lang_reflect_Constructor_getDeclaredAnnotations (JNIEnv * env,
							     jobject
							     constructor)
{
  ClassFile *cfile = NULL;
  method_info *minfo = NULL;
  get_Constructor_data (env, constructor, &cfile, &minfo);

  raw_annotations_data_of_method_info *raw = minfo->annotations;
  if (raw)
    {
      raw_annotations_data *raw_annotations = raw->annotations;
      if (raw_annotations)
	{
	  jobjectArray result = NULL;
	  u4 bytes_read =
	    parse_annotations (env, cfile, raw_annotations->data,
			       raw_annotations->length, &result);
	  return result;
	}
    }

  // cannot found annotations.
  jclass clazz = env->FindClass ("java/lang/annotation/Annotation");
  return env->NewObjectArray (0, clazz, 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;
}

/*
 * Class:     java_lang_reflect_Field
 * Method:    getDeclaredAnnotations
 * Signature: ()[Ljava/lang/annotation/Annotation;
 */
JNIEXPORT jobjectArray JNICALL
  Java_java_lang_reflect_Field_getDeclaredAnnotations (JNIEnv * env,
						       jobject field)
{
  jclass declaring = NULL;
  field_info *finfo = NULL;
  jfieldID fid = NULL;
  get_Field_data (env, field, &declaring, &finfo, &fid);
  ClassFile *cfile = get_ClassFile (get_static_data (env, declaring));

  raw_annotations_data *raw_annotations = finfo->annotations;
  if (raw_annotations)
    {
      jobjectArray result = NULL;
      u4 bytes_read =
	parse_annotations (env, cfile, raw_annotations->data,
			   raw_annotations->length, &result);
      return result;
    }

  // cannot found annotations.
  jclass clazz = env->FindClass ("java/lang/annotation/Annotation");
  return env->NewObjectArray (0, clazz, 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 (NULLPOINTEREXCEPTION_CLASS_NAME);
      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_jclass (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, NULLPOINTEREXCEPTION_CLASS_NAME);
	  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, 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 * env, jobject method)
{
  jobject result = NULL;

  ClassFile *cfile = NULL;
  method_info *minfo = NULL;
  get_Method_data (env, method, &cfile, &minfo);

  raw_annotations_data_of_method_info *rawdata = minfo->annotations;
  if (rawdata)
    {
      raw_annotations_data *ann = rawdata->annotation_default;
      if (ann)
	{
	  jstring elementName = env->NewStringUTF (minfo->name);
	  jmethodID mid =
	    env->GetMethodID (env->GetObjectClass (method),
			      "getDeclaringClass", "()Ljava/lang/Class;");
	  jclass clazz = (jclass) env->CallObjectMethod (method, mid);
	  parse_element_value (env, cfile, clazz, elementName, ann->data,
			       ann->length, &result);
	  assert (result);
	}
    }
  return result;
}

/*
 * Class:     java_lang_reflect_Method
 * Method:    getParameterAnnotations
 * Signature: ()[[Ljava/lang/annotation/Annotation;
 */
JNIEXPORT jobjectArray JNICALL
  Java_java_lang_reflect_Method_getParameterAnnotations (JNIEnv * env,
							 jobject method)
{
  jobjectArray result = NULL;
  jclass clazz = env->FindClass ("[Ljava/lang/annotation/Annotation;");

  ClassFile *cfile = NULL;
  method_info *minfo = NULL;
  get_Method_data (env, method, &cfile, &minfo);

  raw_annotations_data_of_method_info *raw_data = minfo->annotations;
  if (raw_data)
    {
      raw_annotations_data *ann = raw_data->parameter_annotations;
      if (ann)
	{
	  u2 num_parameters = load_u1 (ann->data);
	  u4 bytes_read = sizeof (u1);
	  result = env->NewObjectArray ((jsize) num_parameters, clazz, NULL);
	  for (u2 i = 0; i < num_parameters; ++i)
	    {
	      jobjectArray comp = NULL;
	      bytes_read +=
		parse_annotations (env, cfile, ann->data + bytes_read,
				   ann->length - bytes_read, &comp);
	      env->SetObjectArrayElement (result, i, comp);
	    }
	}
    }
  if (!result)
    {
      jobject comp =
	env->NewObjectArray (0,
			     env->
			     FindClass ("java/lang/annotation/Annotation"),
			     NULL);
      jobjectArray paramTypes =
	create_parameter_types (env, minfo->descriptor,
				get_defining_loader (cfile));
      result =
	env->NewObjectArray (env->GetArrayLength (paramTypes), clazz, comp);
    }
  return result;
}

/*
 * Class:     java_lang_reflect_Method
 * Method:    getDeclaredAnnotations
 * Signature: ()[Ljava/lang/annotation/Annotation;
 */
JNIEXPORT jobjectArray JNICALL
  Java_java_lang_reflect_Method_getDeclaredAnnotations (JNIEnv * env,
							jobject method)
{
  ClassFile *cfile = NULL;
  method_info *minfo = NULL;
  get_Method_data (env, method, &cfile, &minfo);

  raw_annotations_data_of_method_info *raw = minfo->annotations;
  if (raw)
    {
      raw_annotations_data *raw_annotations = raw->annotations;
      if (raw_annotations)
	{
	  jobjectArray result = NULL;
	  u4 bytes_read =
	    parse_annotations (env, cfile, raw_annotations->data,
			       raw_annotations->length, &result);
	  return result;
	}
    }

  // cannot found annotations.
  jclass clazz = env->FindClass ("java/lang/annotation/Annotation");
  return env->NewObjectArray (0, clazz, 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
	case 'B':		// byte
	case 'C':		// char
	case 'S':		// short
	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
	  {
	    // floatはdoubleに拡張されているので、va_arg(args, float)ではダメ
	    jdouble f = va_arg (args, jdouble);
	    PUSH_FLOAT (current_frame, (jfloat) 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);

            // 多次元配列
            while (bytes[pos + 1] == '[')
            {
              if (pos >= length)
              {
                // 不正なシグニチャ
                assert(false);
                break;
              }
              pos++;
            }

            // シグニチャを読み飛ばす
	    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++;
            
            while (ch == '[')
            {
              // 多次元配列
              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, "wrong number of arguments");
      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_jclass (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, "argument type mismatch");
	  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)
	    {
              class_loader* c_loader = g_hmodules[i].c_loader;
              if (cfile->defining_loader == c_loader)
              {
	        HMODULE hModule = g_hmodules[i].hModule;
	        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)
		{
                  class_loader* c_loader = g_hmodules[i].c_loader;
                  if (cfile->defining_loader == c_loader)
                  {
		    HMODULE hModule = g_hmodules[i].hModule;
		    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_jclass (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");
    }

  // Bug #19181
  // "package.ClassName" -> "package/ClassName"
  const char* binary_name;
  if (strchr(name, '.'))
  {
    int len = strlen(name);
    char* tmp = (char*) malloc(len + 1);
    strcpy(tmp, name);
    for (int i = 0; i < len; ++i)
    {
      if (tmp[i] == '.')
      {
        tmp[i] = '/';
      }
    }
    binary_name = intern_utf8(tmp);
    free(tmp);
  }
  else
  {
    binary_name = name;
  }

  jclass result = find_jclass (env, binary_name, loader);
  if (!result && cfile->this_class_name == vmRuntime
      && current_frame->current_method_info->name == nativeLoad)
    {
      // ClassLoader.getSystemClassLoader()を呼び出す
      jclass clazz = find_jclass (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_jclass (env, binary_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,
			      NULLPOINTEREXCEPTION_CLASS_NAME);
    }
  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, 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, 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, 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, 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 (NULLPOINTEREXCEPTION_CLASS_NAME);
      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_class_file, minfo);

  return (jstring) string_reference;
}

/**
 * GetStringLength本体
 */
jsize JNICALL
jni_get_string_length (JNIEnv * env, jstring str)
{
  if (! str)
  {
    env->ThrowNew(env->FindClass(NULLPOINTEREXCEPTION_CLASS_NAME), "str");
    return 0;
  }
  static jfieldID countFID;
  jobject string_reference = str;
  ClassFile* string_cfile = get_ClassFile(string_reference);
  if (! countFID)
  {
    countFID = get_field_id(string_cfile, intern_utf8("count"), intern_utf8("I"));
    assert(countFID);
  }
  jni_env *jenv = (jni_env *) env;
  frame *current_frame = get_current_frame (jenv->jni_frame);
  PUSH_OBJECT(current_frame, string_reference);
  jint len;
  if (get_field(current_frame, string_cfile, countFID))
  {
    POP_INT(current_frame, len);
  }
  return (jsize) len;


#if 0
  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;
#endif
}

/**
 * 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 (NULLPOINTEREXCEPTION_CLASS_NAME);
      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 (NULLPOINTEREXCEPTION_CLASS_NAME);
      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 (NULLPOINTEREXCEPTION_CLASS_NAME);
      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 (NULLPOINTEREXCEPTION_CLASS_NAME);
      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');
      _tcscat(module_path, _T("\\jre"));

      // 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;
}

static u4
parse_annotations (JNIEnv * env, ClassFile * cfile, const char *data,
		   u4 length, jobjectArray * result)
{
  jclass clazz = env->FindClass ("java/lang/annotation/Annotation");
  if (!clazz)
    {
      return 0;
    }

  const char *buff = data;
  u2 num_annotations = load_u2 (buff);
  u4 bytes_read = 2;

  *result = env->NewObjectArray (num_annotations, clazz, NULL);
  for (u2 i = 0; i < num_annotations; ++i)
    {
      jobject ann = NULL;
      bytes_read +=
	parse_annotation (env, cfile, buff + bytes_read, length - bytes_read,
			  &ann);
      env->SetObjectArrayElement (*result, i, ann);
    }
  return bytes_read;
}

static u4
parse_annotation (JNIEnv * env, ClassFile * cfile, const char *data,
		  u4 length, jobject * result)
{
  const char *buff = data;
  u2 type_index = load_u2 (buff);
  u4 bytes_read = sizeof (u2);

  // Loads specified Annotation class.
  java_utf8 *type_name = (java_utf8 *) cfile->constant_pool[type_index];
  class_loader *loader = get_defining_loader (cfile);
  size_t type_name_length = strlen (type_name);
  char *class_name = (char *)::malloc (type_name_length - 1);
  strncpy (class_name, type_name + 1, type_name_length - 2);
  class_name[type_name_length - 2] = '\0';
  jclass annotationType = find_jclass (env, class_name, loader);
  free (class_name);
  if (annotationType)
    {
      u2 num_element_value_pairs = load_u2 (buff + bytes_read);
      bytes_read += sizeof (u2);

      // pairs = new HashMap();
      jclass clazz = env->FindClass ("java/util/HashMap");
      jmethodID mid = env->GetMethodID (clazz, "<init>", "()V");
      jobject pairs = env->NewObject (clazz, mid);

      mid =
	env->GetMethodID (clazz, "put",
			  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

      for (u2 i = 0; i < num_element_value_pairs; ++i)
	{
	  jstring name;
	  u2 element_name_index = load_u2 (buff + bytes_read);
	  bytes_read += sizeof (u2);
	  const char *binary_name =
	    (const char *) cfile->constant_pool[element_name_index];
	  name = env->NewStringUTF (binary_name);

	  jobject value = NULL;
	  bytes_read +=
	    parse_element_value (env, cfile, annotationType, name,
				 buff + bytes_read, length - bytes_read,
				 &value);

	  // pairs.put(name, value);
	  if (value != NULL)
	    {
	      env->CallObjectMethod (pairs, mid, name, value);
	    }
	  else
	    {
	      // abort.
	      break;
	    }
	}

      // result = AnnotationInvocationHandler.create(annotationType, pairs);
      clazz =
	env->FindClass ("sun/reflect/annotation/AnnotationInvocationHandler");
      mid =
	env->GetStaticMethodID (clazz, "create",
				"(Ljava/lang/Class;Ljava/util/Map;)Ljava/lang/annotation/Annotation;");
      *result =
	env->CallStaticObjectMethod (clazz, mid, annotationType, pairs);
    }
  else
    {
      *result = NULL;
    }

  return bytes_read;
}

static u4
parse_element_value (JNIEnv * env, ClassFile * cfile, jclass annotationType,
		     jstring elementName, const char *data, u4 length,
		     jobject * result)
{
  const char *buff = data;
  u1 tag = load_u1 (buff);
  u4 bytes_read = sizeof (u1);

  switch (tag)
    {
    case 'B':
      {
	u2 const_value_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);

	jbyte b = (jbyte) cfile->constant_pool[const_value_index];
	jclass clazz = env->FindClass ("java/lang/Byte");
	jmethodID mid =
	  env->GetStaticMethodID (clazz, "valueOf", "(B)Ljava/lang/Byte;");
	*result = env->CallStaticObjectMethod (clazz, mid, b);
      }
      break;

    case 'C':
      {
	u2 const_value_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);

	jchar c = (jchar) cfile->constant_pool[const_value_index];
	jclass clazz = env->FindClass ("java/lang/Character");
	jmethodID mid =
	  env->GetStaticMethodID (clazz, "valueOf",
				  "(C)Ljava/lang/Character;");
	*result = env->CallStaticObjectMethod (clazz, mid, c);
      }
      break;

    case 'D':
      {
	u2 const_value_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);

	jdouble d = *(jdouble *) & cfile->constant_pool[const_value_index];
	jclass clazz = env->FindClass ("java/lang/Double");
	jmethodID mid =
	  env->GetStaticMethodID (clazz, "valueOf", "(D)Ljava/lang/Double;");
	*result = env->CallStaticObjectMethod (clazz, mid, d);
      }
      break;

    case 'F':
      {
	u2 const_value_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);

	jfloat f = *(jfloat *) & cfile->constant_pool[const_value_index];
	jclass clazz = env->FindClass ("java/lang/Float");
	jmethodID mid =
	  env->GetStaticMethodID (clazz, "valueOf", "(F)Ljava/lang/Float;");
	*result = env->CallStaticObjectMethod (clazz, mid, f);
      }
      break;

    case 'I':
      {
	u2 const_value_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);

	jint i = (jint) cfile->constant_pool[const_value_index];
	jclass clazz = env->FindClass ("java/lang/Integer");
	jmethodID mid =
	  env->GetStaticMethodID (clazz, "valueOf", "(I)Ljava/lang/Integer;");
	*result = env->CallStaticObjectMethod (clazz, mid, i);
      }
      break;

    case 'J':
      {
	u2 const_value_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);

	jlong j = *(jlong *) & cfile->constant_pool[const_value_index];
	jclass clazz = env->FindClass ("java/lang/Long");
	jmethodID mid =
	  env->GetStaticMethodID (clazz, "valueOf", "(J)Ljava/lang/Long;");
	*result = env->CallStaticObjectMethod (clazz, mid, j);
      }
      break;

    case 'S':
      {
	u2 const_value_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);

	jshort s = (jshort) cfile->constant_pool[const_value_index];
	jclass clazz = env->FindClass ("java/lang/Short");
	jmethodID mid =
	  env->GetStaticMethodID (clazz, "valueOf", "(S)Ljava/lang/Short;");
	*result = env->CallStaticObjectMethod (clazz, mid, s);
      }
      break;

    case 'Z':
      {
	u2 const_value_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);

	jboolean z = (jboolean) cfile->constant_pool[const_value_index];
	jclass clazz = env->FindClass ("java/lang/Boolean");
	jmethodID mid =
	  env->GetStaticMethodID (clazz, "valueOf", "(Z)Ljava/lang/Boolean;");
	*result = env->CallStaticObjectMethod (clazz, mid, z);
      }
      break;

    case 's':
      {
	u2 const_value_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);

	java_utf8 *tmp =
	  (java_utf8 *) cfile->constant_pool[const_value_index];
	*result = env->NewStringUTF (tmp);
      }
      break;

    case 'c':
      // class
      {
	u2 class_info_index = load_u2 (buff + bytes_read);
	bytes_read += sizeof (u2);
	java_utf8 *tmp = (java_utf8 *) cfile->constant_pool[class_info_index];

	// load the class
	size_t class_name_length =::strlen (tmp) - 2;	// Lclass_name;
	char *class_name = (char *)::malloc (class_name_length + 1);
	if (!class_name)
	  {
	    break;
	  }
	::strncpy (class_name, tmp + 1, class_name_length);
	class_name[class_name_length] = '\0';
	class_loader *loader = get_defining_loader (cfile);
	*result = find_jclass (env, class_name, loader);
	free (class_name);

      }
      break;

    case 'e':
      // enum
      {
	u2 type_name_index = load_u2 (buff + bytes_read);
	u2 const_name_index = load_u2 (buff + bytes_read + sizeof (u2));
	bytes_read += (sizeof (u2) + sizeof (u2));

	java_utf8 *type_name =
	  (java_utf8 *) cfile->constant_pool[type_name_index];
	java_utf8 *const_name =
	  (java_utf8 *) cfile->constant_pool[const_name_index];

	// loads enum class
	size_t class_name_length =::strlen (type_name) - 2;	// Lclass_name;
	char *class_name = (char *)::malloc (class_name_length + 1);
	if (!class_name)
	  {
	    break;
	  }
	::strncpy (class_name, type_name + 1, class_name_length);
	class_name[class_name_length] = '\0';
	class_loader *loader = get_defining_loader (cfile);
	jclass enum_class = find_jclass (env, class_name, loader);
	free (class_name);
	if (!enum_class)
	  {
	    break;
	  }

	jfieldID const_field_id =
	  env->GetStaticFieldID (enum_class, const_name, type_name);
	*result = env->GetStaticObjectField (enum_class, const_field_id);
      }
      break;

    case '@':
      // Annotation
      {
	bytes_read +=
	  parse_annotation (env, cfile, buff + bytes_read,
			    length - bytes_read, result);
      }
      break;

    case '[':
      // array
      {
	// get type of the array
	java_utf8 *array_type =
	  get_element_value_type (env, annotationType, elementName);

	if (array_type)
	  {
	    bytes_read +=
	      parse_element_value_array (env, cfile, annotationType,
					 elementName, buff + bytes_read,
					 length - bytes_read, array_type,
					 result);
	  }
      }
      break;

    default:
      // Do nothing
      break;
    }

  return bytes_read;
}

/**
 * Macro for parse array of primitive type element value paris.
 */
#define PARSE_ELEMENT_VALUE_ARRAY_PRIMITIVE(JAVA_TYPE, JNI_ARRAY_NAME) \
static u4 parse_element_value_array_##JAVA_TYPE##(JNIEnv* env, ClassFile* cfile, const char* data, u4 data_length, u2 num_values, jobject* result) \
{ \
  *result = env->New##JNI_ARRAY_NAME##Array((jsize) num_values); \
  if (! *result) \
  { \
    return 0; \
  } \
  data++;  /* skip tag byte */ \
  JAVA_TYPE* dest = env->Get##JNI_ARRAY_NAME##ArrayElements((##JAVA_TYPE##Array) *result, NULL); \
  for (u2 i = 0; i < num_values; ++i) \
  { \
    u2 const_value_index = load_u2(data); \
    data += 3; /* skip a tag byte */ \
    JAVA_TYPE value = *(JAVA_TYPE*)&cfile->constant_pool[const_value_index]; \
    dest[i] = value; \
  } \
  env->Release##JNI_ARRAY_NAME##ArrayElements((##JAVA_TYPE##Array) *result, dest, 0); \
  return 3 * num_values; \
}

PARSE_ELEMENT_VALUE_ARRAY_PRIMITIVE (jboolean, Boolean);
PARSE_ELEMENT_VALUE_ARRAY_PRIMITIVE (jbyte, Byte);
PARSE_ELEMENT_VALUE_ARRAY_PRIMITIVE (jshort, Short);
PARSE_ELEMENT_VALUE_ARRAY_PRIMITIVE (jchar, Char);
PARSE_ELEMENT_VALUE_ARRAY_PRIMITIVE (jint, Int);
PARSE_ELEMENT_VALUE_ARRAY_PRIMITIVE (jlong, Long);
PARSE_ELEMENT_VALUE_ARRAY_PRIMITIVE (jfloat, Float);
PARSE_ELEMENT_VALUE_ARRAY_PRIMITIVE (jdouble, Double);

static u4
parse_element_value_array (JNIEnv * env, ClassFile * cfile,
			   jclass annotationType, jstring elementName,
			   const char *data, u4 data_length,
			   const char *array_type, jobject * result)
{
  const char *buff = data;
  u2 num_values = load_u2 (buff);
  u4 bytes_read = sizeof (u2);

  if (*array_type != '[')
    {
      return bytes_read;
    }
  if (array_type[1] == '[')
    {
      // cannot parse multiarray
      return bytes_read;
    }
  if (array_type[1] == 'L')
    {
      // array of Annotation or enum
      java_utf8 *component_name = get_array_component_name (array_type);
      class_loader *loader = get_defining_loader (cfile);
      jclass clazz = find_jclass (env, component_name, loader);
      *result = env->NewObjectArray ((jsize) num_values, clazz, NULL);
      for (u2 i = 0; i < num_values; ++i)
	{
	  jobject comp = NULL;
	  bytes_read +=
	    parse_element_value (env, cfile, annotationType, elementName,
				 buff + bytes_read, data_length - bytes_read,
				 &comp);
	  env->SetObjectArrayElement ((jobjectArray) * result, i, comp);
	}
    }
  else
    {
      switch (array_type[1])
	{
	case 'B':
	  // [B - byte array
	  bytes_read +=
	    parse_element_value_array_jbyte (env, cfile, buff + bytes_read,
					     data_length - bytes_read,
					     num_values, result);
	  break;

	case 'C':
	  // [C - char array
	  bytes_read +=
	    parse_element_value_array_jchar (env, cfile, buff + bytes_read,
					     data_length - bytes_read,
					     num_values, result);
	  break;

	case 'D':
	  // [D - double array
	  bytes_read +=
	    parse_element_value_array_jdouble (env, cfile, buff + bytes_read,
					       data_length - bytes_read,
					       num_values, result);
	  break;

	case 'F':
	  // [F - float array
	  bytes_read +=
	    parse_element_value_array_jfloat (env, cfile, buff + bytes_read,
					      data_length - bytes_read,
					      num_values, result);
	  break;

	case 'I':
	  // [I - integer array
	  bytes_read +=
	    parse_element_value_array_jint (env, cfile, buff + bytes_read,
					    data_length - bytes_read,
					    num_values, result);
	  break;

	case 'J':
	  // [J - long array
	  bytes_read +=
	    parse_element_value_array_jlong (env, cfile, buff + bytes_read,
					     data_length - bytes_read,
					     num_values, result);
	  break;

	case 'S':
	  // [S - short array
	  bytes_read +=
	    parse_element_value_array_jshort (env, cfile, buff + bytes_read,
					      data_length - bytes_read,
					      num_values, result);
	  break;

	case 'Z':
	  // [Z - boolean array
	  bytes_read +=
	    parse_element_value_array_jboolean (env, cfile, buff + bytes_read,
						data_length - bytes_read,
						num_values, result);
	  break;
	}
    }
  return bytes_read;
}

java_utf8 *
get_element_value_type (JNIEnv * env, jclass annotationType,
			jstring elementName)
{
  java_utf8 *type = NULL;

  // method = annotationType.getMethod(elementName);
  jclass clazz = env->GetObjectClass (annotationType);
  jmethodID mid =
    env->GetMethodID (clazz, "getMethod",
		      "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
  jobject method =
    env->CallObjectMethod (annotationType, mid, elementName, NULL);
  if (!env->ExceptionCheck ())
    {
      // method.getReturnType();
      mid =
	env->GetMethodID (env->GetObjectClass (method), "getReturnType",
			  "()Ljava/lang/Class;");
      jclass returnType = (jclass) env->CallObjectMethod (method, mid);
      if (!env->ExceptionCheck ())
	{
	  jobject static_data = get_static_data (env, returnType);
	  ClassFile *cfile = get_ClassFile (static_data);
	  type = cfile->this_class_name;
	}
    }
  else
    {
      // ignore exception
      env->ExceptionClear ();
    }
  return type;
}
