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