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 [22/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/resolve2.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/resolve2.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/resolve2.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/resolve2.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,1526 @@
+
+/*
+ * 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: resolve2.c,v 1.4 2005/07/10 21:03:54 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/* Internal functions */
+static jint		_jc_resolve_fields(_jc_env *env, _jc_type *type,
+				_jc_resolve_info *info);
+static jint		_jc_resolve_methods(_jc_env *env, _jc_type *type,
+				_jc_resolve_info *info);
+static jint		_jc_resolve_bytecode(_jc_env *env,
+				_jc_method *const method,
+				_jc_cf_bytecode *bytecode,
+				_jc_resolve_info *rinfo);
+static jint		_jc_derive_imethod_tables(_jc_env *env, _jc_type *type);
+static jint		_jc_derive_instanceof_table(_jc_env *env,
+				_jc_type *type);
+static jint		_jc_resolve_inner_classes(_jc_env *env, _jc_type *type,
+				_jc_resolve_info *info);
+static int		_jc_add_iface_methods(_jc_type *type,
+				_jc_method **methods);
+static int		_jc_method_sorter(const void *item1, const void *item2);
+static int		_jc_type_sorter(const void *item1, const void *item2);
+
+/*
+ * Resolve an interpreted class.
+ *
+ * If unsuccessful, an exception is posted.
+ */
+jint
+_jc_resolve_interp(_jc_env *env, _jc_type *type, _jc_resolve_info *info)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_nonarray_type *const ntype = &type->u.nonarray;
+	_jc_classfile *const cfile = ntype->u.cfile;
+
+	/* Sanity check */
+	_JC_ASSERT(_JC_ACC_TEST(type, INTERP));
+	_JC_ASSERT(!_JC_FLG_TEST(type, ARRAY));
+
+	/* Verbosity */
+	if (type->loader == vm->boot.loader) {
+		VERBOSE(RESOLUTION, vm,
+		    "resolving `%s' (via bootstrap loader)", type->name);
+	} else {
+		VERBOSE(RESOLUTION, vm,
+		    "resolving `%s' (via %s@%p)", type->name,
+		    type->loader->instance->type->name, type->loader->instance);
+	}
+
+	/* Copy source filename */
+	_JC_ASSERT(ntype->source_file == NULL);
+	if (cfile->source_file != NULL) {
+		_JC_MUTEX_LOCK(env, type->loader->mutex);
+		if ((ntype->source_file = _jc_cl_strdup(env,
+		    type->loader, cfile->source_file)) == NULL) {
+			_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+			_jc_post_exception_info(env);
+			goto fail;
+		}
+		_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+	}
+
+	/* Resolve fields */
+	if (_jc_resolve_fields(env, type, info) != JNI_OK)
+		return JNI_ERR;
+
+	/* Resolve methods */
+	if (_jc_resolve_methods(env, type, info) != JNI_OK)
+		return JNI_ERR;
+
+	/* Resolve inner classes */
+	if (_jc_resolve_inner_classes(env, type, info) != JNI_OK)
+		return JNI_ERR;
+
+	/* Build interface method hash tables */
+	if (!_JC_ACC_TEST(type, INTERFACE)
+	    && !_JC_ACC_TEST(type, ABSTRACT)
+	    && _jc_derive_imethod_tables(env, type) != JNI_OK)
+		goto fail;
+
+	/* Build instanceof hash table */
+	if (_jc_derive_instanceof_table(env, type) != JNI_OK)
+		goto fail;
+
+	/* We don't need the class file anymore */
+	_jc_destroy_classfile(&type->u.nonarray.u.cfile);
+
+	/* Done */
+	return JNI_OK;
+
+fail:
+	/* Clean up after failure */
+	_JC_MUTEX_LOCK(env, type->loader->mutex);
+	if (ntype->source_file != NULL) {
+		_jc_cl_unalloc(type->loader,
+		    &ntype->source_file, strlen(ntype->source_file));
+	}
+	_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+	return JNI_ERR;
+}
+
+/*
+ * Resolve fields.
+ */
+static jint
+_jc_resolve_fields(_jc_env *env, _jc_type *type, _jc_resolve_info *info)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_nonarray_type *const ntype = &type->u.nonarray;
+	_jc_classfile *const cfile = ntype->u.cfile;
+	size_t initial_values_size;
+	void *initial_values;
+	char *ptr;
+	int i;
+
+	/* Sanity check */
+	_JC_ASSERT(_JC_ACC_TEST(type, INTERP));
+
+	/* Resolve each field's type and count static initializers */
+	initial_values_size = 0;
+	for (i = 0; i < ntype->num_fields; i++) {
+		_jc_cf_field *const cfield = &cfile->fields[i];
+		const u_char ptype = _jc_sig_types[(u_char)*cfield->descriptor];
+		_jc_field *const field = ntype->fields[i];
+
+		/* Sanity check */
+		_JC_ASSERT(_JC_ACC_TEST(field, INTERP));
+
+		/* Resolve type and add up initializer sizes */
+		switch (ptype) {
+		case _JC_TYPE_BOOLEAN:
+		case _JC_TYPE_BYTE:
+		case _JC_TYPE_CHAR:
+		case _JC_TYPE_SHORT:
+		case _JC_TYPE_INT:
+		case _JC_TYPE_LONG:
+		case _JC_TYPE_FLOAT:
+		case _JC_TYPE_DOUBLE:
+			field->type = vm->boot.types.prim[ptype];
+			if (cfield->initial_value != NULL)
+				initial_values_size += _jc_type_sizes[ptype];
+			break;
+		case _JC_TYPE_REFERENCE:
+		    {
+			_jc_class_loader *const loader = type->loader;
+			const char *desc = cfield->descriptor;
+			_jc_class_ref ref;
+			const char *end;
+			u_char btype;
+
+			/* Parse field's type */
+			end = _jc_parse_class_ref(desc, &ref, 0, &btype);
+			if (btype == _JC_TYPE_INVALID
+			    || (*desc != '[' && *(end - 1) != ';'))
+				goto invalid_signature;
+
+			/* Resolve field's type */
+			field->type = (*desc == '[') ?
+			    _jc_load_type2(env, loader, desc, end - desc) :
+			    _jc_load_type2(env, loader,
+			      desc + 1, end - desc - 2);
+			if (field->type == NULL)
+				return JNI_ERR;
+			if (_jc_resolve_add_loader_ref(env, info,
+			    field->type->loader) != JNI_OK) {
+				_jc_post_exception_info(env);
+				return JNI_ERR;
+			}
+
+			/* Add initial value size */
+			if (cfield->initial_value != NULL) {
+				_JC_ASSERT(field->type
+				    == vm->boot.types.String);
+				initial_values_size +=
+				    strlen(cfield->initial_value->u.Utf8) + 1;
+			}
+			break;
+		    }
+		default:
+invalid_signature:
+			_jc_post_exception_msg(env, _JC_ClassFormatError,
+			    "invalid descriptor `%s' for field `%s.%s'",
+			    cfield->descriptor, cfile->name, cfield->name);
+			return JNI_ERR;
+		}
+	}
+
+	/* Any initial values? */
+	if (initial_values_size == 0)
+		goto skip_initial_values;
+
+	/* Allocate storage for initial values */
+	_JC_MUTEX_LOCK(env, type->loader->mutex);
+	if ((initial_values = _jc_cl_zalloc(env,
+	    type->loader, initial_values_size)) == NULL) {
+		_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+		_jc_post_exception_info(env);
+		goto fail;
+	}
+	_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+
+	/* Copy initial values from classfile */
+	ptr = initial_values;
+	for (i = 0; i < ntype->num_fields; i++) {
+		_jc_cf_field *const cfield = &cfile->fields[i];
+		_jc_field *const field = ntype->fields[i];
+		_jc_value prim_value;
+		u_char ptype;
+
+		/* Does this field have a static initializer? */
+		if (cfield->initial_value == NULL)
+			continue;
+		field->initial_value = ptr;
+		ptype = _jc_sig_types[(u_char)*field->signature];
+
+		/* Handle strings */
+		if (ptype == _JC_TYPE_REFERENCE) {
+			const char *const utf8 = cfield->initial_value->u.Utf8;
+			const size_t slen = strlen(utf8) + 1;
+
+			memcpy(field->initial_value, utf8, slen);
+			ptr += slen;
+			continue;
+		}
+
+		/* Handle primitives */
+		switch (ptype) {
+		case _JC_TYPE_BOOLEAN:
+			prim_value.z = !!cfield->initial_value->u.Integer;
+			break;
+		case _JC_TYPE_BYTE:
+			prim_value.b = cfield->initial_value->u.Integer;
+			break;
+		case _JC_TYPE_CHAR:
+			prim_value.c = cfield->initial_value->u.Integer;
+			break;
+		case _JC_TYPE_SHORT:
+			prim_value.s = cfield->initial_value->u.Integer;
+			break;
+		case _JC_TYPE_INT:
+			prim_value.i = cfield->initial_value->u.Integer;
+			break;
+		case _JC_TYPE_LONG:
+			prim_value.j = cfield->initial_value->u.Long;
+			break;
+		case _JC_TYPE_FLOAT:
+			prim_value.f = cfield->initial_value->u.Float;
+			break;
+		case _JC_TYPE_DOUBLE:
+			prim_value.d = cfield->initial_value->u.Double;
+			break;
+		default:
+			_JC_ASSERT(JNI_FALSE);
+		}
+		memcpy(field->initial_value,
+		    &prim_value, _jc_type_sizes[ptype]);
+		ptr += _jc_type_sizes[ptype];
+	}
+
+skip_initial_values:
+	/* Done */
+	return JNI_OK;
+
+fail:
+	/* Clean up after failure */
+	_JC_MUTEX_LOCK(env, type->loader->mutex);
+	_jc_cl_unalloc(type->loader, &initial_values, initial_values_size);
+	_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+	return JNI_ERR;
+}
+
+/*
+ * Resolve methods.
+ */
+static jint
+_jc_resolve_methods(_jc_env *env, _jc_type *const type, _jc_resolve_info *info)
+{
+	_jc_nonarray_type *const ntype = &type->u.nonarray;
+	_jc_class_loader *const loader = type->loader;
+	_jc_classfile *const cfile = ntype->u.cfile;
+	int i;
+
+	/* Sanity check */
+	_JC_ASSERT(_JC_ACC_TEST(type, INTERP));
+
+	/* Resolve method signature info */
+	for (i = 0; i < ntype->num_methods; i++) {
+		_jc_cf_method *const cmethod = &cfile->methods[i];
+		_jc_method *const method = ntype->methods[i];
+		u_char md5[MD5_DIGEST_LENGTH];
+		MD5_CTX ctx;
+		int j;
+
+		/* Sanity check */
+		_JC_ASSERT(_JC_ACC_TEST(method, INTERP));
+
+		/* Compute method's signature hash */
+		MD5_Init(&ctx);
+		MD5_Update(&ctx, method->name, strlen(method->name));
+		MD5_Update(&ctx, method->signature, strlen(method->signature));
+		MD5_Final(md5, &ctx);
+		method->signature_hash =
+		      ((jlong)md5[ 8] << 56) | ((jlong)md5[ 9] << 48)
+		    | ((jlong)md5[10] << 40) | ((jlong)md5[11] << 32)
+		    | ((jlong)md5[12] << 24) | ((jlong)md5[13] << 16)
+		    | ((jlong)md5[14] <<  8) | ((jlong)md5[15] <<  0);
+
+		/* Compute the number of parameters */
+		if ((j = _jc_resolve_signature(env, method, NULL)) == -1) {
+			_jc_post_exception_info(env);
+			return JNI_ERR;
+		}
+
+		/* Initialize easy stuff */
+		method->num_parameters = j;
+		method->num_exceptions = cmethod->exceptions != NULL ?
+		    cmethod->exceptions->num_exceptions : 0;
+
+		/* Resolve method parameter types */
+		if (_jc_resolve_signature(env, method, info) == -1) {
+			_jc_post_exception_info(env);
+			return JNI_ERR;
+		}
+
+		/* Resolve exception types */
+		for (j = 0; j < method->num_exceptions; j++) {
+			if ((method->exceptions[j] = _jc_load_type(env, loader,
+			    cmethod->exceptions->exceptions[j])) == NULL)
+				return JNI_ERR;
+			if (_jc_resolve_add_loader_ref(env, info,
+			    method->exceptions[j]->loader) != JNI_OK) {
+				_jc_post_exception_info(env);
+				return JNI_ERR;
+			}
+		}
+	}
+
+	/* Resolve bytecode for non-abstract methods */
+	for (i = 0; i < ntype->num_methods; i++) {
+		_jc_cf_method *const cmethod = &cfile->methods[i];
+		_jc_method *const method = ntype->methods[i];
+
+		if (_JC_ACC_TEST(method, ABSTRACT)
+		    || _JC_ACC_TEST(method, NATIVE))
+			continue;
+		if (_jc_resolve_bytecode(env,
+		    method, cmethod->code, info) != JNI_OK)
+			return JNI_ERR;
+	}
+
+	/* Skip remaining stuff for interfaces */
+	if (_JC_ACC_TEST(type, INTERFACE))
+		goto done;
+
+done:
+	/* Done */
+	return JNI_OK;
+}
+
+/*
+ * Parse/resolve method signature.
+ *
+ * Returns the number of parameters and fills in method->param_ptypes;
+ * if info != NULL, also resolves and fills in method->param_types and
+ * method->return_type.
+ *
+ * Returns -1 and stores an exception on failure.
+ */
+int
+_jc_resolve_signature(_jc_env *env, _jc_method *method, _jc_resolve_info *info)
+{
+	_jc_jvm *const vm = env->vm;
+	jboolean resolve;
+	int done = 0;
+	const char *s;
+	int i;
+
+	/* Should we resolve types? */
+	resolve = (info != NULL);
+
+	/* Sanity check signature */
+	if (*method->signature != '(') {
+invalid:	_jc_post_exception_msg(env, _JC_ClassFormatError,
+		    "invalid signature `%s' for %s.%s", method->signature,
+		    method->class->name, method->name);
+		return -1;
+	}
+
+	/* Parse parameters */
+	for (i = 0, s = method->signature + 1; !done; i++) {
+		_jc_type *type = NULL;
+		int ptype;
+
+		/* Handle return value */
+		if (*s == ')') {
+			done = 1;
+			s++;
+		}
+
+		/* Parse type */
+		ptype = _jc_sig_types[(u_char)*s];
+		switch (ptype) {
+		case _JC_TYPE_VOID:
+			if (!done)
+				goto invalid;
+			/* FALLTHROUGH */
+		case _JC_TYPE_BOOLEAN:
+		case _JC_TYPE_BYTE:
+		case _JC_TYPE_CHAR:
+		case _JC_TYPE_SHORT:
+		case _JC_TYPE_INT:
+		case _JC_TYPE_LONG:
+		case _JC_TYPE_FLOAT:
+		case _JC_TYPE_DOUBLE:
+			if (resolve)
+				type = vm->boot.types.prim[ptype];
+			s++;
+			break;
+		case _JC_TYPE_REFERENCE:
+		    {
+			_jc_class_loader *const loader = method->class->loader;
+			_jc_class_ref ref;
+			const char *end;
+			u_char btype;
+
+			end = _jc_parse_class_ref(s, &ref, 0, &btype);
+			if (btype == _JC_TYPE_INVALID
+			    || (*s != '[' && *(end - 1) != ';'))
+				goto invalid;
+			if (resolve) {
+				type = (*s == '[') ?
+				    _jc_load_type2(env, loader, s, end - s) :
+				    _jc_load_type2(env, loader,
+					s + 1, end - s - 2);
+				if (type == NULL)
+					return -1;
+			}
+			s = end;
+			break;
+		    }
+		default:
+			goto invalid;
+		}
+
+		/* Set param (or return value) type */
+		if (resolve) {
+			if (!done)
+				method->param_types[i] = type;
+			else
+				method->return_type = type;
+			if (_jc_resolve_add_loader_ref(env,
+			    info, type->loader) != JNI_OK) {
+				_jc_post_exception_info(env);
+				return -1;
+			}
+		}
+
+		/* Set param (or return value) ptype */
+		if (method->param_ptypes != NULL)
+			method->param_ptypes[i] = ptype;
+	}
+
+	/* Trailing garbage? */
+	if (*s != '\0')
+		goto invalid;
+
+	/* Done */
+	return i - 1;
+}
+
+/*
+ * Parse and resolve method bytecode.
+ *
+ * Posts an exception upon failure.
+ */
+static jint
+_jc_resolve_bytecode(_jc_env *env, _jc_method *const method,
+	_jc_cf_bytecode *bytecode, _jc_resolve_info *rinfo)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_class_loader *const loader = method->class->loader;
+	_jc_classfile *const cfile = method->class->u.nonarray.u.cfile;
+	_jc_method_code *const interp = &method->u.code;
+	_jc_cf_code code_mem;
+	_jc_cf_code *const code = &code_mem;
+	jboolean mutex_locked;
+	int i;
+
+	/* Sanity check */
+	_JC_ASSERT(_JC_ACC_TEST(method, INTERP));
+	_JC_ASSERT(!_JC_FLG_TEST(method->class, RESOLVED));
+
+	/* Parse bytecode */
+	if (_jc_parse_code(env, cfile, bytecode, code) != JNI_OK) {
+		_jc_post_exception_info(env);
+		return JNI_ERR;
+	}
+
+	/* Allocate resolved method info */
+	memset(interp, 0, sizeof(*interp));
+	_JC_MUTEX_LOCK(env, loader->mutex);
+	mutex_locked = JNI_TRUE;
+	if ((interp->opcodes = _jc_cl_alloc(env, loader,
+	    code->num_insns * sizeof(*interp->opcodes))) == NULL)
+		goto post_fail;
+	if ((interp->info = _jc_cl_alloc(env, loader,
+	    code->num_insns * sizeof(*interp->info))) == NULL)
+		goto post_fail;
+	if ((interp->traps = _jc_cl_alloc(env, loader,
+	    code->num_traps * sizeof(*interp->traps))) == NULL)
+		goto post_fail;
+	if ((interp->linemaps = _jc_cl_alloc(env, loader,
+	    code->num_linemaps * sizeof(*interp->linemaps))) == NULL)
+		goto post_fail;
+	_JC_MUTEX_UNLOCK(env, loader->mutex);
+	mutex_locked = JNI_FALSE;
+
+	/* Initialize */
+	interp->max_stack = code->max_stack;
+	interp->max_locals = code->max_locals;
+	interp->num_insns = code->num_insns;
+	interp->num_traps = code->num_traps;
+	interp->num_linemaps = code->num_linemaps;
+
+	/* Copy line number table */
+	for (i = 0; i < interp->num_linemaps; i++) {
+		_jc_cf_linemap *const clinemap = &code->linemaps[i];
+		_jc_linemap *const linemap = &interp->linemaps[i];
+
+		linemap->index = clinemap->index;
+		linemap->line = clinemap->line;
+	}
+
+	/* Determine parameter count with long/double counted twice */
+	_JC_ASSERT(interp->num_params2 == 0);
+	interp->num_params2 = method->num_parameters;
+	for (i = 0; i < method->num_parameters; i++) {
+		if (_jc_dword_type[method->param_ptypes[i]])
+			interp->num_params2++;
+	}
+
+	/* Resolve and copy trap info */
+	for (i = 0; i < code->num_traps; i++) {
+		_jc_cf_trap *const ptrap = &code->traps[i];
+		_jc_interp_trap *const itrap = &interp->traps[i];
+
+		itrap->start = ptrap->start;
+		itrap->end = ptrap->end;
+		itrap->target = ptrap->target;
+		if (ptrap->type == NULL) {
+			itrap->type = NULL;
+			continue;
+		}
+		if ((itrap->type = _jc_load_type(env,
+		    loader, ptrap->type)) == NULL)
+			goto fail;
+		if (_jc_resolve_add_loader_ref(env,
+		    rinfo, itrap->type->loader) != JNI_OK)
+			goto post_fail;
+	}
+
+	/* Resolve and copy instructions */
+	for (i = 0; i < code->num_insns; i++) {
+		_jc_cf_insn *const insn = &code->insns[i];
+		_jc_insn_info *const info = &interp->info[i];
+		u_char opcode;
+
+		/* Get opcode */
+		opcode = insn->opcode;
+
+		/* Copy and resolve additional info, possibly changing opcode */
+		switch (opcode) {
+		case _JC_aconst_null:
+			info->constant.l = NULL;
+			opcode = _JC_ldc;
+			break;
+		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:
+			info->local = insn->u.local.index;
+			break;
+		case _JC_anewarray:
+		    {
+			const char *etype = insn->u.type.name;
+			const size_t elen = strlen(etype) + 1;
+			char *atype;
+
+			if ((atype = _jc_vm_alloc(env, elen + 3)) == NULL)
+				goto post_fail;
+			atype[0] = '[';
+			if (*etype == '[')
+				memcpy(atype + 1, etype, elen);
+			else {
+				atype[1] = 'L';
+				memcpy(atype + 2, etype, elen);
+				atype[elen + 1] = ';';
+				atype[elen + 2] = '\0';
+			}
+			if ((info->type = _jc_load_type(env,
+			    loader, atype)) == NULL) {
+				_jc_vm_free(&atype);
+				goto fail;
+			}
+			_jc_vm_free(&atype);
+			if (_jc_resolve_add_loader_ref(env,
+			    rinfo, info->type->loader) != JNI_OK)
+				goto post_fail;
+			break;
+		    }
+		case _JC_checkcast:
+		case _JC_instanceof:
+		case _JC_new:
+			if ((info->type = _jc_load_type(env,
+			    loader, insn->u.type.name)) == NULL)
+				goto fail;
+			if (_jc_resolve_add_loader_ref(env,
+			    rinfo, info->type->loader) != JNI_OK)
+				goto post_fail;
+			break;
+		case _JC_bipush:
+		case _JC_sipush:
+			info->constant.i = insn->u.immediate.value;
+			opcode = _JC_ldc;
+			break;
+		case _JC_dconst_0:
+			info->constant.d = 0.0;
+			opcode = _JC_ldc2_w;
+			break;
+		case _JC_dconst_1:
+			info->constant.d = 1.0;
+			opcode = _JC_ldc2_w;
+			break;
+		case _JC_fconst_0:
+			info->constant.f = 0.0;
+			opcode = _JC_ldc;
+			break;
+		case _JC_fconst_1:
+			info->constant.f = 1.0;
+			opcode = _JC_ldc;
+			break;
+		case _JC_fconst_2:
+			info->constant.f = 2.0;
+			opcode = _JC_ldc;
+			break;
+		case _JC_iconst_m1:
+			info->constant.i = -1;
+			opcode = _JC_ldc;
+			break;
+		case _JC_iconst_0:
+			info->constant.i = 0;
+			opcode = _JC_ldc;
+			break;
+		case _JC_iconst_1:
+			info->constant.i = 1;
+			opcode = _JC_ldc;
+			break;
+		case _JC_iconst_2:
+			info->constant.i = 2;
+			opcode = _JC_ldc;
+			break;
+		case _JC_iconst_3:
+			info->constant.i = 3;
+			opcode = _JC_ldc;
+			break;
+		case _JC_iconst_4:
+			info->constant.i = 4;
+			opcode = _JC_ldc;
+			break;
+		case _JC_iconst_5:
+			info->constant.i = 5;
+			opcode = _JC_ldc;
+			break;
+		case _JC_lconst_0:
+			info->constant.j = 0;
+			opcode = _JC_ldc2_w;
+			break;
+		case _JC_lconst_1:
+			info->constant.j = 1;
+			opcode = _JC_ldc2_w;
+			break;
+		case _JC_getfield:
+		case _JC_getstatic:
+		case _JC_putfield:
+		case _JC_putstatic:
+		    {
+			const int is_static = opcode == _JC_getstatic
+			    || opcode == _JC_putstatic;
+			_jc_cf_ref *const ref = insn->u.fieldref.field;
+			_jc_field *field;
+			_jc_type *type;
+
+			/* Resolve field's class */
+			if ((type = _jc_load_type(env,
+			    loader, ref->class)) == NULL)
+				goto fail;
+			if (_jc_resolve_add_loader_ref(env,
+			    rinfo, type->loader) != JNI_OK)
+				goto post_fail;
+
+			/* Resolve field */
+			if ((field = _jc_resolve_field(env, type,
+			    ref->name, ref->descriptor, is_static)) == NULL) {
+				env->ex.num = _JC_IncompatibleClassChangeError;
+				goto post_fail;
+			}
+
+			/* Done */
+			info->field = field;
+			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:
+			info->target = insn->u.branch.target;
+			break;
+		case _JC_iinc:
+			info->iinc.index = insn->u.iinc.index;
+			info->iinc.value = insn->u.iinc.value;
+			break;
+		case _JC_invokeinterface:
+		case _JC_invokespecial:
+		case _JC_invokestatic:
+		case _JC_invokevirtual:
+		    {
+			_jc_cf_ref *const ref = insn->u.invoke.method;
+			_jc_method *imethod;
+			_jc_type *type;
+
+			/* Resolve method's class */
+			if ((type = _jc_load_type(env,
+			    loader, ref->class)) == NULL)
+				goto fail;
+			if (_jc_resolve_add_loader_ref(env,
+			    rinfo, type->loader) != JNI_OK)
+				goto post_fail;
+
+			/* Check class vs. interface */
+			if ((opcode == _JC_invokeinterface)
+			    != _JC_ACC_TEST(type, INTERFACE)) {
+				_JC_EX_STORE(env, IncompatibleClassChangeError,
+				    "%s %s.%s%s", _jc_bytecode_names[opcode],
+				    ref->class, ref->name, ref->descriptor);
+				goto post_fail;
+			}
+
+			/* Resolve method */
+			if ((imethod = _jc_resolve_method(env,
+			    type, ref->name, ref->descriptor)) == NULL) {
+				if (opcode == _JC_invokespecial)
+					env->ex.num = _JC_AbstractMethodError;
+				goto post_fail;
+			}
+
+			/* Handle "Miranda methods" (punt) */
+			if (_JC_ACC_TEST(imethod->class, INTERFACE))
+				opcode = _JC_invokeinterface;
+
+			/* Check static-ness and virtual-ness */
+			if (((opcode == _JC_invokestatic)
+			      != _JC_ACC_TEST(imethod, STATIC))
+			    || (opcode != _JC_invokespecial
+			      && *imethod->name == '<')) {
+				_JC_EX_STORE(env, IncompatibleClassChangeError,
+				    "%s.%s%s invoked from %s.%s%s",
+				    imethod->class->name, imethod->name,
+				    imethod->signature, method->class->name,
+				    method->name, method->signature);
+				goto post_fail;
+			}
+
+			/* Handle invokespecial */
+			if (opcode == _JC_invokespecial) do {
+				_jc_method *vmethod;
+				_jc_type *stype;
+
+				/* Check for abstract method */
+				if (_JC_ACC_TEST(imethod, ABSTRACT)) {
+					_JC_EX_STORE(env, AbstractMethodError,
+					    "%s.%s%s", ref->class, ref->name,
+					    ref->descriptor);
+					goto post_fail;
+				}
+
+				/* Perform invokespecial test */
+				if (*imethod->name == '<')
+					break;
+				if (!_JC_ACC_TEST(method->class, SUPER))
+					break;
+				for (stype = method->class->superclass;
+				    stype != imethod->class && stype != NULL;
+				    stype = stype->superclass);
+				if (stype == NULL)
+					break;
+
+				/* Search class hierarchy for matching method */
+				vmethod = NULL;
+				for (stype = method->class->superclass;
+				    stype != NULL; stype = stype->superclass) {
+					if ((vmethod = _jc_get_declared_method(
+					    env, stype, imethod->name,
+					    imethod->signature, _JC_ACC_STATIC,
+					    0)) != NULL)
+						break;
+				}
+
+				/* Not found? */
+				if (vmethod == NULL) {
+					_JC_EX_STORE(env, AbstractMethodError,
+					    "%s.%s%s", ref->class, ref->name,
+					    ref->descriptor);
+					goto post_fail;
+				}
+
+				/* Check for illegal constructor access */
+				if (*imethod->name == '<'
+				    && vmethod->class != imethod->class) {
+					_JC_EX_STORE(env, NoSuchMethodError,
+					    "%s.%s%s", ref->class, ref->name,
+					    ref->descriptor);
+					goto post_fail;
+				}
+
+				/* OK, got it */
+				imethod = vmethod;
+			} while (JNI_FALSE);
+
+			/* Sanity check */
+			_JC_ASSERT((opcode == _JC_invokeinterface)
+			    == _JC_ACC_TEST(imethod->class, INTERFACE));
+
+			/* OK */
+			info->method = imethod;
+			break;
+		    }
+		case _JC_ldc:
+		    {
+			_jc_cf_constant *const c = insn->u.constant;
+
+			switch (c->type) {
+			case CONSTANT_Integer:
+				info->constant.i = c->u.Integer;
+				break;
+			case CONSTANT_Float:
+				info->constant.f = c->u.Float;
+				break;
+			case CONSTANT_String:
+			    {
+				const char *const utf8 = c->u.String;
+				char *copy;
+
+				/* Copy string data */
+				_JC_MUTEX_LOCK(env, loader->mutex);
+				if ((copy = _jc_cl_strdup(env,
+				    loader, utf8)) == NULL) {
+					_JC_MUTEX_UNLOCK(env, loader->mutex);
+					goto post_fail;
+				}
+				_JC_MUTEX_UNLOCK(env, loader->mutex);
+				info->utf8 = copy;
+
+				/* Set opcode */
+				opcode = _JC_ldc_string;
+				break;
+			    }
+			case CONSTANT_Class:		/* JSR 202 */
+			    {
+				_jc_type *type;
+
+				if ((type = _jc_load_type(env,
+				    loader, c->u.Class)) == NULL)
+					goto fail;
+				if (_jc_resolve_add_loader_ref(env,
+				    rinfo, type->loader) != JNI_OK)
+					goto post_fail;
+				_JC_ASSERT(type->instance != NULL);
+				info->constant.l = type->instance;
+				break;
+			    }
+			default:
+				_JC_ASSERT(JNI_FALSE);
+			}
+			break;
+		    }
+		case _JC_ldc2_w:
+		    {
+			_jc_cf_constant *const c = insn->u.constant;
+
+			switch (c->type) {
+			case CONSTANT_Long:
+				info->constant.j = c->u.Long;
+				break;
+			case CONSTANT_Double:
+				info->constant.d = c->u.Double;
+				break;
+			default:
+				_JC_ASSERT(JNI_FALSE);
+			}
+			break;
+		    }
+		case _JC_lookupswitch:
+		    {
+			_jc_cf_lookupswitch *const csw = insn->u.lookupswitch;
+			_jc_lookupswitch *sw;
+			int j;
+
+			/* Allocate structure */
+			_JC_MUTEX_LOCK(env, loader->mutex);
+			if ((sw = _jc_cl_alloc(env, loader, sizeof(*sw)
+			    + csw->num_pairs * sizeof(*sw->pairs))) == NULL) {
+				_JC_MUTEX_UNLOCK(env, loader->mutex);
+				goto post_fail;
+			}
+			_JC_MUTEX_UNLOCK(env, loader->mutex);
+
+			/* Copy info */
+			sw->default_target = csw->default_target;
+			sw->num_pairs = csw->num_pairs;
+			for (j = 0; j < sw->num_pairs; j++) {
+				_jc_cf_lookup *const clup = &csw->pairs[j];
+				_jc_lookup *const lup = &sw->pairs[j];
+
+				lup->match = clup->match;
+				lup->target = clup->target;
+			}
+
+			/* Done */
+			info->lookupswitch = sw;
+			break;
+		    }
+		case _JC_multianewarray:
+			if ((info->multianewarray.type = _jc_load_type(env,
+			    loader, insn->u.multianewarray.type)) == NULL)
+				goto fail;
+			if (_jc_resolve_add_loader_ref(env, rinfo,
+			    info->multianewarray.type->loader) != JNI_OK)
+				goto post_fail;
+			info->multianewarray.dims = insn->u.multianewarray.dims;
+			break;
+		case _JC_newarray:
+			info->type = vm->boot.types.prim_array[
+			    insn->u.newarray.type];
+			break;
+		case _JC_tableswitch:
+		    {
+			_jc_cf_tableswitch *const csw = insn->u.tableswitch;
+			const int num_targets = csw->high - csw->low + 1;
+			_jc_tableswitch *sw;
+			int j;
+
+			/* Allocate structure */
+			_JC_MUTEX_LOCK(env, loader->mutex);
+			if ((sw = _jc_cl_alloc(env, loader, sizeof(*sw)
+			    + num_targets * sizeof(*sw->targets))) == NULL) {
+				_JC_MUTEX_UNLOCK(env, loader->mutex);
+				goto post_fail;
+			}
+			_JC_MUTEX_UNLOCK(env, loader->mutex);
+
+			/* Copy info */
+			sw->default_target = csw->default_target;
+			sw->low = csw->low;
+			sw->high = csw->high;
+			for (j = 0; j < num_targets; j++)
+				sw->targets[j] = csw->targets[j];
+
+			/* Done */
+			info->tableswitch = sw;
+			break;
+		    }
+		default:
+			break;
+		}
+
+		/* Copy opcode */
+		interp->opcodes[i] = opcode;
+	}
+
+	/* Done */
+	return JNI_OK;
+
+post_fail:
+	_jc_post_exception_info(env);
+
+fail:
+	/* Give back class loader memory */
+	if (!mutex_locked) {
+		_JC_MUTEX_LOCK(env, loader->mutex);
+		mutex_locked = JNI_TRUE;
+	}
+	_jc_cl_unalloc(loader, &interp->traps,
+	    code->num_traps * sizeof(*interp->traps));
+	_jc_cl_unalloc(loader, &interp->info,
+	    code->num_insns * sizeof(*interp->info));
+	_jc_cl_unalloc(loader, &interp->opcodes,
+	    code->num_insns * sizeof(*interp->opcodes));
+	_JC_MUTEX_UNLOCK(env, loader->mutex);
+
+	/* Free parsed code */
+	_jc_destroy_code(code);
+
+	/* Done */
+	return JNI_ERR;
+}
+
+/*
+ * Create the interface method hash table and "quick" hash table.
+ */
+static jint
+_jc_derive_imethod_tables(_jc_env *env, _jc_type *type)
+{
+	int heads[_JC_IMETHOD_HASHSIZE];
+	int num_singleton_buckets;
+	int num_nonempty_buckets;
+	_jc_method **methods;
+	int max_methods;
+	int num_methods;
+	_jc_type *stype;
+	const void **qptr;
+	_jc_method **ptr;
+	int *follows;
+	int i;
+
+	/* Sanity check */
+	_JC_ASSERT(!_JC_ACC_TEST(type, INTERFACE));
+
+	/* Count the number of interface methods (including duplicates) */
+	max_methods = 0;
+	for (stype = type; stype != NULL; stype = stype->superclass)
+		max_methods += _jc_add_iface_methods(stype, NULL);
+
+	/* Anything to do? */
+	if (max_methods == 0) {
+		type->imethod_quick_table = _jc_empty_quick_table;
+		type->imethod_hash_table = _jc_empty_imethod_table;
+		return JNI_OK;
+	}
+
+	/* Allocate temporary array */
+	if ((methods = _JC_STACK_ALLOC(env,
+	    max_methods * sizeof(*methods))) == NULL) {
+		_jc_post_exception_info(env);
+		return JNI_ERR;
+	}
+
+	/* Gather interface methods */
+	i = 0;
+	for (stype = type; stype != NULL; stype = stype->superclass)
+		i += _jc_add_iface_methods(stype, &methods[i]);
+	_JC_ASSERT(i == max_methods);
+
+	/* Resolve all interface methods */
+	for (i = 0; i < max_methods; i++) {
+		_jc_method *const imethod = methods[i];
+		_jc_method *method = NULL;
+		_jc_type *stype;
+
+		/* Resolve the interface method via name & signature */
+		for (stype = type; stype != NULL; stype = stype->superclass) {
+			method = _jc_method_lookup(stype, imethod);
+			if (method != NULL && _JC_ACC_TEST(method, PUBLIC))
+				break;
+		}
+		if (stype == NULL) {
+			_jc_post_exception_msg(env, _JC_NoSuchMethodError,
+			    "%s.%s%s", imethod->class->name, imethod->name,
+			    imethod->signature);
+			return JNI_ERR;
+		}
+
+		/* Add method to our list */
+		methods[i] = method;
+	}
+
+	/* Sort methods by name & signature */
+	qsort(methods, max_methods, sizeof(*methods), _jc_method_sorter);
+
+	/* Eliminate redundancies (methods declared in multiple interfaces) */
+	num_methods = max_methods;
+	for (i = 0; i < num_methods - 1; i++) {
+		_jc_method *const current = methods[i];
+		_jc_method *const next = methods[i + 1];
+
+		if (current != next)
+			continue;
+		memmove(methods + i, methods + i + 1,
+		    (--num_methods - i) * sizeof(*methods));
+	}
+
+	/* Allocate links for hash buckets */
+	if ((follows = _JC_STACK_ALLOC(env,
+	    num_methods * sizeof(*follows))) == NULL) {
+		_jc_post_exception_info(env);
+		return JNI_ERR;
+	}
+
+	/* Sort interface methods into hash buckets */
+	num_nonempty_buckets = 0;
+	num_singleton_buckets = 0;
+	memset(&heads, ~0, sizeof(heads));
+	for (i = 0; i < num_methods; i++) {
+		_jc_method *const method = methods[i];
+		const int bucket = (int)method->signature_hash
+		    & (_JC_IMETHOD_HASHSIZE - 1);
+
+		/* Keep track of the number of nonempty and singleton buckets */
+		if (heads[bucket] == -1) {
+			num_singleton_buckets++;
+			num_nonempty_buckets++;
+		} else if (follows[heads[bucket]] == -1)
+			num_singleton_buckets--;
+
+		/* Add method to bucket */
+		follows[i] = heads[bucket];
+		heads[bucket] = i;
+	}
+
+	/* Allocate "quick" table, hash buckets, and room for bucket lists */
+	_JC_MUTEX_LOCK(env, type->loader->mutex);
+	if ((type->imethod_hash_table = _jc_cl_zalloc(env, type->loader,
+	    _JC_IMETHOD_HASHSIZE * sizeof(*type->imethod_hash_table)
+	    + (num_singleton_buckets > 0 ? 
+	      _JC_IMETHOD_HASHSIZE * sizeof(*type->imethod_quick_table) : 0)
+	    + (num_methods + num_nonempty_buckets)
+	      * sizeof(**type->imethod_hash_table))) == NULL) {
+		_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+		_jc_post_exception_info(env);
+		return JNI_ERR;
+	}
+	_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+
+	/* Fill in the "quick" hash table (if any) */
+	qptr = (const void **)(type->imethod_hash_table + _JC_IMETHOD_HASHSIZE);
+	if (num_singleton_buckets > 0) {
+		type->imethod_quick_table = qptr;
+		qptr += _JC_IMETHOD_HASHSIZE;
+		for (i = 0; i < _JC_IMETHOD_HASHSIZE; i++) {
+			if (heads[i] != -1 && follows[heads[i]] == -1) {
+				type->imethod_quick_table[i]
+				    = methods[heads[i]]->function;
+			}
+		}
+	} else
+		type->imethod_quick_table = _jc_empty_quick_table;
+
+	/* Fill in the hash table */
+	ptr = (_jc_method **)qptr;
+	for (i = 0; i < _JC_IMETHOD_HASHSIZE; i++) {
+		int next = heads[i];
+
+		/* Skip this if bucket is empty */
+		if (next == -1)
+			continue;
+
+		/* Add entries in the hash bucket */
+		type->imethod_hash_table[i] = ptr;
+		while (next != -1) {
+			*ptr++ = methods[next];
+			next = follows[next];
+		}
+
+		/* Terminate entry list with a NULL pointer */
+		ptr++;
+	}
+
+	/* Sanity check */
+	_JC_ASSERT((char *)ptr == (char *)type->imethod_hash_table
+	    + _JC_IMETHOD_HASHSIZE * sizeof(*type->imethod_hash_table)
+	    + (num_singleton_buckets > 0 ? 
+	      _JC_IMETHOD_HASHSIZE * sizeof(*type->imethod_quick_table) : 0)
+	    + (num_methods + num_nonempty_buckets)
+	      * sizeof(**type->imethod_hash_table));
+
+	/* Done */
+	return JNI_OK;
+}
+
+/*
+ * Create the instanceof table.
+ */
+static jint
+_jc_derive_instanceof_table(_jc_env *env, _jc_type *const type)
+{
+	_jc_nonarray_type *const ntype = &type->u.nonarray;
+	int heads[_JC_INSTANCEOF_HASHSIZE];
+	int num_nonempty_buckets;
+	_jc_type **types = NULL;
+	_jc_type *stype;
+	int num_types;
+	_jc_type **ptr;
+	int *follows;
+	int i;
+
+again:
+	/* Scan instanceof types from superclasses & superinterfaces */
+	num_types = 0;
+	for (stype = type; stype != NULL; stype = stype->superclass) {
+		int j;
+
+		/* Inherit instanceof's from each implemented interface */
+		for (j = 0; j < stype->num_interfaces; j++) {
+			_jc_type *const iftype = stype->interfaces[j];
+			int bkt;
+
+			for (bkt = 0; bkt < _JC_INSTANCEOF_HASHSIZE; bkt++) {
+				ptr = iftype->u.nonarray
+				    .instanceof_hash_table[bkt];
+				if (ptr == NULL)
+					continue;
+				while (*ptr != NULL) {
+					if (types != NULL)
+						types[num_types] = *ptr;
+					num_types++;
+					ptr++;
+				}
+			}
+		}
+
+		/* Add one for this (super)class */
+		if (types != NULL)
+			types[num_types] = stype;
+		num_types++;
+	}
+
+	/* Allocate temporary array and scan again */
+	if (types == NULL) {
+		if ((types = _JC_STACK_ALLOC(env,
+		    num_types * sizeof(*types))) == NULL) {
+			_jc_post_exception_info(env);
+			return JNI_ERR;
+		}
+		goto again;
+	}
+
+	/* Sort types */
+	qsort(types, num_types, sizeof(*types), _jc_type_sorter);
+
+	/* Eliminate redundancies */
+	for (i = 0; i < num_types - 1; i++) {
+		_jc_type *const current = types[i];
+		_jc_type *const next = types[i + 1];
+
+		if (current != next)
+			continue;
+		memmove(types + i, types + i + 1,
+		    (--num_types - i) * sizeof(*types));
+	}
+
+	/* Allocate links for hash buckets */
+	if ((follows = _JC_STACK_ALLOC(env,
+	    num_types * sizeof(*follows))) == NULL) {
+		_jc_post_exception_info(env);
+		return JNI_ERR;
+	}
+
+	/* Put instanceof types into hash buckets */
+	num_nonempty_buckets = 0;
+	memset(&heads, ~0, sizeof(heads));
+	for (i = 0; i < num_types; i++) {
+		_jc_type *const type = types[i];
+		const int bucket = (int)type->u.nonarray.hash
+		    & (_JC_INSTANCEOF_HASHSIZE - 1);
+
+		/* Keep track of the number of nonempty buckets */
+		if (heads[bucket] == -1)
+			num_nonempty_buckets++;
+
+		/* Add type to bucket */
+		follows[i] = heads[bucket];
+		heads[bucket] = i;
+	}
+
+	/* Allocate hash buckets, and room for bucket lists */
+	_JC_MUTEX_LOCK(env, type->loader->mutex);
+	if ((ntype->instanceof_hash_table = _jc_cl_zalloc(env, type->loader,
+	    _JC_INSTANCEOF_HASHSIZE * sizeof(*ntype->instanceof_hash_table)
+	    + (num_types + num_nonempty_buckets)
+	      * sizeof(**ntype->instanceof_hash_table))) == NULL) {
+		_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+		_jc_post_exception_info(env);
+		return JNI_ERR;
+	}
+	_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+
+	/* Fill in the hash table */
+	ptr = (_jc_type **)(ntype->instanceof_hash_table
+	    + _JC_INSTANCEOF_HASHSIZE);
+	for (i = 0; i < _JC_INSTANCEOF_HASHSIZE; i++) {
+		int next = heads[i];
+
+		/* Skip this if bucket is empty */
+		if (next == -1)
+			continue;
+
+		/* Add entries in the hash bucket */
+		ntype->instanceof_hash_table[i] = ptr;
+		while (next != -1) {
+			*ptr++ = types[next];
+			next = follows[next];
+		}
+
+		/* Terminate entry list with a NULL pointer */
+		ptr++;
+	}
+
+	/* Sanity check */
+	_JC_ASSERT((char *)ptr == (char *)ntype->instanceof_hash_table
+	    + _JC_INSTANCEOF_HASHSIZE * sizeof(*ntype->instanceof_hash_table)
+	    + (num_types + num_nonempty_buckets)
+	      * sizeof(**ntype->instanceof_hash_table));
+
+	/* Done */
+	return JNI_OK;
+}
+
+/*
+ * Resolve inner classes.
+ */
+static jint
+_jc_resolve_inner_classes(_jc_env *env, _jc_type *type, _jc_resolve_info *info)
+{
+	_jc_nonarray_type *const ntype = &type->u.nonarray;
+	_jc_classfile *const cfile = ntype->u.cfile;
+	_jc_cf_inner_classes *const cinners = cfile->inner_classes;
+	int count;
+	int i;
+
+	/* Sanity check */
+	_JC_ASSERT(ntype->inner_classes == NULL);
+
+	/* Any inner class info? */
+	if (cinners == NULL)
+		return JNI_OK;
+
+again:
+	/* Find my inner classes */
+	count = 0;
+	for (i = 0; i < cinners->num_classes; i++) {
+		_jc_cf_inner_class *const cinner = &cinners->classes[i];
+
+		/* Look for classes that are inner to this class */
+		if (cinner->outer != NULL
+		    && strcmp(cinner->outer, type->name) != 0)
+			continue;
+		if (cinner->inner == NULL
+		    || strcmp(cinner->inner, type->name) == 0)
+			continue;
+
+		/* Resolve the inner class */
+		if (ntype->inner_classes != NULL) {
+			_jc_inner_class *const inner
+			    = &ntype->inner_classes[count];
+
+			/* Resolve inner class type */
+			if ((inner->type = _jc_load_type(env,
+			    type->loader, cinner->inner)) == NULL)
+				return JNI_ERR;
+			if (_jc_resolve_add_loader_ref(env, info,
+			    inner->type->loader) != JNI_OK) {
+				_jc_post_exception_info(env);
+				return JNI_ERR;
+			}
+
+			/* Set access flags */
+			inner->access_flags = cinner->access_flags;
+
+			/* Set back pointer */
+			inner->type->u.nonarray.outer_class = type;
+		}
+		count++;
+	}
+
+	/* Allocate memory and rescan */
+	if (ntype->inner_classes == NULL) {
+		_JC_MUTEX_LOCK(env, type->loader->mutex);
+		if ((ntype->inner_classes = _jc_cl_alloc(env, type->loader,
+		    count * sizeof(*ntype->inner_classes))) == NULL) {
+			_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+			_jc_post_exception_info(env);
+			return JNI_ERR;
+		}
+		_JC_MUTEX_UNLOCK(env, type->loader->mutex);
+		goto again;
+	}
+
+	/* Set count */
+	ntype->num_inner_classes = count;
+
+	/* Done */
+	return JNI_OK;
+}
+
+/*
+ * Find all interface methods a class supposedly implements.
+ */
+static int
+_jc_add_iface_methods(_jc_type *type, _jc_method **methods)
+{
+	int count = 0;
+	int i;
+
+	/* Check all declared interfaces */
+	for (i = 0; i < type->num_interfaces; i++) {
+		_jc_type *const iftype = type->interfaces[i];
+		int num;
+		int j;
+
+		/* Add interface methods for this interface */
+		for (j = 0; j < iftype->u.nonarray.num_methods; j++) {
+			_jc_method *const imethod
+			    = iftype->u.nonarray.methods[j];
+
+			/* Add interface method */
+			if (!_JC_ACC_TEST(imethod, STATIC)) {
+				if (methods != NULL)
+					*methods++ = imethod;
+				count++;
+			}
+		}
+
+		/* Recurse on interface */
+		num = _jc_add_iface_methods(iftype, methods);
+		if (methods != NULL)
+			methods += num;
+		count += num;
+	}
+
+	/* Recurse on superclass */
+	if (type->superclass != NULL)
+		count += _jc_add_iface_methods(type->superclass, methods);
+
+	/* Done */
+	return count;
+}
+
+/*
+ * Lookup an instance method.
+ */
+_jc_method *
+_jc_method_lookup(_jc_type *type, _jc_method *key)
+{
+	_jc_nonarray_type *const ntype = &type->u.nonarray;
+	_jc_method **methodp;
+
+	/* Sanity check */
+	_JC_ASSERT(*key->name != '<');
+
+	/* Search */
+	methodp = bsearch(&key, ntype->methods, ntype->num_methods,
+	    sizeof(*ntype->methods), _jc_method_sorter);
+	if (methodp != NULL && !_JC_ACC_TEST(*methodp, STATIC))
+		return *methodp;
+
+	/* Not found */
+	return NULL;
+}
+
+/*
+ * Sorts method pointers 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_method *const method1 = *((_jc_method **)item1);
+        const _jc_method *const method2 = *((_jc_method **)item2);
+	int diff;
+
+	if ((diff = strcmp(method1->name, method2->name)) != 0)
+		return diff;
+	return strcmp(method1->signature, method2->signature);
+}
+
+/*
+ * Sorts type pointers by name.
+ */
+static int
+_jc_type_sorter(const void *item1, const void *item2)
+{
+        const _jc_type *const type1 = *((_jc_type **)item1);
+        const _jc_type *const type2 = *((_jc_type **)item2);
+
+	return strcmp(type1->name, type2->name);
+}
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/signals.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/signals.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/signals.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/signals.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,326 @@
+
+/*
+ * 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: signals.c,v 1.9 2005/02/23 22:48:16 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/* Internal definitions */
+enum {
+	_JC_SIGNAL_SEGV,
+	_JC_SIGNAL_BUS,
+	_JC_SIGNAL_FPE,
+	_JC_SIGNAL_USR1,
+	_JC_SIGNAL_MAX
+};
+
+/* Internal functions */
+static void	_jc_signal_action(int sig_num,
+			siginfo_t *info, ucontext_t *uctx);
+static void	_jc_signal_fallthrough(int sig_index,
+			siginfo_t *info, ucontext_t *uctx);
+
+/* Internal variables */
+static struct	sigaction _jc_previous[_JC_SIGNAL_MAX];
+static const	int _jc_signals[_JC_SIGNAL_MAX] = {
+	[_JC_SIGNAL_SEGV]=	SIGSEGV,
+#ifdef SIGBUS
+	[_JC_SIGNAL_BUS]=	SIGBUS,
+#endif
+	[_JC_SIGNAL_FPE]=	SIGFPE,
+	[_JC_SIGNAL_USR1]=	SIGUSR1,
+};
+
+/*
+ * Initialize signal handlers.
+ */
+jint
+_jc_init_signals(void)
+{
+	struct sigaction sa;
+	int i;
+
+	/* We want all the context no blocking of signals when handled */
+	memset(&sa, 0, sizeof(sa));
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_SIGINFO | SA_NODEFER;
+	sa.sa_sigaction = (void (*)(int, siginfo_t *, void *))_jc_signal_action;
+
+	/* Redirect the signals but save the previous handlers */
+	for (i = 0; i < _JC_SIGNAL_MAX; i++) {
+		if (_jc_signals[i] == 0)
+			continue;
+		if (sigaction(_jc_signals[i], &sa, &_jc_previous[i]) == -1) {
+			fprintf(stderr, "jc: sigaction: %s", strerror(errno));
+			while (i-- > 0) {
+				(void)sigaction(_jc_signals[i],
+				    &_jc_previous[i], NULL);
+			}
+			return JNI_ERR;
+		}
+	}
+
+	/* Done */
+	return JNI_OK;
+}
+
+/*
+ * Restore original signal handlers.
+ */
+void
+_jc_restore_signals(void)
+{
+	int i;
+
+	/* Restore previous signal handlers */
+	for (i = 0; i < _JC_SIGNAL_MAX; i++) {
+		if (_jc_signals[i] == 0)
+			continue;
+		if (sigaction(_jc_signals[i], &_jc_previous[i], NULL) == -1)
+			fprintf(stderr, "jc: sigaction: %s", strerror(errno));
+	}
+}
+
+/*
+ * Handle a signal.
+ */
+static void
+_jc_signal_action(int sig_num, siginfo_t *info, ucontext_t *uctx)
+{
+	_jc_env *const env = _jc_get_current_env();
+	_jc_exec_stack *estack;
+	_jc_jvm *vm = NULL;
+	int sig_index;
+
+	/* Get signal index */
+	for (sig_index = 0; sig_index < _JC_SIGNAL_MAX
+	    && sig_num != _jc_signals[sig_index]; sig_index++);
+	if (sig_index == _JC_SIGNAL_MAX)
+		goto unexpected;
+
+	/*
+	 * Handle SIGUSR1 specially
+	 * XXX bug: signal could occur while vm mutex already held.
+	 */
+	if (sig_index == _JC_SIGNAL_USR1) {
+		JavaVM *jvm;
+		jsize nvms;
+
+		/* We can only debug the first VM */
+		if (JNI_GetCreatedJavaVMs(&jvm, 1, &nvms) != JNI_OK) {
+			_jc_signal_fallthrough(sig_index, info, uctx);
+			return;
+		}
+		if (nvms < 1)
+			return;
+		vm = _JC_JNI2JVM(jvm);
+		if (vm->debug_thread != NULL)
+			_jc_thread_interrupt_instance(vm, *vm->debug_thread);
+		return;
+	}
+
+	/* Is this thread attached to a VM? */
+	if (env == NULL) {
+		_jc_signal_fallthrough(sig_index, info, uctx);
+		return;
+	}
+	vm = env->vm;
+
+	/* Check for double signal */
+	if (env->handling_signal)
+		_jc_fatal_error(vm, "caught double signal %d", sig_num);
+	env->handling_signal = JNI_TRUE;
+
+	/* What state is the thread in? */
+	switch (env->status) {
+	case _JC_THRDSTAT_RUNNING_NONJAVA:
+	case _JC_THRDSTAT_HALTING_NONJAVA:
+		env->handling_signal = JNI_FALSE;
+		_jc_signal_fallthrough(sig_index, info, uctx);
+		return;
+	case _JC_THRDSTAT_RUNNING_NORMAL:
+	case _JC_THRDSTAT_HALTING_NORMAL:
+		break;
+	default:
+		goto unexpected;
+	}
+
+	/*
+	 * If this signal occured while there is no Java stack, then 
+	 * then it didn't occur in Java code and so was unexpected.
+	 */
+	if (env->java_stack == NULL)
+		goto unexpected;
+
+	/* If the signal occurred while interpeting, it was unexpected */
+	if (env->java_stack->interp)
+		goto unexpected;
+	estack = (_jc_exec_stack *)env->java_stack;
+
+	/*
+	 * If the signal occurred while the Java stack was clipped,
+	 * then it didn't occur in Java code and so was unexpected.
+	 */
+	if (estack->pc != NULL)
+		goto unexpected;
+
+#if !HAVE_GETCONTEXT
+	/* Poor man's getcontext() using signals */
+	if (env->ctx != NULL) {
+		*env->ctx = uctx->uc_mcontext;
+		env->handling_signal = JNI_FALSE;
+		return;
+	}
+#endif
+
+	/*
+	 * Clip the top of the Java stack. We must do this here because it's
+	 * not possible to follow saved frame pointers across a signal frame.
+	 * That is, any signal creates a new contiguous Java stack region.
+	 */
+	_jc_stack_clip_ctx(env, &uctx->uc_mcontext);
+
+	/*
+	 * Adjust PC to be consistent with our 'frame' semantics.
+	 * Signals leave the PC pointing to the triggering instruction
+	 * rather than the 'return address' instruction.
+	 */
+	estack->pc = (const char *)estack->pc + 1;
+
+#ifndef NDEBUG
+	/*
+	 * For signals used to generate Java exceptions, verify that the signal
+	 * happened within a Java method and not some internal libjc function.
+	 */
+	switch (sig_index) {
+	case _JC_SIGNAL_SEGV:
+	case _JC_SIGNAL_BUS:
+	case _JC_SIGNAL_FPE:
+	    {
+		_jc_method *method;
+		_jc_method key;
+
+		key.function = key.u.exec.function_end = estack->pc;
+		_JC_MUTEX_LOCK(env, vm->mutex);
+		method = _jc_splay_find(&vm->method_tree, &key);
+		_JC_MUTEX_UNLOCK(env, vm->mutex);
+		if (method == NULL)
+			goto unexpected;
+		break;
+	    }
+	default:
+		break;
+	}
+#endif
+
+	/* Take the appropriate action */
+	switch (sig_index) {
+
+	/*
+	 * For SEGV/BUS, there are three cases:
+	 *
+	 * 1. null object dereference (generate NullPointerException)
+	 * 2. touched stack guard page (generate StackOverflowError)
+	 * 3. doing periodic thread check (call _jc_thread_check())
+	 */
+	case _JC_SIGNAL_SEGV:
+	case _JC_SIGNAL_BUS:
+	    {
+		jboolean stack_overflow;
+		const void *fault_addr;
+		unsigned int diff;
+
+		/* Get the fault address */
+		fault_addr = _jc_signal_fault_address(sig_num, info, uctx);
+
+		/* Test for thread check address */
+		if ((char *)fault_addr >= vm->check_address
+		    && (char *)fault_addr < vm->check_address + _JC_PAGE_SIZE) {
+			env->handling_signal = JNI_FALSE;
+			if (_jc_thread_check(env) != JNI_OK)
+				_jc_throw_exception(env);
+			_jc_stack_unclip(env);
+			return;
+		}
+
+		/*
+		 * Test for stack overflow. The signal happens when the
+		 * _JC_STACK_OVERFLOW_CHECK() macro probes past the top
+		 * of the stack and hits the guard page.  If the fault
+		 * address is "above" (stack relative) but "close to"
+		 * (within the stack overflow margin) the current stack
+		 * address then that must be what happened; otherwise, it
+		 * must just be a normal NullPointerException.
+		 */
+#if _JC_DOWNWARD_STACK
+		diff = (char *)&sig_num - (char *)fault_addr;
+#else
+		diff = (char *)fault_addr - (char *)&sig_num;
+#endif
+		stack_overflow = (diff <= _JC_STACK_OVERFLOW_MARGIN);
+
+		/* Throw the appropriate exception */
+		env->handling_signal = JNI_FALSE;
+		_jc_post_exception(env, stack_overflow ?
+		    _JC_StackOverflowError : _JC_NullPointerException);
+		_jc_throw_exception(env);
+	    }
+
+	case _JC_SIGNAL_FPE:
+		if (info->si_code != FPE_INTDIV)
+			break;
+		env->handling_signal = JNI_FALSE;
+		_jc_post_exception(env, _JC_ArithmeticException);
+		_jc_throw_exception(env);
+	default:
+		break;
+	}
+
+unexpected:
+	/* Unexpected signal */
+	_jc_fatal_error(vm, "caught unexpected signal %d in thread %p",
+	    sig_num, env);
+}
+
+/*
+ * Handle a signal by falling through to the previously defined handler.
+ */
+static void
+_jc_signal_fallthrough(int sig_index, siginfo_t *info, ucontext_t *uctx)
+{
+	struct sigaction *const sa = &_jc_previous[sig_index];
+	const int sig_num = _jc_signals[sig_index];
+
+	/* Sanity check */
+	_JC_ASSERT(sa->sa_sigaction != (void *)_jc_signal_action);
+
+	/* Invoke previous handler */
+	if ((sa->sa_flags & SA_SIGINFO) != 0) {
+		(*sa->sa_sigaction)(sig_num, info, uctx);
+		return;
+	}
+	if (sa->sa_handler == SIG_IGN)
+		return;
+	if (sa->sa_handler != SIG_DFL) {
+		(*sa->sa_handler)(sig_num);
+		return;
+	}
+	_jc_fatal_error(NULL, "caught unexpected signal %d",
+	    _jc_signals[sig_index]);
+}
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/splay.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/splay.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/splay.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/splay.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,269 @@
+
+/*
+ * 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: splay.c,v 1.4 2004/07/26 01:41:37 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/* Internal functions */
+static _jc_splay_node	*_jc_splay(_jc_splay_tree *tree, _jc_splay_node *node,
+				void *key, int *match);
+
+/*
+ * Initialize a new splay tree.
+ */
+void
+_jc_splay_init(_jc_splay_tree *tree, _jc_splay_cmp_t *compare, size_t offset)
+{
+	memset(tree, 0, sizeof(*tree));
+	tree->offset = offset;
+	tree->compare = compare;
+}
+
+/*
+ * Splay the node that matches 'key' to the top of the sub-tree rooted
+ * at 'node' if found, or is the previous node we'd find if not found.
+ */
+static _jc_splay_node *
+_jc_splay(_jc_splay_tree *tree, _jc_splay_node *node, void *key, int *match)
+{
+	_jc_splay_node *temp;
+	_jc_splay_node *left;
+	_jc_splay_node *right;
+	_jc_splay_node fixed;
+	int diff;
+
+	/* Initialize */
+	memset(&fixed, 0, sizeof(fixed));
+	left = right = &fixed;
+	*match = 0;
+
+	/* Handle empty tree */
+	if (node == NULL)
+		return NULL;
+
+	/* Search & splay */
+	while (node != NULL) {
+		_JC_ASSERT(node->inserted);
+		diff = (*tree->compare)(key, _JC_NODE2ITEM(tree, node));
+		if (diff < 0) {
+			if (node->left == NULL)
+				break;
+			diff = (*tree->compare)(key,
+			    _JC_NODE2ITEM(tree, node->left));
+			if (diff < 0) {
+				temp = node->left;	/* rotate right */
+				node->left = temp->right;
+				temp->right = node;
+				node = temp;
+				if (node->left == NULL)
+					break;
+			}
+			right->left = node;		/* link right */
+			right = node;
+			node = node->left;
+			continue;
+		}
+		if (diff > 0) {
+			if (node->right == NULL)
+				break;
+			diff = (*tree->compare)(key,
+			    _JC_NODE2ITEM(tree, node->right));
+			if (diff > 0) {
+				temp = node->right;	/* rotate left */
+				node->right = temp->left;
+				temp->left = node;
+				node = temp;
+				if (node->right == NULL)
+					break;
+			}
+			left->right = node;		/* link left */
+			left = node;
+			node = node->right;
+			continue;
+		}
+		*match = 1;
+		break;
+	}
+
+	/* Reassemble */
+	left->right = node->left;
+	right->left = node->right;
+	node->left = fixed.right;
+	node->right = fixed.left;
+
+	/* Return the new root */
+	return node;
+}
+
+/* 
+ * Find an item in the tree.
+ */
+void *
+_jc_splay_find(_jc_splay_tree *tree, void *item)
+{
+	int match;
+
+	/* Sanity check */
+	_JC_ASSERT(item != NULL);
+
+	/* Search for item */
+	tree->root = _jc_splay(tree, tree->root, item, &match);
+	return match ? _JC_NODE2ITEM(tree, tree->root) : NULL;
+}
+
+/*
+ * Insert an item into the tree. It must not already be there.
+ */
+void
+_jc_splay_insert(_jc_splay_tree *tree, void *item)
+{
+	_jc_splay_node *const new_node = _JC_ITEM2NODE(tree, item);
+	_jc_splay_node *root = tree->root;
+	int match;
+	int diff;
+
+	/* Sanity check */
+	_JC_ASSERT(new_node->left == NULL);
+	_JC_ASSERT(new_node->right == NULL);
+	_JC_ASSERT(!new_node->inserted);
+
+	/* Handle empty tree */
+	if (root == NULL) {
+		new_node->left = NULL;
+		new_node->right = NULL;
+		goto done;
+	}
+
+	/* Splay insertion point up to the root */
+	root = tree->root = _jc_splay(tree, root, item, &match);
+	_JC_ASSERT(!match);
+
+	/* See where to add new node */
+	diff = (*tree->compare)(item, _JC_NODE2ITEM(tree, tree->root));
+	_JC_ASSERT(diff != 0);
+	if (diff < 0) {
+		new_node->left = root->left;
+		new_node->right = root;
+		root->left = NULL;
+	} else {
+		new_node->right = root->right;
+		new_node->left = root;
+		root->right = NULL;
+	}
+
+done:
+	/* Done */
+#ifndef NDEBUG
+	new_node->inserted = 1;
+#endif
+	tree->root = new_node;
+	tree->size++;
+}
+
+/*
+ * Remove an item from the tree. It must be in the tree.
+ */
+void
+_jc_splay_remove(_jc_splay_tree *tree, void *item)
+{
+#ifndef NDEBUG
+	_jc_splay_node *const node = _JC_ITEM2NODE(tree, item);
+#endif
+	_jc_splay_node *root = tree->root;
+	_jc_splay_node *new_root;
+	int found;
+
+	/* Sanity check */
+	_JC_ASSERT(node->inserted);
+	_JC_ASSERT(root != NULL);
+	_JC_ASSERT(tree->size > 0);
+
+	/* Splay node to the root */
+	root = _jc_splay(tree, root, item, &found);
+	_JC_ASSERT(found);
+
+	/* Remove node */
+	if (root->left == NULL)
+		new_root = root->right;
+	else {
+		new_root = _jc_splay(tree, root->left, item, &found);
+		new_root->right = root->right;
+	}
+
+#ifndef NDEBUG
+	/* Nuke removed node */
+	memset(node, 0, sizeof(*node));
+#endif
+
+	/* Done */
+	tree->root = new_root;
+	tree->size--;
+}
+
+/*
+ * Generate a sorted list of all items in the tree.
+ *
+ * This assumes "items" points to an array of length at least tree->size.
+ *
+ * This algorithm uses the list memory as a stack, which grows upward from
+ * the start of the list, while simultaneously building the list itself,
+ * which grows downward from the end of the list. Because each node can
+ * appear only once in either the stack or the list, the two never meet.
+ *
+ * The point of doing this is to avoid the stack usage that would result
+ * from the typical recursive function approach.
+ */
+void
+_jc_splay_list(_jc_splay_tree *tree, void **items)
+{
+	_jc_splay_node **const stack = (_jc_splay_node **)items;
+	_jc_splay_node *node;
+	int depth;		/* next slot for stack (grows upward) */
+	int next;		/* next slot for list (grows downward) */
+	int i;
+
+	/* Initialize search stack */
+	depth = 0;
+	next = tree->size;
+	node = tree->root;
+
+	/* Recursively expand stack */
+	while (depth > 0 || node != NULL) {
+
+		/* Sanity check */
+		_JC_ASSERT(depth <= next);
+
+		/* Travelling down the tree */
+		if (node != NULL) {
+			stack[depth++] = node;
+			node = node->right;
+			continue;
+		}
+
+		/* Travelling back up the tree */
+		node = stack[--depth];
+		stack[--next] = node;
+		node = node->left;
+	}
+
+	/* Convert nodes to items */
+	for (i = 0; i < tree->size; i++)
+		items[i] = _JC_NODE2ITEM(tree, stack[i]);
+}
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/stack.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/stack.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/stack.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/stack.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,486 @@
+
+/*
+ * 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: stack.c,v 1.19 2005/04/04 14:04:05 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/*
+ * Clip the current top of the Java stack. A thread's Java stack is
+ * a sequence of disconnected chunks of contiguous C stack frames
+ * intersperced with non-crawlable stuff like native stack frames
+ * and signal frames. Each chunk also contains a set of saved registers
+ * which are scanned conservatively during garbage collection.
+ *
+ * This sets the top frame to be the calling function's calling function.
+ * See also _jc_invoke_jcni_a().
+ *
+ * Returns JNI_TRUE if the stack was clipped, otherwise JNI_FALSE
+ * (i.e., the stack was already clipped so we didn't clip it again).
+ *
+ * NOTE: If this function returns JNI_TRUE, the function calling this
+ * function must call _jc_stack_unclip() before returning or else throw
+ * (not just post) an exception.
+ */
+jboolean
+_jc_stack_clip(_jc_env *env)
+{
+	_jc_java_stack *const stack = env->java_stack;
+	_jc_exec_stack *estack;
+
+	/* Sanity check */
+	_JC_ASSERT(env == _jc_get_current_env());
+
+	/* If no Java call stack yet, nothing to do */
+	if (stack == NULL)
+		return JNI_FALSE;
+
+	/* If interpreting, just set the flag */
+	if (stack->interp) {
+		_jc_interp_stack *const istack = (_jc_interp_stack *)stack;
+
+		if (!istack->clipped) {
+			istack->clipped = JNI_TRUE;
+			return JNI_TRUE;
+		}
+		return JNI_FALSE;
+	}
+
+	/* We are running executable Java */
+	estack = (_jc_exec_stack *)stack;
+
+	/* Is the Java stack already clipped? */
+	if (estack->pc != NULL) {
+		_JC_ASSERT(_jc_stack_frame_valid(estack->frame));
+		return JNI_FALSE;
+	}
+
+	/* Grab the current context and clip the Java stack with it */
+#if HAVE_GETCONTEXT
+    {
+	ucontext_t ctx;
+
+	getcontext(&ctx);
+	_jc_stack_clip_ctx(env, &ctx.uc_mcontext);
+    }
+#else
+    {
+	mcontext_t ctx;
+
+	/* Sanity check */
+	_JC_ASSERT(env->ctx == NULL);
+
+	/* Get current machine context */
+#if defined(__FreeBSD__) && defined(__i386__)
+	/* Work around for FreeBSD bug threads/75374 */
+	{
+	    jmp_buf buf;
+
+	    setjmp(buf);
+	    memset(&ctx, 0, sizeof(ctx));
+	    ctx.mc_eip = buf[0]._jb[0];
+	    ctx.mc_ebx = buf[0]._jb[1];
+	    ctx.mc_esp = buf[0]._jb[2];
+	    ctx.mc_ebp = buf[0]._jb[3];
+	    ctx.mc_esi = buf[0]._jb[4];
+	    ctx.mc_edi = buf[0]._jb[5];
+	}
+#else
+	env->ctx = &ctx;
+	pthread_kill(pthread_self(), SIGSEGV);
+	env->ctx = NULL;
+#endif
+
+	/* Use it to clip Java stack */
+	_jc_stack_clip_ctx(env, &ctx);
+    }
+#endif
+
+	/* Roll back two frames to calling function's calling function */
+	_jc_stack_frame_next(&estack->frame, &estack->pc);
+	_jc_stack_frame_next(&estack->frame, &estack->pc);
+
+	/* Done */
+	return JNI_TRUE;
+}
+
+/*
+ * Clip the top of the Java stack using the supplied machine context,
+ * which must be created either from getcontext() or by a signal.
+ * The stack must not already be clipped.
+ *
+ * This sets the top of the Java stack to be the function in which
+ * the context was created.
+ *
+ * Returns JNI_TRUE if the stack was clipped, otherwise JNI_FALSE
+ */
+jboolean
+_jc_stack_clip_ctx(_jc_env *env, const mcontext_t *ctx)
+{
+	_jc_java_stack *const stack = env->java_stack;
+	_jc_exec_stack *estack;
+
+	/* Sanity check */
+	_JC_ASSERT(env == _jc_get_current_env());
+
+	/* If there's no Java call stack yet, nothing to do */
+	if (stack == NULL)
+		return JNI_FALSE;
+
+	/* Sanity check */
+	_JC_ASSERT(!stack->interp);
+	estack = (_jc_exec_stack *)stack;
+
+	/* Sanity check */
+	_JC_ASSERT(estack->pc == NULL);
+	_JC_ASSERT(!_jc_stack_frame_valid(estack->frame));
+
+	/* Save machine registers */
+	estack->regs = *ctx;
+
+	/* Extract frame info from saved context */
+	estack->frame = _jc_mcontext_frame(&estack->regs);
+	estack->pc = _jc_mcontext_pc(&estack->regs);
+
+	/* Done */
+	return JNI_TRUE;
+}
+
+/*
+ * Un-do the effects of _jc_stack_clip().
+ */
+void
+_jc_stack_unclip(_jc_env *env)
+{
+	_jc_java_stack *const stack = env->java_stack;
+	_jc_exec_stack *estack;
+
+	/* Sanity check */
+	_JC_ASSERT(stack != NULL);
+	_JC_ASSERT(env == _jc_get_current_env());
+
+	/* If interpreting, just unset the flag */
+	if (stack->interp) {
+		_jc_interp_stack *const istack = (_jc_interp_stack *)stack;
+
+		/* Sanity check: the stack is clipped */
+		_JC_ASSERT(istack->clipped);
+
+		/* Mark it unclipped */
+		istack->clipped = JNI_FALSE;
+		return;
+	}
+
+	/* We are running executable Java */
+	estack = (_jc_exec_stack *)stack;
+
+	/* Sanity check: the stack is clipped */
+	_JC_ASSERT(estack->pc != NULL);
+
+	/* Invalidate Java stack top, allowing it to "float" again */
+	estack->pc = NULL;
+#ifndef NDEBUG
+	_jc_stack_frame_init(&estack->frame);
+#endif
+}
+
+/*
+ * Print out the exception stack trace associated with the
+ * currently posted exception, which must not be NULL.
+ */
+void
+_jc_print_stack_trace(_jc_env *env, FILE *fp)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_saved_frame *frames;
+	_jc_object *vmThrowable;
+	_jc_byte_array *bytes;
+	_jc_object *cause;
+	int num_frames;
+	_jc_object *e;
+
+	/* Get exception */
+	e = env->head.pending;
+
+again:
+	/* Sanity check */
+	_JC_ASSERT(_jc_subclass_of(e, vm->boot.types.Throwable));
+
+	/* Print exception headling */
+	_jc_fprint_exception_headline(env, fp, e);
+	_jc_fprintf(vm, fp, "\n");
+
+	/* Get associated VMThrowable */
+	if ((vmThrowable = *_JC_VMFIELD(vm, e,
+	    Throwable, vmState, _jc_object *)) == NULL)
+		goto no_trace;
+
+	/* Get saved frames from 'vmdata' byte[] array */
+	if ((bytes = *_JC_VMFIELD(vm, vmThrowable,
+	    VMThrowable, vmdata, _jc_byte_array *)) == NULL)
+		goto no_trace;
+
+	/* Print stack frames */
+	frames = (_jc_saved_frame *)_JC_ROUNDUP2(
+	    (_jc_word)bytes->elems, _JC_FULL_ALIGNMENT);
+	num_frames = (bytes->length -
+	    ((_jc_word)frames - (_jc_word)bytes->elems)) / sizeof(*frames);
+	_jc_print_stack_frames(env, fp, num_frames, frames);
+
+no_trace:
+	/* Print causing exception, if any */
+	if ((cause = *_JC_VMFIELD(vm, e,
+	      Throwable, cause, _jc_object *)) != NULL
+	    && cause != e) {
+		_jc_fprintf(vm, fp, "Caused by ");
+		e = cause;
+		goto again;
+	}
+}
+
+/*
+ * Print out stack frames from an array of _jc_saved_frame's.
+ */
+void
+_jc_print_stack_frames(_jc_env *env, FILE *fp,
+	int num_frames, _jc_saved_frame *frames)
+{
+	_jc_jvm *const vm = env->vm;
+	int i;
+
+	/* Print out stack trace */
+	for (i = 0; i < num_frames; i++) {
+		_jc_saved_frame *const frame = &frames[i];
+		_jc_method *method;
+		_jc_type *class;
+
+		/* Get method and class */
+		method = frame->method;
+		class = method->class;
+
+		/* Print out stack frame */
+		_jc_fprintf(vm, fp, "\tat ");
+		_jc_fprint_noslash(vm, fp, class->name);
+		_jc_fprintf(vm, fp, ".%s(", method->name);
+		if (_JC_ACC_TEST(method, NATIVE))
+			_jc_fprintf(vm, fp, "Native Method");
+		else if (class->u.nonarray.source_file == NULL)
+			_jc_fprintf(vm, fp, "Unknown");
+		else {
+			int jline;
+
+			/* Print source file */
+			_jc_fprintf(vm, fp, "%s",
+			    class->u.nonarray.source_file);
+
+			/* Print Java line number if known */
+			jline = _JC_ACC_TEST(method, INTERP) ?
+			    _jc_interp_pc_to_jline(method, frame->u.ipc) :
+			    _jc_exec_pc_to_jline(method, frame->u.pc);
+			if (jline != 0)
+				_jc_fprintf(vm, fp, ":%d", jline);
+		}
+		_jc_fprintf(vm, fp, ")\n");
+	}
+}
+
+/*
+ * Save the current Java stack trace as an array of _jc_saved_frame's.
+ *
+ * If 'thread' doesn't correspond to the current thread, then the
+ * target thread must be running in non-java mode or halted
+ * (so that it's top Java stack frame is already clipped).
+ *
+ * The array is stored in the area pointed to by 'frames' (if not NULL).
+ * At most 'max' frames are stored.
+ *
+ * Returns the total number of frames if successful, otherwise -1.
+ */
+int
+_jc_save_stack_frames(_jc_env *env, _jc_env *thread,
+	int max, _jc_saved_frame *frames)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_stack_crawl crawl;
+	int i;
+
+	/* Lock VM */
+	_JC_MUTEX_LOCK(env, vm->mutex);
+
+	/* Crawl the stack */
+	for (i = 0, _jc_stack_crawl_first(thread, &crawl);
+	    crawl.method != NULL; _jc_stack_crawl_next(vm, &crawl)) {
+		_jc_saved_frame *const frame = &frames[i];
+
+		/* Skip non-Java functions */
+		if (crawl.method->class == NULL)
+			continue;
+
+		/* Save this one */
+		if (i < max) {
+			frame->method = crawl.method;
+			if (_JC_ACC_TEST(crawl.method, INTERP)) {
+				_jc_interp_stack *const istack
+				    = (_jc_interp_stack *)crawl.stack;
+
+				frame->u.ipc = *istack->pcp;
+			} else
+				frame->u.pc = crawl.pc;
+		}
+		i++;
+	}
+
+	/* Unlock VM */
+	_JC_MUTEX_UNLOCK(env, vm->mutex);
+
+	/* Done */
+	return i;
+}
+
+/*
+ * Initialize a Java stack crawl.
+ *
+ * If 'thread' doesn't correspond to the current thread, then the
+ * target thread must be running in non-java mode or halted
+ * (so that it's top Java stack frame is already clipped).
+ *
+ * NOTE: caller must acquire the VM lock before calling this function.
+ */
+void
+_jc_stack_crawl_first(_jc_env *thread, _jc_stack_crawl *crawl)
+{
+	_jc_java_stack *const stack = thread->java_stack;
+	_jc_exec_stack *const estack = (_jc_exec_stack *)stack;
+	_jc_interp_stack *const istack = (_jc_interp_stack *)stack;
+
+	/* Initialize */
+	memset(crawl, 0, sizeof(*crawl));
+
+	/* If there's no Java call stack yet, return end of stack */
+	if (stack == NULL)
+		return;
+
+	/* Sanity check */
+	_JC_ASSERT(thread == _jc_get_current_env()
+	    || (((!stack->interp && estack->pc != NULL)
+	       || (stack->interp && istack->clipped))
+	      && (thread->status == _JC_THRDSTAT_HALTING_NONJAVA
+	       || thread->status == _JC_THRDSTAT_HALTED)));
+
+	/* Start with top of stack */
+	crawl->stack = stack;
+
+	/* Interpreted case is easy */
+	if (stack->interp) {
+		crawl->method = istack->method;
+		return;
+	}
+
+	/*
+	 * Executable case.
+	 *
+	 * If the stack top already clipped, use it; otherwise start here.
+	 * In the latter case, this must be the current thread.
+	 */
+	if ((crawl->pc = estack->pc) != NULL)
+		crawl->frame = estack->frame;
+	else {
+		_JC_ASSERT(!_jc_stack_frame_valid(estack->frame));
+		_jc_stack_frame_current(&crawl->frame);
+		_jc_stack_frame_next(&crawl->frame, &crawl->pc);
+	}
+
+	/* Skip over non-Java C stack frames */
+	_jc_stack_crawl_skip(thread->vm, crawl);
+}
+
+/*
+ * Get the next registered Java method (or _jc_invoke_jcni_a()) in a stack
+ * crawl. The 'thread' parameter does not have to be the current thread.
+ *
+ * If the bottom of the stack is reached, crawl->method is set to NULL.
+ *
+ * NOTE: caller must acquire the VM lock before calling this function.
+ */
+void
+_jc_stack_crawl_next(_jc_jvm *vm, _jc_stack_crawl *crawl)
+{
+	_jc_java_stack *stack = crawl->stack;
+
+	/* Sanity check */
+	_JC_ASSERT(crawl->method != NULL);
+
+	/*
+	 * For executable stack, just scan C stack frames until we
+	 * reach _jc_invoke_jcni_a() (we always will eventually).
+	 */
+	if (!stack->interp && crawl->method != &vm->invoke_method) {
+		_jc_stack_frame_next(&crawl->frame, &crawl->pc);
+		_jc_stack_crawl_skip(vm, crawl);
+		return;
+	}
+
+	/* Advance to the next deeper stack chunk */
+	crawl->stack = crawl->stack->next;
+
+	/* Did we reach the end of the Java stack? */
+	if (crawl->stack == NULL) {
+		memset(crawl, 0, sizeof(*crawl));
+		return;
+	}
+
+	/* Initialize new stack chunk */
+	if (crawl->stack->interp) {
+		_jc_interp_stack *const istack
+		    = (_jc_interp_stack *)crawl->stack;
+
+		crawl->method = istack->method;
+	} else {
+		_jc_exec_stack *const estack = (_jc_exec_stack *)crawl->stack;
+
+		crawl->pc = estack->pc;
+		crawl->frame = estack->frame;
+		_JC_ASSERT(_jc_stack_frame_valid(crawl->frame));
+		_jc_stack_crawl_skip(vm, crawl);
+	}
+}
+
+/*
+ * March backwards through consecutive stack frames
+ * until we hit one that matches a registered method.
+ */
+void
+_jc_stack_crawl_skip(_jc_jvm *vm, _jc_stack_crawl *crawl)
+{
+	/* Look for a Java function (or _jc_invoke_jcni_a()) */
+	while (JNI_TRUE) {
+		_jc_method key;
+
+		/* See if stack frame function is a registered method */
+		key.function = key.u.exec.function_end = (void *)crawl->pc;
+		if ((crawl->method = _jc_splay_find(
+		    &vm->method_tree, &key)) != NULL) {
+			_JC_ASSERT(!_JC_ACC_TEST(crawl->method, INTERP));
+			break;
+		}
+
+		/* Advance to next C stack frame */
+		_jc_stack_frame_next(&crawl->frame, &crawl->pc);
+	}
+}
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/string.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/string.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/string.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/string.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,161 @@
+
+/*
+ * 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: string.c,v 1.6 2005/03/16 15:31:12 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/*
+ * Create a new string object from valid UTF-8 data.
+ */
+_jc_object *
+_jc_new_string(_jc_env *env, const void *utf, size_t len)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_char_array *chars;
+	jobject aref = NULL;
+	jobject sref = NULL;
+	jint clen;
+
+	/* Compute length in characters */
+	if ((clen = _jc_utf_decode(utf, len, NULL)) == -1) {
+		_jc_post_exception_msg(env, _JC_InternalError,
+		    "%s: invalid UTF-8 data!", __FUNCTION__);
+		goto done;
+	}
+
+	/* Create character array and wrap temporarily */
+	if ((aref = _jc_new_local_native_ref(env,
+	    (_jc_object *)_jc_new_array(env,
+	      vm->boot.types.prim_array[_JC_TYPE_CHAR], clen))) == NULL)
+		goto done;
+	chars = (_jc_char_array *)*aref;
+
+	/* Decode characters into char[] array */
+	_jc_utf_decode(utf, len, chars->elems);
+
+	/* Instantiate new string */
+	if ((sref = _jc_new_local_native_ref(env,
+	    _jc_new_object(env, vm->boot.types.String))) == NULL)
+		goto done;
+	if (_jc_invoke_nonvirtual(env,
+	    vm->boot.methods.String.init, *sref, chars) != JNI_OK) {
+		_jc_free_local_native_ref(&sref);
+		goto done;
+	}
+
+done:
+	/* Clean up and return */
+	_jc_free_local_native_ref(&aref);
+	return _jc_free_local_native_ref(&sref);
+}
+
+/*
+ * Create a new intern'd string object from valid UTF-8 data.
+ */
+_jc_object *
+_jc_new_intern_string(_jc_env *env, const void *utf, size_t len)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_object *string = NULL;
+	jobject sref;
+
+	/* Create a normal string */
+	if ((sref = _jc_new_local_native_ref(env,
+	    _jc_new_string(env, utf, len))) == NULL)
+		goto done;
+
+	/* Intern it */
+	if (_jc_invoke_virtual(env,
+	    vm->boot.methods.String.intern, *sref) != JNI_OK)
+		goto done;
+	string = env->retval.l;
+	_JC_ASSERT(string != NULL);
+
+done:
+	/* Clean up and return */
+	_jc_free_local_native_ref(&sref);
+	return string;
+}
+
+/*
+ * Convert a String object to a character array.
+ * The length of the array is returned.
+ *
+ * If 'chars' is not NULL, the array is copied there.
+ */
+jint
+_jc_decode_string_chars(_jc_env *env, _jc_object *string, jchar *chars)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_char_array *array;
+	jint offset;
+	jint count;
+
+	/* Sanity check */
+	_JC_ASSERT(string->type == vm->boot.types.String);
+
+	/* Get character array region */
+	array = *_JC_VMFIELD(vm, string, String, value, _jc_char_array *);
+	offset = *_JC_VMFIELD(vm, string, String, offset, jint);
+	count = *_JC_VMFIELD(vm, string, String, count, jint);
+	_JC_ASSERT(array != NULL);
+	_JC_ASSERT(_JC_BOUNDS_CHECK(array, offset, count));
+
+	/* Copy characters */
+	if (chars != NULL)
+		memcpy(chars, array->elems + offset, count * sizeof(*chars));
+
+	/* Done */
+	return count;
+}
+
+/*
+ * Convert a String object to a NUL-terminated UTF-8 string.
+ * The length of the UTF-8 string (NOT including NUL) is returned.
+ * If 'buf' is not NULL, the UTF-8 string PLUS NUL is wrttten there.
+ */
+size_t
+_jc_decode_string_utf8(_jc_env *env, _jc_object *string, char *buf)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_char_array *array;
+	jint offset;
+	jint count;
+	size_t len;
+
+	/* Sanity check */
+	_JC_ASSERT(string->type == vm->boot.types.String);
+
+	/* Get character array region */
+	array = *_JC_VMFIELD(vm, string, String, value, _jc_char_array *);
+	offset = *_JC_VMFIELD(vm, string, String, offset, jint);
+	count = *_JC_VMFIELD(vm, string, String, count, jint);
+	_JC_ASSERT(array != NULL);
+	_JC_ASSERT(_JC_BOUNDS_CHECK(array, offset, count));
+
+	/* UTF-8 decode and nul-terminate the string */
+	len = _jc_utf_encode(array->elems + offset, count, (u_char *)buf);
+	if (buf != NULL)
+		buf[len] = '\0';
+
+	/* Done */
+	return len;
+}
+
+