You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by ge...@apache.org on 2005/10/05 04:20:10 UTC

svn commit: r294974 [10/25] - in /incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm: ./ jchevm/ jchevm/doc/ jchevm/etc/ jchevm/include/ jchevm/java/ jchevm/java/org/ jchevm/java/org/dellroad/ jchevm/java/org/dellroad/jc/ jchevm/java/org/dellroad...

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/bootstrap.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/bootstrap.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/bootstrap.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/bootstrap.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,416 @@
+
+/*
+ * Copyright 2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * $Id: bootstrap.c,v 1.16 2005/03/26 23:20:19 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/*
+ * Macros for code simplification.
+ */
+
+#define BOOTSTRAP_TYPE(name, field)					\
+    do {								\
+	if ((vm->boot.types.field = _jc_load_type(env,			\
+	    vm->boot.loader, name)) == NULL)				\
+		goto fail;						\
+    } while (0)
+
+#define RESOLVE_METHOD1(class, cname, jname, signature, static)		\
+    do {								\
+	if ((vm->boot.methods.class.cname				\
+	    = _jc_get_declared_method(env, vm->boot.types.class,	\
+	      jname, signature, _JC_ACC_STATIC, static)) == NULL) {	\
+		_jc_post_exception_info(env);				\
+		goto fail;						\
+	}								\
+    } while (0)
+
+#define RESOLVE_METHOD(class, name, signature, static)			\
+	RESOLVE_METHOD1(class, name, #name, signature, static)
+
+#define RESOLVE_CONSTRUCTOR(class, signature)				\
+	RESOLVE_METHOD1(class, init, "<init>", signature, 0)
+
+#define RESOLVE_FIELD(class, fname, signature, is_static)		\
+    do {								\
+	if ((vm->boot.fields.class.fname				\
+	    = _jc_get_declared_field(env, vm->boot.types.class,		\
+	      #fname, signature, is_static)) == NULL) {			\
+		_jc_post_exception_info(env);				\
+		goto fail;						\
+	}								\
+    } while (0)
+
+/*
+ * During bootstrap, the throwing of internal exceptions caused by missing
+ * object files on which certain core classes depend for class resolution
+ * can lead to infinite loops and/or ExceptionInInitializerErrors during
+ * the initialization of java.lang.Class. In other words, "Avoid all
+ * exceptions until Class.<clinit> finishes". So we preemptively create
+ * and load these object files here. This list pretty much has to be
+ * cobbled together by hand based on trial & error and is highly dependent
+ * on the Class.<clinit> initialization sequence.
+ */
+static const char *const _jc_bootstrap_types[] = {
+	"java/io/FileInputStream",
+	"java/lang/ClassLoader",
+	"java/lang/Math",
+	"java/lang/Runtime",
+	"java/lang/String",
+	"java/lang/StringBuffer",
+	"java/lang/System",
+	"java/lang/VMRuntime",
+	"java/lang/VMString",
+	"java/lang/VMSystem",
+	"java/lang/VMClassLoader",
+	"java/lang/ref/WeakReference",
+	"java/security/AllPermission",
+	"java/security/ProtectionDomain",
+	"java/util/Collections",
+	"java/util/Hashtable$HashIterator",
+	"java/util/Map",
+	"java/util/Properties",
+	"java/util/StringTokenizer",
+	"java/util/Vector",
+	"java/util/WeakHashMap",
+	"java/util/WeakHashMap$WeakEntrySet",
+	"java/util/zip/ZipFile",
+};
+#define NUM_BOOTSTRAP_TYPES						\
+	(sizeof(_jc_bootstrap_types) / sizeof(*_jc_bootstrap_types))
+
+/*
+ * Bootstrap Java classes
+ */
+jint
+_jc_bootstrap_classes(_jc_env *env)
+{
+	_jc_jvm *const vm = env->vm;
+	jboolean long_ptr = JNI_FALSE;
+	_jc_type **types;
+	int num_types;
+	int i;
+
+	/* Get pointer size */
+	switch (sizeof(void *)) {
+	case 4:
+		long_ptr = JNI_FALSE;
+		break;
+	case 8:
+		long_ptr = JNI_TRUE;
+		break;
+	default:
+		_JC_ASSERT(JNI_FALSE);
+		break;
+	}
+
+	/*
+	 * Load some special types and locate various fields,
+	 * methods, and constructors. We are loading types only,
+	 * which simply means creating the _jc_type structures.
+	 *
+	 * Since java.lang.Class is not loaded until later, during
+	 * this initial loading we defer creating Class instances.
+	 * We also disable class initialization until later.
+	 */
+	vm->initialization->may_execute = JNI_FALSE;
+
+	/* Load primitive types */
+	for (i = _JC_TYPE_BOOLEAN; i <= _JC_TYPE_VOID; i++) {
+		if ((vm->boot.types.prim[i]
+		    = _jc_load_primitive_type(env, i)) == NULL) {
+			_jc_post_exception_info(env);
+			goto fail;
+		}
+	}
+
+	/* Load types required for creating arrays */
+	BOOTSTRAP_TYPE("java/lang/Object", Object);
+	BOOTSTRAP_TYPE("java/lang/Cloneable", Cloneable);
+	BOOTSTRAP_TYPE("java/io/Serializable", Serializable);
+
+	/* Initialize array type info */
+	if (_jc_setup_array_types(env) != JNI_OK) {
+		_jc_post_exception_info(env);
+		goto fail;
+	}
+
+	/* Load primitive array types */
+	for (i = _JC_TYPE_BOOLEAN; i < _JC_TYPE_VOID; i++) {
+		char jname[] = { '[', _jc_prim_chars[i], '\0' };
+
+		BOOTSTRAP_TYPE(jname, prim_array[i]);
+	}
+
+	/* Load some classes related to exceptions */
+	BOOTSTRAP_TYPE("java/lang/String", String);
+	BOOTSTRAP_TYPE("java/lang/Throwable", Throwable);
+	BOOTSTRAP_TYPE("java/lang/VMThrowable", VMThrowable);
+	RESOLVE_FIELD(Throwable, cause, "Ljava/lang/Throwable;", 0);
+	RESOLVE_FIELD(Throwable, detailMessage, "Ljava/lang/String;", 0);
+	RESOLVE_FIELD(Throwable, vmState, "Ljava/lang/VMThrowable;", 0);
+	RESOLVE_FIELD(VMThrowable, vmdata, "[B", 0);
+	for (i = 0; i < _JC_VMEXCEPTION_MAX; i++)
+		BOOTSTRAP_TYPE(_jc_vmex_names[i], vmex[i]);
+
+	/* Load more types we need for bootstrapping */
+	for (i = 0; i < NUM_BOOTSTRAP_TYPES; i++) {
+		if (_jc_load_type(env, vm->boot.loader,
+		    _jc_bootstrap_types[i]) == NULL)
+			goto fail;
+	}
+
+	/* Load more special classes */
+	BOOTSTRAP_TYPE(long_ptr ?
+	    "gnu/classpath/RawData64" : "gnu/classpath/RawData32", RawData);
+	BOOTSTRAP_TYPE("gnu/classpath/VMStackWalker", VMStackWalker);
+	BOOTSTRAP_TYPE("java/lang/Error", Error);
+	BOOTSTRAP_TYPE("java/lang/ClassLoader", ClassLoader);
+	BOOTSTRAP_TYPE("java/lang/StackTraceElement", StackTraceElement);
+	BOOTSTRAP_TYPE("java/lang/System", System);
+	BOOTSTRAP_TYPE("java/lang/Thread", Thread);
+	BOOTSTRAP_TYPE("java/lang/ThreadGroup", ThreadGroup);
+	BOOTSTRAP_TYPE("java/lang/VMThread", VMThread);
+	BOOTSTRAP_TYPE("java/lang/ref/Reference", Reference);
+	BOOTSTRAP_TYPE("java/lang/ref/SoftReference", SoftReference);
+	BOOTSTRAP_TYPE("java/lang/ref/WeakReference", WeakReference);
+	BOOTSTRAP_TYPE("java/lang/ref/PhantomReference", PhantomReference);
+	BOOTSTRAP_TYPE("java/lang/reflect/AccessibleObject", AccessibleObject);
+	BOOTSTRAP_TYPE("java/lang/reflect/Constructor", Constructor);
+	BOOTSTRAP_TYPE("java/lang/reflect/Field", Field);
+	BOOTSTRAP_TYPE("java/lang/reflect/Method", Method);
+	BOOTSTRAP_TYPE("java/nio/Buffer", Buffer);
+	BOOTSTRAP_TYPE("java/nio/DirectByteBufferImpl", DirectByteBufferImpl);
+	BOOTSTRAP_TYPE("[Ljava/lang/Class;", Class_array);
+	BOOTSTRAP_TYPE("[Ljava/lang/StackTraceElement;",
+	    StackTraceElement_array);
+	BOOTSTRAP_TYPE("[Ljava/lang/reflect/Constructor;", Constructor_array);
+	BOOTSTRAP_TYPE("[Ljava/lang/reflect/Field;", Field_array);
+	BOOTSTRAP_TYPE("[Ljava/lang/reflect/Method;", Method_array);
+	for (i = _JC_TYPE_BOOLEAN; i <= _JC_TYPE_VOID; i++)
+		BOOTSTRAP_TYPE(_jc_prim_wrapper_class[i], prim_wrapper[i]);
+	if (vm->generation_enabled)
+		BOOTSTRAP_TYPE("org/dellroad/jc/Generate", Generate);
+
+	/* Find special constructors */
+	RESOLVE_CONSTRUCTOR(StackTraceElement,
+	    "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Z)V");
+	RESOLVE_CONSTRUCTOR(Constructor, "(Ljava/lang/Class;I)V");
+	RESOLVE_CONSTRUCTOR(DirectByteBufferImpl,
+	    "(Ljava/lang/Object;Lgnu/classpath/RawData;III)V");
+	RESOLVE_CONSTRUCTOR(Field, "(Ljava/lang/Class;Ljava/lang/String;I)V");
+	RESOLVE_CONSTRUCTOR(Method, "(Ljava/lang/Class;Ljava/lang/String;I)V");
+	RESOLVE_CONSTRUCTOR(String, "([C)V");
+	RESOLVE_CONSTRUCTOR(Thread,
+	    "(Ljava/lang/VMThread;Ljava/lang/String;IZ)V");
+	RESOLVE_CONSTRUCTOR(ThreadGroup,
+	    "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V");
+	RESOLVE_CONSTRUCTOR(VMThread, "(Ljava/lang/Thread;)V");
+	for (i = _JC_TYPE_BOOLEAN; i <= _JC_TYPE_DOUBLE; i++) {
+		char signature[] = { '(', _jc_prim_chars[i], ')', 'V', '\0' };
+
+		RESOLVE_CONSTRUCTOR(prim_wrapper[i], signature);
+	}
+	for (i = 0; i < _JC_VMEXCEPTION_MAX; i++) {
+		switch (i) {
+		case _JC_ClassNotFoundException:
+			RESOLVE_CONSTRUCTOR(vmex[i],
+			    "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+			break;
+		case _JC_ExceptionInInitializerError:
+		case _JC_InvocationTargetException:
+			RESOLVE_CONSTRUCTOR(vmex[i],
+			    "(Ljava/lang/Throwable;)V");
+			break;
+		case _JC_ThreadDeath:
+			RESOLVE_CONSTRUCTOR(vmex[i], "()V");
+			break;
+		default:
+			RESOLVE_CONSTRUCTOR(vmex[i], "(Ljava/lang/String;)V");
+			break;
+		}
+	}
+
+	/* Find special methods */
+	RESOLVE_METHOD(AccessibleObject, isAccessible, "()Z", 0);
+	RESOLVE_METHOD(ClassLoader, getSystemClassLoader,
+	    "()Ljava/lang/ClassLoader;", _JC_ACC_STATIC);
+	RESOLVE_METHOD(ClassLoader, loadClass,
+	    "(Ljava/lang/String;)Ljava/lang/Class;", 0);
+	RESOLVE_METHOD(Method, invoke,
+	    "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	RESOLVE_METHOD(Object, finalize, "()V", 0);
+	RESOLVE_METHOD(Object, notifyAll, "()V", 0);
+	RESOLVE_METHOD(Object, toString, "()Ljava/lang/String;", 0);
+	RESOLVE_METHOD(Object, wait, "()V", 0);
+	RESOLVE_METHOD(Reference, enqueue, "()Z", 0);
+	RESOLVE_METHOD(String, intern, "()Ljava/lang/String;", 0);
+	RESOLVE_METHOD(Thread, stop, "()V", 0);
+	RESOLVE_METHOD(ThreadGroup, addThread, "(Ljava/lang/Thread;)V", 0);
+	RESOLVE_METHOD(ThreadGroup, uncaughtException,
+	    "(Ljava/lang/Thread;Ljava/lang/Throwable;)V", 0);
+	RESOLVE_METHOD(VMThread, run, "()V", 0);
+	for (i = _JC_TYPE_BOOLEAN; i <= _JC_TYPE_DOUBLE; i++) {
+		char signature[] = { '(', ')', _jc_prim_chars[i], '\0' };
+		char mname[32];
+
+		snprintf(mname, sizeof(mname), "%sValue", _jc_prim_names[i]);
+		RESOLVE_METHOD1(prim_wrapper[i], value, mname, signature, 0);
+	}
+	if (vm->generation_enabled) {
+		RESOLVE_METHOD(Generate, v,
+		    "()Lorg/dellroad/jc/Generate;", _JC_ACC_STATIC);
+		RESOLVE_METHOD(Generate, generateObject,
+		    "(Ljava/lang/String;Ljava/lang/ClassLoader;)V", 0);
+	}
+
+	/* Find special fields */
+	RESOLVE_FIELD(Buffer, address, "Lgnu/classpath/RawData;", 0);
+	RESOLVE_FIELD(Buffer, cap, "I", 0);
+	RESOLVE_FIELD(ClassLoader, parent, "Ljava/lang/ClassLoader;", 0);
+	RESOLVE_FIELD(ClassLoader, vmdata, "J", 0);
+	RESOLVE_FIELD(Constructor, clazz, "Ljava/lang/Class;", 0);
+	RESOLVE_FIELD(Constructor, slot, "I", 0);
+	RESOLVE_FIELD(Field, declaringClass, "Ljava/lang/Class;", 0);
+	RESOLVE_FIELD(Field, slot, "I", 0);
+	RESOLVE_FIELD(Method, declaringClass, "Ljava/lang/Class;", 0);
+	RESOLVE_FIELD(Method, slot, "I", 0);
+	RESOLVE_FIELD(RawData, data, long_ptr ? "J" : "I", 0);
+	RESOLVE_FIELD(Reference, referent, "Ljava/lang/Object;", 0);
+	RESOLVE_FIELD(Reference, queue, "Ljava/lang/ref/ReferenceQueue;", 0);
+	RESOLVE_FIELD(String, value, "[C", 0);
+	RESOLVE_FIELD(String, offset, "I", 0);
+	RESOLVE_FIELD(String, count, "I", 0);
+	RESOLVE_FIELD(System, in, "Ljava/io/InputStream;", 1);
+	RESOLVE_FIELD(System, out, "Ljava/io/PrintStream;", 1);
+	RESOLVE_FIELD(System, err, "Ljava/io/PrintStream;", 1);
+	RESOLVE_FIELD(Thread, daemon, "Z", 0);
+	RESOLVE_FIELD(Thread, group, "Ljava/lang/ThreadGroup;", 0);
+	RESOLVE_FIELD(Thread, name, "Ljava/lang/String;", 0);
+	RESOLVE_FIELD(Thread, priority, "I", 0);
+	RESOLVE_FIELD(Thread, vmThread, "Ljava/lang/VMThread;", 0);
+	RESOLVE_FIELD(ThreadGroup, root, "Ljava/lang/ThreadGroup;", 1);
+	RESOLVE_FIELD(VMThread, thread, "Ljava/lang/Thread;", 0);
+	RESOLVE_FIELD(VMThread, vmdata, "J", 0);
+
+	/* Load java.lang.Class */
+	BOOTSTRAP_TYPE("java/lang/Class", Class);
+	RESOLVE_FIELD(Class, vmdata, "J", 0);
+	RESOLVE_FIELD(Class, pd, "Ljava/security/ProtectionDomain;", 0);
+
+	/* Set initial lockwords for Object and Class */
+	_jc_initialize_lockword(env, vm->boot.types.Object, NULL);
+	_jc_initialize_lockword(env, vm->boot.types.Class,
+	    vm->boot.types.Object);
+
+	/*
+	 * We're now able to create "java/lang/Class" instances.
+	 * Belatedly create them for all of the types we just loaded.
+	 * Class initialization is disabled though so no Java code runs.
+	 * Note: we never invoke constructors for Class instances.
+	 */
+	vm->initialization->create_class = JNI_TRUE;
+	num_types = vm->boot.loader->defined_types.size;
+	if ((types = _JC_STACK_ALLOC(env,
+	    num_types * sizeof(*types))) == NULL) {
+		_jc_post_exception_info(env);
+		goto fail;
+	}
+	_jc_splay_list(&vm->boot.loader->defined_types, (void **)types);
+	_JC_MUTEX_LOCK(env, vm->boot.loader->mutex);
+	for (i = 0; i < num_types; i++) {
+		if (_jc_create_class_instance(env, types[i]) != JNI_OK) {
+			_JC_MUTEX_UNLOCK(env, vm->boot.loader->mutex);
+			_jc_post_exception_info(env);
+			goto fail;
+		}
+	}
+	for (i = _JC_TYPE_BOOLEAN; i <= _JC_TYPE_VOID; i++) {
+		if (_jc_create_class_instance(env,
+		    vm->boot.types.prim[i]) != JNI_OK) {
+			_JC_MUTEX_UNLOCK(env, vm->boot.loader->mutex);
+			_jc_post_exception_info(env);
+			goto fail;
+		}
+	}
+	_JC_MUTEX_UNLOCK(env, vm->boot.loader->mutex);
+
+	/*
+	 * Now resolve all loaded types. This will cause a bunch of
+	 * other classes to be loaded, and their Class instances will
+	 * be created normally, but still no class initialization will
+	 * take place so no Java code runs yet.
+	 */
+	if (_jc_resolve_type(env, vm->boot.types.Object) != JNI_OK)
+		goto fail;
+	if (_jc_resolve_type(env, vm->boot.types.Class) != JNI_OK)
+		goto fail;
+	for (i = 0; i < num_types; i++) {
+		if (_jc_resolve_type(env, types[i]) != JNI_OK)
+			goto fail;
+	}
+
+	/*
+	 * Initialize java.lang.Class (and therefore java.lang.Object).
+	 * This causes bunch of Java code to run for the first time,
+	 * due to static initializers in Class and Object.
+	 */
+	vm->initialization->may_execute = JNI_TRUE;
+	if (_jc_initialize_type(env, vm->boot.types.Class) != JNI_OK)
+		goto fail;
+
+	/* Create a "fallback" instance for each exception class */
+	for (i = 0; i < _JC_VMEXCEPTION_MAX; i++) {
+		_jc_method *cons;
+		jobject ref;
+
+		/* Get no-arg constructor */
+		if ((cons = _jc_get_declared_method(env, vm->boot.types.vmex[i],
+		    "<init>", "()V", _JC_ACC_STATIC, 0)) == NULL) {
+			_jc_post_exception_info(env);
+			goto fail;
+		}
+
+		/* Create object */
+		if ((vm->boot.objects.vmex[i] = _jc_new_object(env,
+		    vm->boot.types.vmex[i])) == NULL)
+			goto fail;
+
+		/* Wrap it in a global native reference */
+		if ((ref = _jc_new_global_native_ref(env,
+		    vm->boot.objects.vmex[i])) == NULL) {
+			_jc_post_exception_info(env);
+			goto fail;
+		}
+
+		/* Invoke the no-arg constructor */
+		if (_jc_invoke_nonvirtual(env, cons, *ref) != JNI_OK) {
+			_jc_free_global_native_ref(&ref);
+			goto fail;
+		}
+	}
+
+	/* Done */
+	return JNI_OK;
+
+fail:
+	/* Handle failure case */
+	return JNI_ERR;
+}
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/c_support.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/c_support.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/c_support.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/c_support.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,429 @@
+
+/*
+ * Copyright 2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * $Id: c_support.c,v 1.10 2005/05/14 21:58:24 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/*
+ * This file contains all C functions called directly by the generated
+ * C code. These functions are special because they are allowed to throw
+ * exceptions, whereas most other functions in JC indicate exceptions by
+ * posting them and returning an error.
+ */
+
+/*
+ * Return the (possibly newly created) intern'ed String object
+ * containing the supplied characters which are encoded in UTF-8.
+ *
+ * The caller may optionally pass a non-NULL pointer "ref" to a
+ * cache location for the result; if "*ref" is not null, it is
+ * returned; otherwise, it is set to point to the intern'd String
+ * object and an additional implicit reference is added to the
+ * class loader that defined the class whose method is calling
+ * this function to represent the cached reference.
+ */
+_jc_object * _JC_JCNI_ATTR
+_jc_cs_intern_string_utf8(_jc_env *env, _jc_object **ref, const char *utf)
+{
+	_jc_object *string;
+
+	/* Does caller already have the reference cached? */
+	if (ref != NULL && *ref != NULL)
+		return *ref;
+
+	/* Create/get intern'd string */
+	if ((string = _jc_new_intern_string(env, utf, strlen(utf))) == NULL)
+		_jc_throw_exception(env);
+
+	/* If cache provided, cache reference and add to implicit references */
+	if (ref != NULL) {
+		_jc_jvm *const vm = env->vm;
+		_jc_stack_crawl crawl;
+		_jc_resolve_info info;
+
+		/* Create reference list with one reference */
+		memset(&info, 0, sizeof(info));
+		info.implicit_refs = &string;
+		info.num_implicit_refs = 1;
+
+		/* Lock VM */
+		_JC_MUTEX_LOCK(env, vm->mutex);
+
+		/* Find which Java method called me and get its loader */
+		_jc_stack_crawl_first(env, &crawl);
+		_JC_ASSERT(crawl.method != NULL && crawl.method->class != NULL);
+
+		/* Unlock VM */
+		_JC_MUTEX_UNLOCK(env, vm->mutex);
+
+		/* Associate reference with calling class' loader */
+		info.loader = crawl.method->class->loader;
+
+		/* Add implicit reference from class loader -> String */
+		if (_jc_merge_implicit_refs(env, &info) != JNI_OK) {
+			_jc_post_exception_info(env);
+			_jc_throw_exception(env);
+		}
+
+		/* Cache reference in caller-supplied cache location */
+		*ref = string;
+	}
+
+	/* Done */
+	return string;
+}
+
+/*
+ * Create a new non-array object instance.
+ */
+_jc_object * _JC_JCNI_ATTR
+_jc_cs_new_object(_jc_env *env, _jc_type *type)
+{
+	_jc_object *obj;
+
+	/* Create object */
+	if ((obj = _jc_new_object(env, type)) == NULL)
+		_jc_throw_exception(env);
+
+	/* Done */
+	return obj;
+}
+
+/*
+ * Initialize a stack-allocated non-array object instance.
+ */
+_jc_object * _JC_JCNI_ATTR
+_jc_cs_init_object(_jc_env *env, void *mem, _jc_type *type)
+{
+	_jc_object *obj = mem;
+
+	/* Initialize object */
+	if ((obj = _jc_init_object(env, mem, type)) == NULL)
+		_jc_throw_exception(env);
+
+	/* Done */
+	return obj;
+}
+
+/*
+ * Create a new array instance.
+ */
+_jc_array * _JC_JCNI_ATTR
+_jc_cs_new_array(_jc_env *env, _jc_type *type, jint len)
+{
+	_jc_array *array;
+
+	/* Create array */
+	if ((array = _jc_new_array(env, type, len)) == NULL)
+		_jc_throw_exception(env);
+
+	/* Done */
+	return array;
+}
+
+/*
+ * Initialize a stack-allocated array.
+ */
+_jc_array * _JC_JCNI_ATTR
+_jc_cs_init_array(_jc_env *env, void *mem, _jc_type *type, jint len)
+{
+	_jc_array *array;
+
+	/* Initialize array */
+	if ((array = _jc_init_array(env, mem, type, len)) == NULL)
+		_jc_throw_exception(env);
+
+	/* Done */
+	return array;
+}
+
+/*
+ * Create a new multi-dimensional array instance.
+ */
+_jc_array * _JC_JCNI_ATTR
+_jc_cs_new_multiarray(_jc_env *env, _jc_type *type,
+	jint num_sizes, const jint *sizes)
+{
+	_jc_array *array;
+
+	/* Create array */
+	if ((array = _jc_new_multiarray(env, type, num_sizes, sizes)) == NULL)
+		_jc_throw_exception(env);
+
+	/* Done */
+	return array;
+}
+
+/*
+ * Initialize a stack-allocated multi-dimensional array instance.
+ */
+_jc_array * _JC_JCNI_ATTR
+_jc_cs_init_multiarray(_jc_env *env, void *mem, _jc_type *type,
+	jint num_sizes, const jint *sizes)
+{
+	_jc_array *array;
+
+	/* Initialize array */
+	if ((array = _jc_init_multiarray(env,
+	    mem, type, num_sizes, sizes)) == NULL)
+		_jc_throw_exception(env);
+
+	/* Done */
+	return array;
+}
+
+/*
+ * Perform class initialization on type.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_initialize_type(_jc_env *env, _jc_type *type)
+{
+	/* Initialize type */
+	if (_jc_initialize_type(env, type) != JNI_OK)
+		_jc_throw_exception(env);
+}
+
+/*
+ * Determine if 'obj' is an instance of type 'type'.
+ */
+jboolean _JC_JCNI_ATTR
+_jc_cs_instanceof(_jc_env *env, _jc_object *obj, _jc_type *type)
+{
+	/* Sanity check */
+	_JC_ASSERT(obj == NULL || _JC_LW_TEST(obj->lockword, ODD));
+
+	/* Compute instanceofness */
+	switch (_jc_instance_of(env, obj, type)) {
+	case 0:
+		return JNI_FALSE;
+	case 1:
+		return JNI_TRUE;
+	case -1:
+		_jc_throw_exception(env);
+	default:
+		_JC_ASSERT(JNI_FALSE);
+		return JNI_FALSE;
+	}
+}
+
+/*
+ * Lookup an interface method.
+ */
+const void * _JC_JCNI_ATTR
+_jc_cs_lookup_interface(_jc_env *env, _jc_object *obj, jlong sig_hash)
+{
+	_jc_type *const type = obj->type;
+	_jc_method *const *methodp;
+	int bucket;
+
+	/* Sanity check */
+	_JC_ASSERT(obj != NULL && _JC_LW_TEST(obj->lockword, ODD));
+
+	/* Seach object's interface method hash table */
+	if (type->imethod_hash_table == NULL)
+		goto fail;
+	bucket = (int)sig_hash & (_JC_IMETHOD_HASHSIZE - 1);
+	methodp = type->imethod_hash_table[bucket];
+	if (methodp == NULL)
+		goto fail;
+	while (*methodp != NULL) {
+		if ((*methodp)->signature_hash == sig_hash)
+			return (*methodp)->function;
+		methodp++;
+	}
+
+fail:
+	/* Not found; throw exception */
+	_jc_post_exception(env, _JC_AbstractMethodError);
+	_jc_throw_exception(env);
+}
+
+/*
+ * Invoke a native method.
+ *
+ * This function is called from the method "wrapper" function for
+ * a native method to invoke the actual native implementation function.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_invoke_native_method(_jc_env *env,
+	_jc_method *method, _jc_value *retval, ...)
+{
+	va_list args;
+	jint status;
+
+	/* Sanity check */
+	_JC_ASSERT(_JC_ACC_TEST(method, NATIVE));
+
+	/* Invoke method */
+	va_start(args, retval);
+	status = _jc_invoke_native_method(env, method, JNI_FALSE, args);
+	va_end(args);
+
+	/* Throw any posted exception */
+	if (status != JNI_OK)
+		_jc_throw_exception(env);
+
+	/* Return return value */
+	*retval = env->retval;
+}
+
+/*
+ * Throw an AbstractMethodError for the given abstract method.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_throw_abstract_method_error(_jc_env *env, _jc_method *method)
+{
+	/* Sanity check */
+	_JC_ASSERT(_JC_ACC_TEST(method, ABSTRACT));
+
+	/* Throw exception */
+	_jc_post_exception_msg(env, _JC_AbstractMethodError,
+	    "%s.%s%s", method->class->name, method->name, method->signature);
+	_jc_throw_exception(env);
+}
+
+/*
+ * Enter object monitor.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_monitorenter(_jc_env *env, _jc_object *obj)
+{
+	/* Check for NULL */
+	if (obj == NULL) {
+		_jc_post_exception(env, _JC_NullPointerException);
+		_jc_throw_exception(env);
+	}
+
+	/* Sanity check */
+	_JC_ASSERT(_JC_LW_TEST(obj->lockword, ODD));
+
+	/* Enter object monitor */
+	if (_jc_lock_object(env, obj) != JNI_OK)
+		_jc_throw_exception(env);
+}
+
+/*
+ * Exit object monitor.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_monitorexit(_jc_env *env, _jc_object *obj)
+{
+	/* Check for NULL */
+	if (obj == NULL) {
+		_jc_post_exception(env, _JC_NullPointerException);
+		_jc_throw_exception(env);
+	}
+
+	/* Sanity check */
+	_JC_ASSERT(_JC_LW_TEST(obj->lockword, ODD));
+
+	/* Exit object monitor */
+	if (_jc_unlock_object(env, obj) != JNI_OK)
+		_jc_throw_exception(env);
+}
+
+/*
+ * Throw an ArrayIndexOutOfBoundsException.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_throw_array_index_exception(_jc_env *env, jint indx)
+{
+	_jc_post_exception_msg(env,
+	    _JC_ArrayIndexOutOfBoundsException, "%d", (int)indx);
+	_jc_throw_exception(env);
+}
+
+/*
+ * Throw an ArrayStoreException.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_throw_array_store_exception(_jc_env *env,
+	_jc_object *obj, _jc_type *type)
+{
+	_jc_post_exception_msg(env, _JC_ArrayStoreException,
+	    "can't store object of type `%s' into array of `%s'",
+	    obj->type->name, type->name);
+	_jc_throw_exception(env);
+}
+
+/*
+ * Throw a ClassCastException.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_throw_class_cast_exception(_jc_env *env, _jc_object *obj, _jc_type *type)
+{
+	_JC_ASSERT(obj != NULL && _JC_LW_TEST(obj->lockword, ODD));
+	_jc_post_exception_msg(env, _JC_ClassCastException,
+	    "can't cast `%s' to `%s'", obj->type->name, type->name);
+	_jc_throw_exception(env);
+}
+
+/*
+ * Throw a NullPointerException.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_throw_null_pointer_exception(_jc_env *env)
+{
+	_jc_post_exception(env, _JC_NullPointerException);
+	_jc_throw_exception(env);
+}
+
+/*
+ * Throw an exception.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_throw(_jc_env *env, _jc_object *obj)
+{
+	/* Check for NULL */
+	if (obj == NULL) {
+		_jc_post_exception(env, _JC_NullPointerException);
+		_jc_throw_exception(env);
+	}
+
+	/* Sanity check */
+	_JC_ASSERT(_jc_subclass_of(obj, env->vm->boot.types.Throwable));
+
+	/* Throw exception */
+	_jc_post_exception_object(env, obj);
+	_jc_throw_exception(env);
+}
+
+/*
+ * Panic.
+ */
+void _JC_JCNI_ATTR
+_jc_cs_panic(_jc_env *env, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	_jc_fatal_error_v(env->vm, fmt, args);
+	va_end(args);
+}
+
+/*
+ * Compute the floating point remainder.
+ */
+jdouble _JC_JCNI_ATTR
+_jc_cs_fmod(jdouble x, jdouble y)
+{
+	return fmod(x, y);		// XXX corner cases are probably wrong
+}
+
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cf_parse.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cf_parse.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cf_parse.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cf_parse.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,2017 @@
+
+/*
+ * Copyright 2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * $Id: cf_parse.c,v 1.10 2005/03/12 23:51:00 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/* Internal functions */
+static int	_jc_parse_cpool(_jc_cf_parse_state *s, _jc_classfile *cfile);
+static int	_jc_parse_constant(_jc_cf_parse_state *s, _jc_cf_constant *cp);
+static int	_jc_parse_field(_jc_cf_parse_state *s, _jc_cf_field *field);
+static int	_jc_parse_method(_jc_cf_parse_state *s, _jc_cf_method *method);
+static int	_jc_parse_attribute(_jc_cf_parse_state *s, _jc_cf_attr *attr);
+static int	_jc_parse_inner_class(_jc_cf_parse_state *s,
+			_jc_cf_inner_class *inner);
+static int	_jc_parse_class(_jc_cf_parse_state *s, const char **classp,
+			int optional);
+static int	_jc_parse_fieldref(_jc_cf_parse_state *s, _jc_cf_ref **refp);
+static int	_jc_parse_methodref(_jc_cf_parse_state *s, _jc_cf_ref **refp);
+static int	_jc_parse_interfacemethodref(_jc_cf_parse_state *s,
+			_jc_cf_ref **refp);
+static int	_jc_parse_bytecode(_jc_cf_parse_state *s, _jc_cf_code *code,
+			uint32_t *offset_map, uint32_t length);
+static int	_jc_map_offset(_jc_env *env, _jc_cf_code *code, uint32_t length,
+			jint *offset_map, jint *targetp);
+static int	_jc_parse_local8(_jc_cf_parse_state *s, _jc_cf_code *code,
+			uint16_t *indexp);
+static int	_jc_parse_local16(_jc_cf_parse_state *s, _jc_cf_code *code,
+			uint16_t *indexp);
+static int	_jc_parse_cpool_index8(_jc_cf_parse_state *s, int types,
+			_jc_cf_constant **ptr, int optional);
+static int	_jc_parse_cpool_index16(_jc_cf_parse_state *s, int types,
+			_jc_cf_constant **ptr, int optional);
+static int	_jc_parse_cpool_index(_jc_cf_parse_state *s, int types,
+			_jc_cf_constant **ptr, uint16_t index);
+static int	_jc_parse_string(_jc_cf_parse_state *s, const char **utfp,
+			int optional);
+static int	_jc_parse_integer(_jc_cf_parse_state *s, jint *value);
+static int	_jc_parse_float(_jc_cf_parse_state *s, jfloat *valuep);
+static int	_jc_parse_long(_jc_cf_parse_state *s, jlong *valuep);
+static int	_jc_parse_double(_jc_cf_parse_state *s, jdouble *valuep);
+static int	_jc_parse_utf8(_jc_cf_parse_state *s, const u_char **utfp,
+			uint16_t *lengthp);
+static int	_jc_parse_uint32(_jc_cf_parse_state *s, uint32_t *valuep);
+static int	_jc_parse_uint16(_jc_cf_parse_state *s, uint16_t *valuep);
+static int	_jc_parse_uint8(_jc_cf_parse_state *s, uint8_t *valuep);
+static int	_jc_scan_constant(_jc_cf_parse_state *s, size_t *lenp);
+static void	_jc_free_attribute(_jc_cf_attr *attr);
+static void	_jc_sub_state(_jc_cf_parse_state *s, _jc_cf_parse_state *t,
+			size_t length);
+static int	_jc_field_sorter(const void *item1, const void *item2);
+static int	_jc_method_sorter(const void *item1, const void *item2);
+
+/*
+ * Parse a class file and do some basic validation checks.
+ *
+ * 'howmuch' determines how much to parse:
+ *
+ *	0	Just enough to get class name
+ *	1	Class name, superclass and superinterfaces
+ *	2	The whole thing
+ *
+ * If unsuccessful an exception is stored.
+ */
+_jc_classfile *
+_jc_parse_classfile(_jc_env *env, _jc_classbytes *bytes, int howmuch)
+{
+	_jc_classfile *cfile;
+	_jc_cf_parse_state s;
+	uint32_t magic;
+	int i;
+
+	/* Initialize parse state */
+	memset(&s, 0, sizeof(s));
+	s.env = env;
+	s.bytes = bytes->bytes;
+	s.length = bytes->length;
+
+	/* Create new classfile object */
+	if ((cfile = _jc_vm_zalloc(env, sizeof(*cfile))) == NULL)
+		goto fail;
+	s.cfile = cfile;
+
+	/* Parse initial stuff */
+	if (_jc_parse_uint32(&s, &magic) != JNI_OK)
+		goto fail;
+	if (magic != 0xcafebabe) {
+		_JC_EX_STORE(env, ClassFormatError,
+		    "invalid magic number 0x%08x != 0x%08x", magic, 0xcafebabe);
+		goto fail;
+	}
+	if (_jc_parse_uint16(&s, &cfile->minor_version) != JNI_OK)
+		goto fail;
+	if (_jc_parse_uint16(&s, &cfile->major_version) != JNI_OK)
+		goto fail;
+	if (!((cfile->major_version == 45 && cfile->minor_version >= 3)
+	    || (cfile->major_version >= 46 && cfile->major_version <= 48))) {
+		_JC_EX_STORE(env, UnsupportedClassVersionError,
+		    "%u.%u", cfile->major_version, cfile->minor_version);
+		goto fail;
+	}
+
+	/* Parse constant pool */
+	if (_jc_parse_cpool(&s, cfile) != JNI_OK)
+		goto fail;
+
+	/* Get access flags and name */
+	if (_jc_parse_uint16(&s, &cfile->access_flags) != JNI_OK)
+		goto fail;
+	if (_jc_parse_class(&s, &cfile->name, JNI_FALSE) != JNI_OK)
+		goto fail;
+
+	/* Check stuff */
+	if (*cfile->name == '[' || strchr(cfile->name, '.') != NULL) {
+		_JC_EX_STORE(env, ClassFormatError,
+		    "invalid class name `%s'", cfile->name);
+		goto fail;
+	}
+	if (_JC_ACC_TEST(cfile, INTERFACE)) {
+		/*
+		 * Note: _JC_ACC_SUPER should not be allowed for interfaces
+		 * (JVMS 4.1) but we allow it here because jikes 1.15 sets it.
+		 */
+		if ((cfile->access_flags & ~(_JC_ACC_PUBLIC|_JC_ACC_SUPER))
+		    != (_JC_ACC_INTERFACE|_JC_ACC_ABSTRACT)) {
+			_JC_EX_STORE(env, ClassFormatError,
+			    "invalid interface access flags 0x%04x",
+			    cfile->access_flags);
+			goto fail;
+		}
+	} else {
+		if (_JC_ACC_TEST(cfile, FINAL)
+		    && _JC_ACC_TEST(cfile, ABSTRACT)) {
+			_JC_EX_STORE(env, ClassFormatError,
+			    "invalid class access flags 0x%04x",
+			    cfile->access_flags);
+			goto fail;
+		}
+	}
+
+	/* Check for partial parsing */
+	if (howmuch <= 0)
+		return cfile;
+
+	/* Get superclass; special case java/lang/Object */
+	if (strcmp(cfile->name, "java/lang/Object") == 0) {
+		uint16_t cp_index;
+
+		if (_jc_parse_uint16(&s, &cp_index) != JNI_OK)
+			goto fail;
+		if (cp_index != 0) {
+			_JC_EX_STORE(env, ClassFormatError,
+			    "superclass specified for `%s'", cfile->name);
+			goto fail;
+		}
+		if ((cfile->access_flags & (_JC_ACC_PUBLIC|_JC_ACC_ABSTRACT
+		    |_JC_ACC_INTERFACE|_JC_ACC_FINAL)) != _JC_ACC_PUBLIC) {
+			_JC_EX_STORE(env, ClassFormatError,
+			    "invalid class access flags 0x%04x for `%s'",
+			    cfile->access_flags, cfile->name);
+			goto fail;
+		}
+	} else {
+		if (_jc_parse_class(&s,
+		    &cfile->superclass, JNI_FALSE) != JNI_OK)
+			goto fail;
+		if (*cfile->superclass == '[') {
+			_JC_EX_STORE(env, ClassFormatError,
+			    "invalid superclass `%s'", cfile->superclass);
+			goto fail;
+		}
+	}
+
+	/* Parse interfaces */
+	if (_jc_parse_uint16(&s, &cfile->num_interfaces) != JNI_OK)
+		goto fail;
+	if (cfile->num_interfaces > 0
+	    && (cfile->interfaces = _jc_vm_zalloc(env, cfile->num_interfaces
+	      * sizeof(*cfile->interfaces))) == NULL)
+		goto fail;
+	for (i = 0; i < cfile->num_interfaces; i++) {
+		if (_jc_parse_class(&s,
+		    &cfile->interfaces[i], JNI_FALSE) != JNI_OK)
+			goto fail;
+		if (*cfile->interfaces[i] == '[') {
+			_JC_EX_STORE(env, ClassFormatError,
+			    "invalid superinterface `%s'",
+			    cfile->interfaces[i]);
+			goto fail;
+		}
+	}
+
+	/* Check for partial parsing */
+	if (howmuch <= 1)
+		return cfile;
+
+	/* Parse fields */
+	if (_jc_parse_uint16(&s, &cfile->num_fields) != JNI_OK)
+		goto fail;
+	if (cfile->num_fields > 0
+	    && (cfile->fields = _jc_vm_zalloc(env, cfile->num_fields
+	      * sizeof(*cfile->fields))) == NULL)
+		goto fail;
+	for (i = 0; i < cfile->num_fields; i++) {
+		if (_jc_parse_field(&s, &cfile->fields[i]) != JNI_OK)
+			goto fail;
+	}
+
+	/* Sort fields */
+	qsort(cfile->fields, cfile->num_fields,
+	    sizeof(*cfile->fields), _jc_field_sorter);
+
+	/* Parse methods */
+	if (_jc_parse_uint16(&s, &cfile->num_methods) != JNI_OK)
+		goto fail;
+	if (cfile->num_methods > 0
+	    && (cfile->methods = _jc_vm_zalloc(env, cfile->num_methods
+	      * sizeof(*cfile->methods))) == NULL)
+		goto fail;
+	for (i = 0; i < cfile->num_methods; i++) {
+		if (_jc_parse_method(&s, &cfile->methods[i]) != JNI_OK)
+			goto fail;
+	}
+
+	/* Sort methods */
+	qsort(cfile->methods, cfile->num_methods,
+	    sizeof(*cfile->methods), _jc_method_sorter);
+
+	/* Parse attributes */
+	if (_jc_parse_uint16(&s, &cfile->num_attributes) != JNI_OK)
+		goto fail;
+	if (cfile->num_attributes > 0
+	    && (cfile->attributes = _jc_vm_zalloc(env, cfile->num_attributes
+	      * sizeof(*cfile->attributes))) == NULL)
+		goto fail;
+	for (i = 0; i < cfile->num_attributes; i++) {
+		_jc_cf_attr *const attr = &cfile->attributes[i];
+
+		if (_jc_parse_attribute(&s, attr) != JNI_OK)
+			goto fail;
+		if (strcmp(attr->name, "InnerClasses") == 0)
+			cfile->inner_classes = &attr->u.InnerClasses;
+		else if (strcmp(attr->name, "SourceFile") == 0)
+			cfile->source_file = attr->u.SourceFile;
+	}
+
+	/* Disallow any extra garbage in the class file */
+	if (s.pos != s.length) {
+		_JC_EX_STORE(env, ClassFormatError,
+		    "extra garbage at end of classfile");
+		goto fail;
+	}
+
+	/* Done */
+	return cfile;
+
+fail:
+	/* Clean up after failure */
+	_jc_destroy_classfile(&cfile);
+	return NULL;
+}
+
+static int
+_jc_parse_cpool(_jc_cf_parse_state *s, _jc_classfile *cfile)
+{
+	size_t strings_size;
+	size_t cpool_start;
+	int i;
+
+	/* Get number of constants */
+	if (_jc_parse_uint16(s, &cfile->num_constants) != JNI_OK)
+		goto fail;
+	if (cfile->num_constants == 0) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid constant pool count of zero");
+		goto fail;
+	}
+
+	/* Allocate constants array */
+	if ((cfile->constants = _jc_vm_alloc(s->env,
+	    (cfile->num_constants - 1) * sizeof(*cfile->constants))) == NULL)
+		goto fail;
+	memset(cfile->constants, 0,
+	    (cfile->num_constants - 1) * sizeof(*cfile->constants));
+
+	/* Record constant types and add up UTF-8 string lengths */
+	cpool_start = s->pos;
+	for (strings_size = 0, i = 1; i < cfile->num_constants; i++) {
+		_jc_cf_constant *const constant = &cfile->constants[i - 1];
+		size_t const_size;
+
+		constant->type = s->bytes[s->pos];
+		if (_jc_scan_constant(s, &const_size) != JNI_OK)
+			goto fail;
+		switch (constant->type) {
+		case CONSTANT_Utf8:
+			strings_size += (const_size - 3) + 1;
+			break;
+		case CONSTANT_Long:
+		case CONSTANT_Double:
+			if (++i >= cfile->num_constants) {
+				_JC_EX_STORE(s->env, ClassFormatError,
+				    "long/double constant at last index");
+				goto fail;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* Copy and nul-terminate all UTF-8 strings */
+	if (strings_size > 0
+	    && (cfile->string_mem = _jc_vm_alloc(s->env, strings_size)) == NULL)
+		goto fail;
+	s->pos = cpool_start;
+	for (strings_size = 0, i = 1; i < cfile->num_constants; i++) {
+		_jc_cf_constant *const constant = &cfile->constants[i - 1];
+		const u_char *utf;
+		uint16_t utf_len;
+
+		switch (constant->type) {
+		case CONSTANT_Utf8:
+			s->pos++;
+			if (_jc_parse_utf8(s, &utf, &utf_len) != JNI_OK)
+				goto fail;
+			constant->u.Utf8 = cfile->string_mem + strings_size;
+			memcpy(cfile->string_mem + strings_size, utf, utf_len);
+			cfile->string_mem[strings_size + utf_len] = '\0';
+			strings_size += utf_len + 1;
+			break;
+		case CONSTANT_Long:
+		case CONSTANT_Double:
+			i++;
+			/* FALL THROUGH */
+		default:
+			_jc_scan_constant(s, NULL);
+			break;
+		}
+	}
+
+	/* Parse String and Class constants */
+	s->pos = cpool_start;
+	for (i = 1; i < cfile->num_constants; i++) {
+		_jc_cf_constant *const constant = &cfile->constants[i - 1];
+
+		switch (constant->type) {
+		case CONSTANT_Class:
+		case CONSTANT_String:
+			if (_jc_parse_constant(s, constant) != JNI_OK)
+				goto fail;
+			break;
+		case CONSTANT_Long:
+		case CONSTANT_Double:
+			i++;
+			/* FALL THROUGH */
+		default:
+			_jc_scan_constant(s, NULL);
+			break;
+		}
+	}
+
+	/* Parse NameAndType constants */
+	s->pos = cpool_start;
+	for (i = 1; i < cfile->num_constants; i++) {
+		_jc_cf_constant *const constant = &cfile->constants[i - 1];
+
+		switch (constant->type) {
+		case CONSTANT_NameAndType:
+			if (_jc_parse_constant(s, constant) != JNI_OK)
+				goto fail;
+			break;
+		case CONSTANT_Long:
+		case CONSTANT_Double:
+			i++;
+			/* FALL THROUGH */
+		default:
+			_jc_scan_constant(s, NULL);
+			break;
+		}
+	}
+
+	/* Parse remaining constants */
+	s->pos = cpool_start;
+	for (i = 1; i < cfile->num_constants; i++) {
+		_jc_cf_constant *const constant = &cfile->constants[i - 1];
+
+		switch (constant->type) {
+		case CONSTANT_Utf8:
+		case CONSTANT_Class:
+		case CONSTANT_String:
+		case CONSTANT_NameAndType:
+			_jc_scan_constant(s, NULL);
+			break;
+		case CONSTANT_Long:
+		case CONSTANT_Double:
+			i++;
+			/* FALL THROUGH */
+		default:
+			if (_jc_parse_constant(s, constant) != JNI_OK)
+				goto fail;
+			break;
+		}
+	}
+
+	/* Done */
+	return JNI_OK;
+
+fail:
+	_jc_vm_free(&cfile->string_mem);
+	_jc_vm_free(&cfile->constants);
+	cfile->num_constants = 0;
+	return JNI_ERR;
+}
+
+static int
+_jc_parse_constant(_jc_cf_parse_state *s, _jc_cf_constant *cp)
+{
+	_jc_cf_constant *cp2;
+
+	if (_jc_parse_uint8(s, &cp->type) != JNI_OK)
+		return JNI_ERR;
+	switch (cp->type) {
+	case CONSTANT_Class:
+		if (_jc_parse_cpool_index16(s,
+		    1 << CONSTANT_Utf8, &cp2, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		cp->u.Class = cp2->u.Utf8;
+		break;
+	case CONSTANT_Fieldref:
+	case CONSTANT_Methodref:
+	case CONSTANT_InterfaceMethodref:
+		if (_jc_parse_cpool_index16(s,
+		    1 << CONSTANT_Class, &cp2, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		cp->u.Ref.class = cp2->u.Class;
+		if (_jc_parse_cpool_index16(s,
+		    1 << CONSTANT_NameAndType, &cp2, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		cp->u.Ref.name = cp2->u.NameAndType.name;
+		cp->u.Ref.descriptor = cp2->u.NameAndType.descriptor;
+		break;
+	case CONSTANT_String:
+		if (_jc_parse_cpool_index16(s,
+		    1 << CONSTANT_Utf8, &cp2, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		cp->u.String = cp2->u.Utf8;
+		break;
+	case CONSTANT_Integer:
+		if (_jc_parse_integer(s, &cp->u.Integer) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case CONSTANT_Float:
+		if (_jc_parse_float(s, &cp->u.Float) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case CONSTANT_Long:
+		if (_jc_parse_long(s, &cp->u.Long) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case CONSTANT_Double:
+		if (_jc_parse_double(s, &cp->u.Double) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case CONSTANT_NameAndType:
+		if (_jc_parse_cpool_index16(s,
+		    1 << CONSTANT_Utf8, &cp2, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		cp->u.NameAndType.name = cp2->u.Utf8;
+		if (_jc_parse_cpool_index16(s,
+		    1 << CONSTANT_Utf8, &cp2, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		cp->u.NameAndType.descriptor = cp2->u.Utf8;
+		break;
+	case CONSTANT_Utf8:
+		if (_jc_parse_utf8(s, NULL, NULL) != JNI_OK)
+			return JNI_ERR;
+		break;
+	default:
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid constant pool entry type %u", cp->type);
+		return JNI_ERR;
+	}
+
+	/* Done */
+	return JNI_OK;
+}
+
+static int
+_jc_scan_constant(_jc_cf_parse_state *s, size_t *lenp)
+{
+	size_t length;
+
+	if (s->pos >= s->length) {
+		_JC_EX_STORE(s->env, ClassFormatError, "truncated class file");
+		return JNI_ERR;
+	}
+	switch (s->bytes[s->pos]) {
+	case CONSTANT_Class:
+	case CONSTANT_String:
+		length = 3;
+		break;
+	case CONSTANT_Fieldref:
+	case CONSTANT_Methodref:
+	case CONSTANT_InterfaceMethodref:
+	case CONSTANT_Integer:
+	case CONSTANT_Float:
+	case CONSTANT_NameAndType:
+		length = 5;
+		break;
+	case CONSTANT_Long:
+	case CONSTANT_Double:
+		length = 9;
+		break;
+	case CONSTANT_Utf8:
+	    {
+		uint16_t utf_len;
+
+		s->pos++;
+		if (_jc_parse_utf8(s, NULL, &utf_len) != JNI_OK)
+			return JNI_ERR;
+		if (lenp != NULL)
+			*lenp = 3 + utf_len;
+		return JNI_OK;
+	    }
+	default:
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid constant pool entry type %u", s->bytes[s->pos]);
+		return JNI_ERR;
+	}
+
+	/* Check length overflow */
+	if (s->pos + length > s->length) {
+		_JC_EX_STORE(s->env, ClassFormatError, "truncated class file");
+		return JNI_ERR;
+	}
+
+	/* Done */
+	if (lenp != NULL)
+		*lenp = length;
+	s->pos += length;
+	return JNI_OK;
+}
+
+/*
+ * Destroy a class file structure.
+ */
+void
+_jc_destroy_classfile(_jc_classfile **cfilep)
+{
+	_jc_classfile *cfile = *cfilep;
+	int i;
+
+	/* Sanity check */
+	if (cfile == NULL)
+		return;
+	*cfilep = NULL;
+
+	/* Free up resources */
+	_jc_vm_free(&cfile->interfaces);
+	for (i = 0; i < cfile->num_fields; i++) {
+		_jc_cf_field *const field = &cfile->fields[i];
+
+		while (field->num_attributes > 0) {
+			_jc_free_attribute(&field->attributes[
+			    --field->num_attributes]);
+		}
+		_jc_vm_free(&field->attributes);
+	}
+	_jc_vm_free(&cfile->fields);
+	for (i = 0; i < cfile->num_methods; i++) {
+		_jc_cf_method *const method = &cfile->methods[i];
+
+		while (method->num_attributes > 0) {
+			_jc_free_attribute(&method->attributes[
+			    --method->num_attributes]);
+		}
+		_jc_vm_free(&method->attributes);
+	}
+	_jc_vm_free(&cfile->methods);
+	while (cfile->num_attributes > 0)
+		_jc_free_attribute(&cfile->attributes[--cfile->num_attributes]);
+	_jc_vm_free(&cfile->attributes);
+	_jc_vm_free(&cfile->string_mem);
+	_jc_vm_free(&cfile->constants);
+	_jc_vm_free(&cfile);
+}
+
+static void
+_jc_free_attribute(_jc_cf_attr *attr)
+{
+	if (strcmp(attr->name, "Exceptions") == 0)
+		_jc_vm_free(&attr->u.Exceptions.exceptions);
+	else if (strcmp(attr->name, "InnerClasses") == 0)
+		_jc_vm_free(&attr->u.InnerClasses.classes);
+	else if (strcmp(attr->name, "LineNumberTable") == 0)
+		_jc_vm_free(&attr->u.LineNumberTable.linenums);
+	else if (strcmp(attr->name, "Code") == 0)
+		_jc_vm_free(&attr->u.Code.bytecode);
+	memset(attr, 0, sizeof(*attr));
+}
+
+static int
+_jc_parse_field(_jc_cf_parse_state *s, _jc_cf_field *field)
+{
+	int i;
+
+	/* Parse the field */
+	if (_jc_parse_uint16(s, &field->access_flags) != JNI_OK)
+		goto fail;
+	if (_jc_parse_string(s, &field->name, JNI_FALSE) != JNI_OK)
+		goto fail;
+	if (_jc_parse_string(s, &field->descriptor, JNI_FALSE) != JNI_OK)
+		goto fail;
+	if (_jc_parse_uint16(s, &field->num_attributes) != JNI_OK)
+		goto fail;
+	if (field->num_attributes > 0
+	    && (field->attributes = _jc_vm_zalloc(s->env,
+	      field->num_attributes * sizeof(*field->attributes))) == NULL)
+		goto fail;
+	for (i = 0; i < field->num_attributes; i++) {
+		_jc_cf_attr *const attr = &field->attributes[i];
+
+		if (_jc_parse_attribute(s, attr) != JNI_OK)
+			goto fail;
+		if (strcmp(attr->name, "ConstantValue") == 0)
+			field->initial_value = attr->u.ConstantValue;
+	}
+
+	/* Check stuff */
+	if (_JC_ACC_TEST(field, PRIVATE)
+	    + _JC_ACC_TEST(field, PROTECTED)
+	    + _JC_ACC_TEST(field, PUBLIC) > 1) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid access flags 0x%04x for field `%s'",
+		    field->access_flags, field->name);
+		goto fail;
+	}
+	if ((field->access_flags & (_JC_ACC_FINAL|_JC_ACC_VOLATILE))
+	    == (_JC_ACC_FINAL|_JC_ACC_VOLATILE)) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid access flags 0x%04x for field `%s'",
+		    field->access_flags, field->name);
+		goto fail;
+	}
+	if (_JC_ACC_TEST(s->cfile, INTERFACE)
+	    && field->access_flags
+	      != (_JC_ACC_PUBLIC|_JC_ACC_STATIC|_JC_ACC_FINAL)) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid access flags 0x%04x for interface field `%s'",
+		    field->access_flags, field->name);
+		goto fail;
+	}
+
+	/* Check "ConstantValue" attributes */
+	for (i = 0; i < field->num_attributes; i++) {
+		_jc_cf_attr *const attr = &field->attributes[i];
+
+		if (strcmp(attr->name, "ConstantValue") == 0) {
+			switch (attr->u.ConstantValue->type) {
+			case CONSTANT_Integer:
+				if (strcmp(field->descriptor, "Z") == 0
+				    || strcmp(field->descriptor, "B") == 0
+				    || strcmp(field->descriptor, "C") == 0
+				    || strcmp(field->descriptor, "S") == 0
+				    || strcmp(field->descriptor, "I") == 0)
+					continue;
+				break;
+			case CONSTANT_Long:
+				if (strcmp(field->descriptor, "J") == 0)
+					continue;
+				break;
+			case CONSTANT_Float:
+				if (strcmp(field->descriptor, "F") == 0)
+					continue;
+				break;
+			case CONSTANT_Double:
+				if (strcmp(field->descriptor, "D") == 0)
+					continue;
+				break;
+			case CONSTANT_String:
+				if (strcmp(field->descriptor,
+				    "Ljava/lang/String;") == 0)
+					continue;
+				break;
+			default:
+				break;
+			}
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "mismatched type for `%s' attribute of field `%s'",
+			    "ConstantValue", field->name);
+			goto fail;
+		}
+	}
+
+	/* Done */
+	return JNI_OK;
+
+fail:
+	/* Clean up after failure */
+	_jc_vm_free(&field->attributes);
+	memset(field, 0, sizeof(*field));
+	return JNI_ERR;
+}
+
+static int
+_jc_parse_method(_jc_cf_parse_state *s, _jc_cf_method *method)
+{
+	int i;
+
+	/* Parse the method */
+	if (_jc_parse_uint16(s, &method->access_flags) != JNI_OK)
+		goto fail;
+	if (_jc_parse_string(s, &method->name, JNI_FALSE) != JNI_OK)
+		goto fail;
+	if (_jc_parse_string(s, &method->descriptor, JNI_FALSE) != JNI_OK)
+		goto fail;
+	if (_jc_parse_uint16(s, &method->num_attributes) != JNI_OK)
+		goto fail;
+	if (method->num_attributes > 0
+	    && (method->attributes = _jc_vm_zalloc(s->env,
+	      method->num_attributes * sizeof(*method->attributes))) == NULL)
+		goto fail;
+	for (i = 0; i < method->num_attributes; i++) {
+		_jc_cf_attr *const attr = &method->attributes[i];
+
+		if (_jc_parse_attribute(s, attr) != JNI_OK)
+			goto fail;
+		if (strcmp(attr->name, "Code") == 0) {
+			if (method->code != NULL) {
+				_JC_EX_STORE(s->env, ClassFormatError,
+				    "multiple `%s' attributes for method"
+				    " `%s%s'", attr->name, method->name,
+				    method->descriptor);
+				goto fail;
+			}
+			method->code = &attr->u.Code;
+		} else if (strcmp(attr->name, "Exceptions") == 0)
+			method->exceptions = &attr->u.Exceptions;
+	}
+	if ((_JC_ACC_TEST(method, NATIVE) || _JC_ACC_TEST(method, ABSTRACT))
+	    != (method->code == NULL)) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "%sconcrete method `%s%s' %s a `Code' attribute",
+		    method->code != NULL ? "non-" : "", method->name,
+		    method->descriptor, method->code == NULL ?
+		      "contains" : "is missing");
+		goto fail;
+	}
+
+	/* Check stuff */
+	if (strcmp(method->name, "<init>") == 0
+	    && (method->access_flags & ~(_JC_ACC_PRIVATE|_JC_ACC_PROTECTED
+	      |_JC_ACC_PUBLIC|_JC_ACC_STRICT)) != 0) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid access flags 0x%04x for method `%s%s'",
+		    method->access_flags, method->name, method->descriptor);
+		goto fail;
+	}
+	if (strcmp(method->name, "<clinit>") == 0
+	    && !_JC_ACC_TEST(method, STATIC)) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid access flags 0x%04x for method `%s%s'",
+		    method->access_flags, method->name, method->descriptor);
+		goto fail;
+	}
+
+	if (_JC_ACC_TEST(method, PRIVATE)
+	    + _JC_ACC_TEST(method, PROTECTED)
+	    + _JC_ACC_TEST(method, PUBLIC) > 1) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid access flags 0x%04x for method `%s%s'",
+		    method->access_flags, method->name, method->descriptor);
+		goto fail;
+	}
+	if (_JC_ACC_TEST(method, ABSTRACT)
+	    && (method->access_flags & (_JC_ACC_FINAL|_JC_ACC_NATIVE
+	      |_JC_ACC_PRIVATE|_JC_ACC_STATIC|_JC_ACC_STRICT
+	      |_JC_ACC_SYNCHRONIZED)) != 0) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid access flags 0x%04x for method `%s%s'",
+		    method->access_flags, method->name, method->descriptor);
+		goto fail;
+	}
+	if (_JC_ACC_TEST(s->cfile, INTERFACE)
+	    && strcmp(method->name, "<clinit>") != 0
+	    && method->access_flags != (_JC_ACC_ABSTRACT|_JC_ACC_PUBLIC)) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid access flags 0x%04x for interface method `%s%s'",
+		    method->access_flags, method->name, method->descriptor);
+		goto fail;
+	}
+
+	/* Done */
+	return JNI_OK;
+
+fail:
+	/* Clean up after failure */
+	_jc_vm_free(&method->attributes);
+	memset(method, 0, sizeof(*method));
+	return JNI_ERR;
+}
+
+static int
+_jc_parse_attribute(_jc_cf_parse_state *s, _jc_cf_attr *attr)
+{
+	_jc_cf_parse_state t;
+
+	if (_jc_parse_string(s, &attr->name, JNI_FALSE) != JNI_OK)
+		return JNI_ERR;
+	if (_jc_parse_uint32(s, &attr->length) != JNI_OK)
+		return JNI_ERR;
+	if (s->pos + attr->length > s->length) {
+		_JC_EX_STORE(s->env, ClassFormatError, "truncated class file");
+		return JNI_ERR;
+	}
+
+	/* Initialize parsing of attribute */
+	_jc_sub_state(s, &t, attr->length);
+	s->pos += attr->length;
+
+	/* Further parse individual attributes */
+	if (strcmp(attr->name, "ConstantValue") == 0) {
+		_jc_cf_constant *cp;
+
+		if (attr->length != 2) {
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "invalid `%s' attribute length %u != %u",
+			    attr->name, attr->length, 2);
+			return JNI_ERR;
+		}
+		if (_jc_parse_cpool_index16(&t,
+		    (1 << CONSTANT_Long)
+		      | (1 << CONSTANT_Float)
+		      | (1 << CONSTANT_Double)
+		      | (1 << CONSTANT_Integer)
+		      | (1 << CONSTANT_String),
+		    &cp, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		attr->u.ConstantValue = cp;
+	} else if (strcmp(attr->name, "SourceFile") == 0) {
+		if (attr->length != 2) {
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "invalid `%s' attribute length %u != %u",
+			    attr->name, attr->length, 2);
+			return JNI_ERR;
+		}
+		if (_jc_parse_string(&t,
+		    &attr->u.SourceFile, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+	} else if (strcmp(attr->name, "Exceptions") == 0) {
+		_jc_cf_exceptions *const etab = &attr->u.Exceptions;
+		int i;
+
+		if (_jc_parse_uint16(&t,
+		    &etab->num_exceptions) != JNI_OK)
+			return JNI_ERR;
+		if (attr->length != 2 + 2 * etab->num_exceptions) {
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "invalid `%s' attribute length %u != %u",
+			    attr->name, attr->length,
+			    2 + 2 * etab->num_exceptions);
+			return JNI_ERR;
+		}
+		if ((etab->exceptions = _jc_vm_zalloc(s->env,
+		    etab->num_exceptions * sizeof(*etab->exceptions))) == NULL)
+			return JNI_ERR;
+		for (i = 0; i < etab->num_exceptions; i++) {
+			if (_jc_parse_class(&t,
+			    &etab->exceptions[i], JNI_FALSE) != JNI_OK)
+				return JNI_ERR;
+		}
+	} else if (strcmp(attr->name, "InnerClasses") == 0) {
+		_jc_cf_inner_classes *const itab = &attr->u.InnerClasses;
+		int i;
+
+		if (_jc_parse_uint16(&t, &itab->num_classes) != JNI_OK)
+			return JNI_ERR;
+		if (attr->length != 2 + 8 * itab->num_classes) {
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "invalid `%s' attribute length %u != %u",
+			    attr->name, attr->length,
+			    2 + 8 * itab->num_classes);
+			return JNI_ERR;
+		}
+		if ((itab->classes = _jc_vm_zalloc(s->env,
+		    itab->num_classes * sizeof(*itab->classes))) == NULL)
+			return JNI_ERR;
+		for (i = 0; i < itab->num_classes; i++) {
+			if (_jc_parse_inner_class(&t,
+			    &itab->classes[i]) != JNI_OK)
+				return JNI_ERR;
+		}
+	} else if (strcmp(attr->name, "Code") == 0) {
+		_jc_cf_bytecode *const code = &attr->u.Code;
+
+		if ((code->bytecode = _jc_vm_alloc(s->env, t.length)) == NULL)
+			return JNI_ERR;
+		memcpy(code->bytecode, t.bytes, t.length);
+		code->length = t.length;
+	} else if (strcmp(attr->name, "LineNumberTable") == 0) {
+		_jc_cf_linenums *const ltab = &attr->u.LineNumberTable;
+		int i;
+
+		if (_jc_parse_uint16(&t, &ltab->length) != JNI_OK)
+			return JNI_ERR;
+		if ((ltab->linenums = _jc_vm_alloc(s->env,
+		    ltab->length * sizeof(*ltab->linenums))) == NULL)
+			return JNI_ERR;
+		for (i = 0; i < ltab->length; i++) {
+			_jc_cf_linenum *const lnum = &ltab->linenums[i];
+
+			if (_jc_parse_uint16(&t, &lnum->offset) != JNI_OK)
+				return JNI_ERR;
+			if (_jc_parse_uint16(&t, &lnum->line) != JNI_OK)
+				return JNI_ERR;
+		}
+	}
+
+	/* Ignore unknown attributes */
+	return JNI_OK;
+}
+
+static int
+_jc_parse_inner_class(_jc_cf_parse_state *s, _jc_cf_inner_class *inner)
+{
+	if (_jc_parse_class(s, &inner->inner, JNI_TRUE) != JNI_OK)
+		return JNI_ERR;
+	if (_jc_parse_class(s, &inner->outer, JNI_TRUE) != JNI_OK)
+		return JNI_ERR;
+	if (_jc_parse_string(s, &inner->name, JNI_TRUE) != JNI_OK)
+		return JNI_ERR;
+	if (_jc_parse_uint16(s, &inner->access_flags) != JNI_OK)
+		return JNI_ERR;
+	return JNI_OK;
+}
+
+/*
+ * Parse a "Code" attribute, including bytecode.
+ *
+ * The parsed code remains valid only as long as "cfile" does.
+ *
+ * Stores an exception on failure.
+ */
+int
+_jc_parse_code(_jc_env *env, _jc_classfile *cfile,
+	_jc_cf_bytecode *bytecode, _jc_cf_code *code)
+{
+	_jc_cf_parse_state state;
+	_jc_cf_parse_state *const s = &state;
+	uint32_t *offset_map = NULL;
+#if 0
+	_jc_cf_insn *new_insns;
+#endif
+	uint32_t code_length;
+	uint16_t num_attrs;
+	int i;
+
+	/* Initialize parse state */
+	memset(s, 0, sizeof(*s));
+	memset(code, 0, sizeof(*code));
+	s->env = env;
+	s->cfile = cfile;
+	s->bytes = bytecode->bytecode;
+	s->length = bytecode->length;
+	s->pos = 0;
+
+	/* Parse bytecode meta-info */
+	if (_jc_parse_uint16(s, &code->max_stack) != JNI_OK)
+		goto fail;
+	if (_jc_parse_uint16(s, &code->max_locals) != JNI_OK)
+		goto fail;
+	if (_jc_parse_uint32(s, &code_length) != JNI_OK)
+		goto fail;
+	if (s->pos + code_length > s->length) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "`Code' attribute bytecode length overflow");
+		goto fail;
+	}
+
+	/* Allocate offset map and instruction array */
+	if ((offset_map = _jc_vm_zalloc(s->env,
+	    code_length * sizeof(*offset_map))) == NULL)
+		goto fail;
+	if ((code->insns = _jc_vm_zalloc(s->env,
+	    code_length * sizeof(*code->insns))) == NULL)
+		goto fail;
+	code->num_insns = -1;
+
+	/* Parse bytecode */
+	if (_jc_parse_bytecode(s, code, offset_map, code_length) != JNI_OK)
+		goto fail;
+
+#if 0
+	/* Shorten up over-allocated instruction array */
+	if ((new_insns = _jc_vm_realloc(s->env, code->insns,
+	    code->num_insns * sizeof(*code->insns))) == NULL)
+		goto fail;
+	code->insns = new_insns;
+#endif
+
+	/* Parse trap table */
+	if (_jc_parse_uint16(s, &code->num_traps) != JNI_OK)
+		goto fail;
+	if (s->pos + code->num_traps * 8 > s->length) {
+		_JC_EX_STORE(s->env, ClassFormatError, "truncated class file");
+		goto fail;
+	}
+	if (code->num_traps > 0
+	    && (code->traps = _jc_vm_zalloc(s->env,
+	      code->num_traps * sizeof(*code->traps))) == NULL)
+		goto fail;
+	for (i = 0; i < code->num_traps; i++) {
+		_jc_cf_trap *const trap = &code->traps[i];
+		uint16_t value16;
+
+		if (_jc_parse_uint16(s, &value16) != JNI_OK)
+			goto fail;
+		trap->start = value16;
+		if (_jc_map_offset(s->env, code, code_length,
+		    offset_map, &trap->start) != JNI_OK)
+			goto fail;
+		if (_jc_parse_uint16(s, &value16) != JNI_OK)
+			goto fail;
+		trap->end = value16;
+		if (trap->end == code_length)
+			trap->end = code->num_insns;
+		else if (_jc_map_offset(s->env, code, code_length,
+		    offset_map, &trap->end) != JNI_OK)
+			goto fail;
+		if (_jc_parse_uint16(s, &value16) != JNI_OK)
+			goto fail;
+		trap->target = value16;
+		if (_jc_map_offset(s->env, code, code_length,
+		    offset_map, &trap->target) != JNI_OK)
+			goto fail;
+		if (trap->end <= trap->start) {
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "invalid trap table entry");
+			goto fail;
+		}
+		if (_jc_parse_class(s, &trap->type, JNI_TRUE) != JNI_OK)
+			goto fail;
+	}
+
+	/* Find and parse the line number table (if any) */
+	if (_jc_parse_uint16(s, &num_attrs) != JNI_OK)
+		goto fail;
+	for (i = 0; i < num_attrs; i++) {
+		_jc_cf_linenums *linenums;
+		_jc_cf_attr attr;
+		int j;
+
+		/* Look for "LineNumberTable" */
+		memset(&attr, 0, sizeof(attr));
+		if (_jc_parse_attribute(s, &attr) != JNI_OK)
+			goto fail;
+		if (strcmp(attr.name, "LineNumberTable") != 0) {
+			_jc_free_attribute(&attr);
+			continue;
+		}
+
+		/* Allocate line number map */
+		linenums = &attr.u.LineNumberTable;
+		if (linenums->length > 0
+		    && (code->linemaps = _jc_vm_alloc(s->env,
+		      linenums->length * sizeof(*code->linemaps))) == NULL) {
+			_jc_free_attribute(&attr);
+			goto fail;
+		}
+		code->num_linemaps = linenums->length;
+
+		/* Fill in map with offsets converted to instruction indicies */
+		for (j = 0; j < linenums->length; j++) {
+			_jc_cf_linenum *const linenum = &linenums->linenums[j];
+			_jc_cf_linemap *const linemap = &code->linemaps[j];
+
+			linemap->index = linenum->offset;
+			if (_jc_map_offset(s->env, code, code_length,
+			    offset_map, &linemap->index) != JNI_OK) {
+				_jc_free_attribute(&attr);
+				goto fail;
+			}
+			linemap->line = linenum->line;
+		}
+
+		/* Done */
+		_jc_free_attribute(&attr);
+		break;
+	}
+
+	/* Done */
+	return JNI_OK;
+
+fail:
+	/* Clean up and exit */
+	_jc_vm_free(&offset_map);
+	return JNI_ERR;
+}
+
+/*
+ * Free a previously parsed "Code" attribute.
+ */
+void
+_jc_destroy_code(_jc_cf_code *code)
+{
+	int i;
+
+	/* Free stuff */
+	for (i = 0; i < code->num_insns; i++) {
+		_jc_cf_insn *const insn = &code->insns[i];
+
+		switch (insn->opcode) {
+		case _JC_lookupswitch:
+			_jc_vm_free(&insn->u.lookupswitch);
+			break;
+		case _JC_tableswitch:
+			_jc_vm_free(&insn->u.tableswitch);
+			break;
+		default:
+			break;
+		}
+	}
+	_jc_vm_free(&code->insns);
+	_jc_vm_free(&code->traps);
+	_jc_vm_free(&code->linemaps);
+}
+
+/*
+ * Parse Java bytecode.
+ */
+static int
+_jc_parse_bytecode(_jc_cf_parse_state *s, _jc_cf_code *code,
+	uint32_t *offset_map, uint32_t code_length)
+{
+	const size_t start = s->pos;
+	const size_t end = s->pos + code_length;
+	_jc_cf_insn *insn = code->insns;
+	uint32_t insn_offset;
+	uint16_t value16;
+	uint8_t value8;
+	int inum = 0;
+	int i;
+
+	/* Initialize mapping from offset -> instruction index */
+	memset(offset_map, 0, code_length * sizeof(*offset_map));
+
+loop:
+	/* Sanity check */
+	_JC_ASSERT(code->num_insns == -1);
+	_JC_ASSERT(s->pos <= end);
+	_JC_ASSERT(inum <= code_length);
+	_JC_ASSERT(inum == insn - code->insns);
+
+	/* Done? */
+	if (s->pos == end) {
+		code->num_insns = inum;
+		goto pass2;
+	}
+
+	/* Get opcode and save its bytecode offset in the offset map */
+	insn_offset = s->pos - start;
+	offset_map[insn_offset] = inum;
+	insn->opcode = s->bytes[s->pos++];
+
+	/* Is opcode valid? */
+	if (_jc_bytecode_names[insn->opcode] == NULL) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid opcode 0x%02x", insn->opcode);
+		return JNI_ERR;
+	}
+
+	/* Decode instructions, except leave target offsets alone */
+	switch (insn->opcode) {
+	case _JC_aload:
+	case _JC_astore:
+	case _JC_dload:
+	case _JC_dstore:
+	case _JC_fload:
+	case _JC_fstore:
+	case _JC_iload:
+	case _JC_istore:
+	case _JC_lload:
+	case _JC_lstore:
+	case _JC_ret:
+		if (_jc_parse_local8(s, code, &insn->u.local.index) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case _JC_aload_0:
+	case _JC_aload_1:
+	case _JC_aload_2:
+	case _JC_aload_3:
+		insn->u.local.index = insn->opcode - _JC_aload_0;
+		insn->opcode = _JC_aload;
+		break;
+	case _JC_astore_0:
+	case _JC_astore_1:
+	case _JC_astore_2:
+	case _JC_astore_3:
+		insn->u.local.index = insn->opcode - _JC_astore_0;
+		insn->opcode = _JC_astore;
+		break;
+	case _JC_dload_0:
+	case _JC_dload_1:
+	case _JC_dload_2:
+	case _JC_dload_3:
+		insn->u.local.index = insn->opcode - _JC_dload_0;
+		insn->opcode = _JC_dload;
+		break;
+	case _JC_dstore_0:
+	case _JC_dstore_1:
+	case _JC_dstore_2:
+	case _JC_dstore_3:
+		insn->u.local.index = insn->opcode - _JC_dstore_0;
+		insn->opcode = _JC_dstore;
+		break;
+	case _JC_fload_0:
+	case _JC_fload_1:
+	case _JC_fload_2:
+	case _JC_fload_3:
+		insn->u.local.index = insn->opcode - _JC_fload_0;
+		insn->opcode = _JC_fload;
+		break;
+	case _JC_fstore_0:
+	case _JC_fstore_1:
+	case _JC_fstore_2:
+	case _JC_fstore_3:
+		insn->u.local.index = insn->opcode - _JC_fstore_0;
+		insn->opcode = _JC_fstore;
+		break;
+	case _JC_iload_0:
+	case _JC_iload_1:
+	case _JC_iload_2:
+	case _JC_iload_3:
+		insn->u.local.index = insn->opcode - _JC_iload_0;
+		insn->opcode = _JC_iload;
+		break;
+	case _JC_istore_0:
+	case _JC_istore_1:
+	case _JC_istore_2:
+	case _JC_istore_3:
+		insn->u.local.index = insn->opcode - _JC_istore_0;
+		insn->opcode = _JC_istore;
+		break;
+	case _JC_lload_0:
+	case _JC_lload_1:
+	case _JC_lload_2:
+	case _JC_lload_3:
+		insn->u.local.index = insn->opcode - _JC_lload_0;
+		insn->opcode = _JC_lload;
+		break;
+	case _JC_lstore_0:
+	case _JC_lstore_1:
+	case _JC_lstore_2:
+	case _JC_lstore_3:
+		insn->u.local.index = insn->opcode - _JC_lstore_0;
+		insn->opcode = _JC_lstore;
+		break;
+	case _JC_anewarray:
+	case _JC_checkcast:
+	case _JC_instanceof:
+	case _JC_new:
+		if (_jc_parse_class(s, &insn->u.type.name, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case _JC_bipush:
+		if (_jc_parse_uint8(s, &value8) != JNI_OK)
+			return JNI_ERR;
+		insn->u.immediate.value = (signed char)value8;
+		break;
+	case _JC_getfield:
+	case _JC_getstatic:
+	case _JC_putfield:
+	case _JC_putstatic:
+		if (_jc_parse_fieldref(s, &insn->u.fieldref.field) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case _JC_goto:
+	case _JC_if_acmpeq:
+	case _JC_if_acmpne:
+	case _JC_if_icmpeq:
+	case _JC_if_icmpne:
+	case _JC_if_icmplt:
+	case _JC_if_icmpge:
+	case _JC_if_icmpgt:
+	case _JC_if_icmple:
+	case _JC_ifeq:
+	case _JC_ifne:
+	case _JC_iflt:
+	case _JC_ifge:
+	case _JC_ifgt:
+	case _JC_ifle:
+	case _JC_ifnonnull:
+	case _JC_ifnull:
+	case _JC_jsr:
+		if (_jc_parse_uint16(s, &value16) != JNI_OK)
+			return JNI_ERR;
+		insn->u.branch.target = insn_offset + (jshort)value16;
+		break;
+	case _JC_jsr_w:
+	case _JC_goto_w:
+		_JC_ASSERT(_JC_jsr_w - _JC_jsr == _JC_goto_w - _JC_goto);
+		insn->opcode -= _JC_jsr_w - _JC_jsr;
+		if (_jc_parse_integer(s, &insn->u.branch.target) != JNI_OK)
+			return JNI_ERR;
+		insn->u.branch.target += insn_offset;
+		break;
+	case _JC_iinc:
+		if (_jc_parse_local8(s, code, &insn->u.iinc.index) != JNI_OK)
+			return JNI_ERR;
+		if (_jc_parse_uint8(s, &value8) != JNI_OK)
+			return JNI_ERR;
+		insn->u.iinc.value = (signed char)value8;
+		break;
+	case _JC_invokeinterface:
+		if (_jc_parse_interfacemethodref(s,
+		    &insn->u.invoke.method) != JNI_OK)
+			return JNI_ERR;
+		if (_jc_parse_uint8(s, &value8) != JNI_OK)
+			return JNI_ERR;
+		if (_jc_parse_uint8(s, &value8) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case _JC_invokespecial:
+	case _JC_invokestatic:
+	case _JC_invokevirtual:
+		if (_jc_parse_methodref(s, &insn->u.invoke.method) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case _JC_ldc:
+		if (_jc_parse_cpool_index8(s, (1 << CONSTANT_Integer)
+		      | (1 << CONSTANT_Float) | (1 << CONSTANT_String)
+		      | (1 << CONSTANT_Class), &insn->u.constant,
+		    JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case _JC_ldc_w:
+		if (_jc_parse_cpool_index16(s, (1 << CONSTANT_Integer)
+		      | (1 << CONSTANT_Float) | (1 << CONSTANT_String)
+		      | (1 << CONSTANT_Class), &insn->u.constant,
+		    JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		insn->opcode = _JC_ldc;
+		break;
+	case _JC_ldc2_w:
+		if (_jc_parse_cpool_index16(s,
+		    (1 << CONSTANT_Long) | (1 << CONSTANT_Double),
+		    &insn->u.constant, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		insn->opcode = _JC_ldc2_w;
+		break;
+	case _JC_lookupswitch:
+	    {
+		const char *const opname = _jc_bytecode_names[insn->opcode];
+		_jc_cf_lookupswitch *lsw;
+		jint default_target;
+		jint num_pairs;
+		int pad;
+
+		/* Parse padding */
+		pad = 3 - (((s->pos - start) + 3) % 4);
+		for (i = 0; i < pad; i++) {
+			if (_jc_parse_uint8(s, &value8) != JNI_OK)
+				return JNI_ERR;
+			if (value8 != 0) {
+				_JC_EX_STORE(s->env, ClassFormatError,
+				    "non-zero %s pad byte", opname);
+				return JNI_ERR;
+			}
+		}
+
+		/* Parse default target offset */
+		if (_jc_parse_integer(s, &default_target) != JNI_OK)
+			return JNI_ERR;
+		default_target += insn_offset;
+
+		/* Parse number of pairs */
+		if (_jc_parse_integer(s, &num_pairs) != JNI_OK)
+			return JNI_ERR;
+		if (num_pairs < 0) {
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "invalid %s #pairs %d", opname, (int)num_pairs);
+			return JNI_ERR;
+		}
+
+		/* Allocate structure */
+		if ((lsw = _jc_vm_alloc(s->env,
+		    sizeof(*lsw) + num_pairs * sizeof(*lsw->pairs))) == NULL)
+			return JNI_ERR;
+		insn->u.lookupswitch = lsw;
+		lsw->default_target = default_target;
+		lsw->num_pairs = num_pairs;
+
+		/* Parse match pairs table */
+		for (i = 0; i < lsw->num_pairs; i++) {
+			_jc_cf_lookup *const lookup = &lsw->pairs[i];
+
+			if (_jc_parse_integer(s, &lookup->match) != JNI_OK)
+				return JNI_ERR;
+			if (i > 0 && (lookup - 1)->match >= lookup->match) {
+				_JC_EX_STORE(s->env, ClassFormatError,
+				    "misordered %s table", opname);
+				return JNI_ERR;
+			}
+			if (_jc_parse_integer(s, &lookup->target) != JNI_OK)
+				return JNI_ERR;
+			lookup->target += insn_offset;
+		}
+		break;
+	    }
+	case _JC_tableswitch:
+	    {
+		const char *const opname = _jc_bytecode_names[insn->opcode];
+		_jc_cf_tableswitch *tsw;
+		jint default_target;
+		jint num_targets;
+		jint high;
+		jint low;
+		int pad;
+
+		/* Parse padding */
+		pad = 3 - (((s->pos - start) + 3) % 4);
+		for (i = 0; i < pad; i++) {
+			if (_jc_parse_uint8(s, &value8) != JNI_OK)
+				return JNI_ERR;
+			if (value8 != 0) {
+				_JC_EX_STORE(s->env, ClassFormatError,
+				    "non-zero %s pad byte", opname);
+				return JNI_ERR;
+			}
+		}
+
+		/* Parse default target offset */
+		if (_jc_parse_integer(s, &default_target) != JNI_OK)
+			return JNI_ERR;
+		default_target += insn_offset;
+
+		/* Parse bounds */
+		if (_jc_parse_integer(s, &low) != JNI_OK)
+			return JNI_ERR;
+		if (_jc_parse_integer(s, &high) != JNI_OK)
+			return JNI_ERR;
+		if (high < low) {
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "reversed %s bounds", opname);
+			return JNI_ERR;
+		}
+		num_targets = high - low + 1;
+
+		/* Allocate structure */
+		if ((tsw = _jc_vm_alloc(s->env, sizeof(*tsw)
+		    + num_targets * sizeof(*tsw->targets))) == NULL)
+			return JNI_ERR;
+		insn->u.tableswitch = tsw;
+		tsw->default_target = default_target;
+		tsw->high = high;
+		tsw->low = low;
+
+		/* Parse targets */
+		for (i = 0; i < num_targets; i++) {
+			if (_jc_parse_integer(s, &tsw->targets[i]) != JNI_OK)
+				return JNI_ERR;
+			tsw->targets[i] += insn_offset;
+		}
+		break;
+	    }
+	case _JC_multianewarray:
+		if (_jc_parse_class(s,
+		    &insn->u.multianewarray.type, JNI_FALSE) != JNI_OK)
+			return JNI_ERR;
+		if (_jc_parse_uint8(s, &insn->u.multianewarray.dims) != JNI_OK)
+			return JNI_ERR;
+		break;
+	case _JC_newarray:
+		if (_jc_parse_uint8(s, &value8) != JNI_OK)
+			return JNI_ERR;
+		switch (value8) {
+		case _JC_boolean:
+			insn->u.newarray.type = _JC_TYPE_BOOLEAN;
+			break;
+		case _JC_char:
+			insn->u.newarray.type = _JC_TYPE_CHAR;
+			break;
+		case _JC_float:
+			insn->u.newarray.type = _JC_TYPE_FLOAT;
+			break;
+		case _JC_double:
+			insn->u.newarray.type = _JC_TYPE_DOUBLE;
+			break;
+		case _JC_byte:
+			insn->u.newarray.type = _JC_TYPE_BYTE;
+			break;
+		case _JC_short:
+			insn->u.newarray.type = _JC_TYPE_SHORT;
+			break;
+		case _JC_int:
+			insn->u.newarray.type = _JC_TYPE_INT;
+			break;
+		case _JC_long:
+			insn->u.newarray.type = _JC_TYPE_LONG;
+			break;
+		default:
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "invalide type %d for newarray", value8);
+			return JNI_ERR;
+		}
+		break;
+	case _JC_sipush:
+		if (_jc_parse_uint16(s, &value16) != JNI_OK)
+			return JNI_ERR;
+		insn->u.immediate.value = (jshort)value16;
+		break;
+	case _JC_wide:
+		if (_jc_parse_uint8(s, &value8) != JNI_OK)
+			return JNI_ERR;
+		switch (value8) {
+		case _JC_aload:
+		case _JC_astore:
+		case _JC_dload:
+		case _JC_dstore:
+		case _JC_fload:
+		case _JC_fstore:
+		case _JC_iload:
+		case _JC_istore:
+		case _JC_lload:
+		case _JC_lstore:
+		case _JC_ret:
+			insn->opcode = value8;
+			if (_jc_parse_local16(s,
+			    code, &insn->u.local.index) != JNI_OK)
+				return JNI_ERR;
+			break;
+		case _JC_iinc:
+			insn->opcode = value8;
+			if (_jc_parse_local16(s,
+			    code, &insn->u.iinc.index) != JNI_OK)
+				return JNI_ERR;
+			if (_jc_parse_uint16(s, &value16) != JNI_OK)
+				return JNI_ERR;
+			insn->u.iinc.value = (jshort)value16;
+			break;
+		default:
+			_JC_EX_STORE(s->env, ClassFormatError,
+			    "invalid wide extension opcode 0x%02x", value8);
+			return JNI_ERR;
+		}
+		break;
+	default:
+		break;
+	}
+
+	/* Advance */
+	inum++;
+	insn++;
+	goto loop;
+
+pass2:
+	/* Convert target bytecode offsets into instruction indicies */
+	for (i = 0; i < code->num_insns; i++) {
+		_jc_cf_insn *const insn = &code->insns[i];
+
+		switch (insn->opcode) {
+		case _JC_goto:
+		case _JC_if_acmpeq:
+		case _JC_if_acmpne:
+		case _JC_if_icmpeq:
+		case _JC_if_icmpne:
+		case _JC_if_icmplt:
+		case _JC_if_icmpge:
+		case _JC_if_icmpgt:
+		case _JC_if_icmple:
+		case _JC_ifeq:
+		case _JC_ifne:
+		case _JC_iflt:
+		case _JC_ifge:
+		case _JC_ifgt:
+		case _JC_ifle:
+		case _JC_ifnonnull:
+		case _JC_ifnull:
+		case _JC_jsr:
+			if (_jc_map_offset(s->env, code, code_length,
+			    offset_map, &insn->u.branch.target) != JNI_OK)
+				return JNI_ERR;
+			break;
+		case _JC_lookupswitch:
+		    {
+			_jc_cf_lookupswitch *const lsw = insn->u.lookupswitch;
+			int j;
+
+			if (_jc_map_offset(s->env, code, code_length,
+			    offset_map, &lsw->default_target) != JNI_OK)
+				return JNI_ERR;
+			for (j = 0; j < lsw->num_pairs; j++) {
+				if (_jc_map_offset(s->env, code,
+				    code_length, offset_map,
+				    &lsw->pairs[j].target) != JNI_OK)
+					return JNI_ERR;
+			}
+			break;
+		    }
+		case _JC_tableswitch:
+		    {
+			_jc_cf_tableswitch *const tsw = insn->u.tableswitch;
+			const jint num_targets = tsw->high - tsw->low + 1;
+			int j;
+
+			if (_jc_map_offset(s->env, code, code_length,
+			    offset_map, &tsw->default_target) != JNI_OK)
+				return JNI_ERR;
+			for (j = 0; j < num_targets; j++) {
+				if (_jc_map_offset(s->env, code, code_length,
+				    offset_map, &tsw->targets[j]) != JNI_OK)
+					return JNI_ERR;
+			}
+			break;
+		    }
+		default:
+			break;
+		}
+	}
+
+	/* Done */
+	return JNI_OK;
+}
+
+/*
+ * Convert a bytecode offset into an instruction index.
+ */
+static int
+_jc_map_offset(_jc_env *env, _jc_cf_code *code, uint32_t length,
+	jint *offset_map, jint *targetp)
+{
+	jint target = *targetp;
+
+	if (target == 0)
+		return JNI_OK;
+	if (target < 0 || target >= length || offset_map[target] == 0) {
+		_JC_EX_STORE(env, ClassFormatError,
+		    "invalid branch target %u", target);
+		return JNI_ERR;
+	}
+	*targetp = offset_map[target];
+	return JNI_OK;
+}
+
+/*
+ * Parse an 8 bit local index.
+ */
+static int
+_jc_parse_local8(_jc_cf_parse_state *s, _jc_cf_code *code, uint16_t *indexp)
+{
+	uint8_t index;
+
+	if (_jc_parse_uint8(s, &index) != JNI_OK)
+		return JNI_ERR;
+	if (index >= code->max_locals) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "local index %u >= %u is out of range",
+		    index, code->max_locals);
+		return JNI_ERR;
+	}
+	*indexp = index;
+	return JNI_OK;
+}
+
+/*
+ * Parse an 16 bit local index.
+ */
+static int
+_jc_parse_local16(_jc_cf_parse_state *s, _jc_cf_code *code, uint16_t *indexp)
+{
+	uint16_t index;
+
+	if (_jc_parse_uint16(s, &index) != JNI_OK)
+		return JNI_ERR;
+	if (index >= code->max_locals) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "local index %u >= %u is out of range",
+		    index, code->max_locals);
+		return JNI_ERR;
+	}
+	*indexp = index;
+	return JNI_OK;
+}
+
+/*
+ * Parse an 8 bit constant pool index.
+ */
+static int
+_jc_parse_cpool_index8(_jc_cf_parse_state *s, int types,
+	_jc_cf_constant **ptr, int optional)
+{
+	uint8_t cp_index;
+
+	if (_jc_parse_uint8(s, &cp_index) != JNI_OK)
+		return JNI_ERR;
+	if (cp_index == 0 && optional) {
+		*ptr = NULL;
+		return JNI_OK;
+	}
+	return _jc_parse_cpool_index(s, types, ptr, cp_index);
+}
+
+/*
+ * Parse a 16 bit constant pool index.
+ */
+static int
+_jc_parse_cpool_index16(_jc_cf_parse_state *s, int types,
+	_jc_cf_constant **ptr, int optional)
+{
+	uint16_t cp_index;
+
+	if (_jc_parse_uint16(s, &cp_index) != JNI_OK)
+		return JNI_ERR;
+	if (cp_index == 0 && optional) {
+		*ptr = NULL;
+		return JNI_OK;
+	}
+	return _jc_parse_cpool_index(s, types, ptr, cp_index);
+}
+
+static int
+_jc_parse_cpool_index(_jc_cf_parse_state *s, int types,
+	_jc_cf_constant **ptr, uint16_t cp_index)
+{
+	_jc_classfile *const cfile = s->cfile;
+	_jc_cf_constant *c;
+
+	/* Map "uncompressed" constant index to real index */
+	if (cp_index < 1 || cp_index >= cfile->num_constants) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "invalid constant pool index %u", cp_index);
+		return JNI_ERR;
+	}
+	c = &cfile->constants[cp_index - 1];
+	if (types != 0 && ((1 << c->type) & types) == 0) {
+		_JC_EX_STORE(s->env, ClassFormatError,
+		    "unexpected constant pool type %u at index %u",
+		    c->type, cp_index);
+		return JNI_ERR;
+	}
+	*ptr = c;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_fieldref(_jc_cf_parse_state *s, _jc_cf_ref **refp)
+{
+	_jc_cf_constant *cp;
+
+	if (_jc_parse_cpool_index16(s,
+	    1 << CONSTANT_Fieldref, &cp, JNI_FALSE) != JNI_OK)
+		return JNI_ERR;
+	*refp = &cp->u.Ref;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_methodref(_jc_cf_parse_state *s, _jc_cf_ref **refp)
+{
+	_jc_cf_constant *cp;
+
+	if (_jc_parse_cpool_index16(s,
+	    1 << CONSTANT_Methodref, &cp, JNI_FALSE) != JNI_OK)
+		return JNI_ERR;
+	*refp = &cp->u.Ref;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_interfacemethodref(_jc_cf_parse_state *s, _jc_cf_ref **refp)
+{
+	_jc_cf_constant *cp;
+
+	if (_jc_parse_cpool_index16(s,
+	    1 << CONSTANT_InterfaceMethodref, &cp, JNI_FALSE) != JNI_OK)
+		return JNI_ERR;
+	*refp = &cp->u.Ref;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_class(_jc_cf_parse_state *s, const char **classp, int optional)
+{
+	_jc_cf_constant *cp;
+
+	if (_jc_parse_cpool_index16(s,
+	    1 << CONSTANT_Class, &cp, optional) != JNI_OK)
+		return JNI_ERR;
+	*classp = (cp != NULL) ? cp->u.Class : NULL;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_string(_jc_cf_parse_state *s, const char **utfp, int optional)
+{
+	_jc_cf_constant *cp;
+
+	if (_jc_parse_cpool_index16(s,
+	    1 << CONSTANT_Utf8, &cp, optional) != JNI_OK)
+		return JNI_ERR;
+	*utfp = (cp != NULL) ? cp->u.Utf8 : NULL;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_integer(_jc_cf_parse_state *s, jint *valuep)
+{
+	uint32_t value;
+
+	if (_jc_parse_uint32(s, &value) != JNI_OK)
+		return JNI_ERR;
+	if (valuep != NULL)
+		*valuep = (jint)value;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_float(_jc_cf_parse_state *s, jfloat *valuep)
+{
+	uint8_t b[4];
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		if (_jc_parse_uint8(s, &b[i]) != JNI_OK)
+			return JNI_ERR;
+	}
+	if (valuep != NULL)
+		*valuep = _JC_FCONST(b[0], b[1], b[2], b[3]);
+	return JNI_OK;
+}
+
+static int
+_jc_parse_long(_jc_cf_parse_state *s, jlong *valuep)
+{
+	uint64_t value = 0;
+	uint8_t byte;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		if (_jc_parse_uint8(s, &byte) != JNI_OK)
+			return JNI_ERR;
+		value = (value << 8) | byte;
+	}
+	if (valuep != NULL)
+		*valuep = value;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_double(_jc_cf_parse_state *s, jdouble *valuep)
+{
+	uint8_t b[8];
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		if (_jc_parse_uint8(s, &b[i]) != JNI_OK)
+			return JNI_ERR;
+	}
+	if (valuep != NULL) {
+		*valuep = _JC_DCONST(b[0], b[1], b[2], b[3],
+		    b[4], b[5], b[6], b[7]);
+	}
+	return JNI_OK;
+}
+
+static int
+_jc_parse_utf8(_jc_cf_parse_state *s, const u_char **utfp, uint16_t *lengthp)
+{
+	uint16_t length;
+	const u_char *utf;
+
+	/* Get length */
+	if (_jc_parse_uint16(s, &length) != JNI_OK)
+		return JNI_ERR;
+	if (s->pos + length > s->length)
+		goto truncated;
+
+	/* Validate UTF-8 encoding */
+	utf = s->bytes + s->pos;
+	if (_jc_utf_decode(utf, length, NULL) == -1)
+		goto invalid;
+
+	/* Update position */
+	s->pos += length;
+
+	/* Done */
+	if (utfp != NULL)
+		*utfp = utf;
+	if (lengthp != NULL)
+		*lengthp = length;
+	return JNI_OK;
+
+truncated:
+	_JC_EX_STORE(s->env, ClassFormatError, "truncated class file");
+	return JNI_ERR;
+
+invalid:
+	_JC_EX_STORE(s->env, ClassFormatError,
+	    "invalid UTF-8 string encoding");
+	return JNI_ERR;
+}
+
+static int
+_jc_parse_uint32(_jc_cf_parse_state *s, uint32_t *valuep)
+{
+	uint32_t value = 0;
+	uint8_t byte;
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		if (_jc_parse_uint8(s, &byte) != JNI_OK)
+			return JNI_ERR;
+		value = (value << 8) | byte;
+	}
+	if (valuep != NULL)
+		*valuep = value;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_uint16(_jc_cf_parse_state *s, uint16_t *valuep)
+{
+	uint16_t value = 0;
+	uint8_t byte;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		if (_jc_parse_uint8(s, &byte) != JNI_OK)
+			return JNI_ERR;
+		value = (value << 8) | byte;
+	}
+	if (valuep != NULL)
+		*valuep = value;
+	return JNI_OK;
+}
+
+static int
+_jc_parse_uint8(_jc_cf_parse_state *s, uint8_t *valuep)
+{
+	if (s->pos >= s->length) {
+		_JC_EX_STORE(s->env, ClassFormatError, "truncated class file");
+		return JNI_ERR;
+	}
+	if (valuep != NULL)
+		*valuep = s->bytes[s->pos];
+	s->pos++;
+	return JNI_OK;
+}
+
+static void
+_jc_sub_state(_jc_cf_parse_state *s, _jc_cf_parse_state *t, size_t length)
+{
+	_JC_ASSERT(s->pos + length <= s->length);
+	*t = *s;
+	t->bytes = s->bytes + s->pos;
+	t->length = length;
+	t->pos = 0;
+}
+
+/*
+ * Sorts fields by type/size, name, then signature.
+ *
+ * This must sort the same as org.dellroad.jc.cgen.Util.fieldComparator.
+ */
+static int
+_jc_field_sorter(const void *item1, const void *item2)
+{
+	const _jc_cf_field *const field1 = (_jc_cf_field *)item1;
+	const _jc_cf_field *const field2 = (_jc_cf_field *)item2;
+	const u_char ptype1 = _jc_sig_types[(u_char)*field1->descriptor];
+	const u_char ptype2 = _jc_sig_types[(u_char)*field2->descriptor];
+	int diff;
+
+	if ((diff = !_JC_ACC_TEST(field1, STATIC)
+	    - !_JC_ACC_TEST(field2, STATIC)) != 0)
+		return diff;
+	if ((diff = _jc_field_type_sort[ptype1]
+	    - _jc_field_type_sort[ptype2]) != 0)
+		return diff;
+	if ((diff = strcmp(field1->name, field2->name)) != 0)
+		return diff;
+	return strcmp(field1->descriptor, field2->descriptor);
+}
+
+/*
+ * Sorts methods by name, then signature.
+ *
+ * This must sort the same as org.dellroad.jc.cgen.Util.methodComparator.
+ */
+static int
+_jc_method_sorter(const void *item1, const void *item2)
+{
+	const _jc_cf_method *const method1 = (_jc_cf_method *)item1;
+	const _jc_cf_method *const method2 = (_jc_cf_method *)item2;
+	int diff;
+
+	if ((diff = strcmp(method1->name, method2->name)) != 0)
+		return diff;
+	return strcmp(method1->descriptor, method2->descriptor);
+}
+