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, <ab->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 = <ab->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);
+}
+