Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/properties.c URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/properties.c?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/properties.c (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/properties.c Tue Oct 4 19:19:16 2005 @@ -0,0 +1,527 @@ + +/* + * 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: properties.c,v 1.17 2005/05/15 01:48:31 archiecobbs Exp $ + */ + +#include "libjc.h" + +/* List of fixed default properties */ +static const _jc_property _jc_fixed_properties[] = { +{ "java.version", "1.4" }, +{ "java.vendor", "JC virtual machine project" }, +{ "java.vendor.url", "http://jcvm.sourceforge.net/" }, +{ "java.home", _AC_DATADIR "/jc" }, +{ "java.vm.name", "JC virtual machine" }, +{ "java.vm.vendor", "JC virtual machine project" }, +{ "java.vm.version", PACKAGE_VERSION }, +{ "java.vm.specification.name", "Java Virtual Machine Specification" }, +{ "java.vm.specification.vendor", "Sun Microsystems Inc." }, +{ "java.vm.specification.version", "1.0" }, +{ "java.specification.name", "Java Platform API Specification" }, +{ "java.specification.vendor", "Sun Microsystems Inc." }, +{ "java.specification.version", "1.4" }, +{ "java.class.version", "46.0" }, +{ "java.io.tmpdir", _JC_TEMP_DIR }, +{ "file.separator", _JC_FILE_SEPARATOR }, +{ "line.separator", _JC_LINE_SEPARATOR }, +{ "path.separator", _JC_PATH_SEPARATOR }, +{ "java.library.path", _JC_LIBRARY_PATH }, +{ "java.boot.class.path", _JC_BOOT_CLASS_PATH }, +#if _JC_BIG_ENDIAN +{ "gnu.cpu.endian", "big" }, +#else +{ "gnu.cpu.endian", "little" }, +#endif +#if _JC_THREAD_LOCAL_SUPPORT +{ "jc.thread.local", "true" }, +#else +{ "jc.thread.local", "false" }, +#endif +{ "jc.stack.minimum", _JC_STACK_MINIMUM }, +{ "jc.stack.maximum", _JC_STACK_MAXIMUM }, +{ "jc.stack.default", _JC_STACK_DEFAULT }, +{ "jc.heap.size", _JC_DEFAULT_HEAP_SIZE }, +{ "jc.loader.size", _JC_DEFAULT_LOADER_SIZE }, +{ "jc.heap.granularity", _JC_DEFAULT_HEAP_GRANULARITY }, +{ "jc.gnu.compiler", _JC_GNU_COMPILER }, +{ "jc.include.dir", _JC_INCLUDE_DIR }, +{ "jc.object.generator", "org.dellroad.jc.cgen.JCObjectGenerator" }, +{ "jc.method.optimizer", "org.dellroad.jc.cgen.DefaultMethodOptimizer" }, +{ "jc.gen.inline.max.expansion", "2.5" }, +{ "jc.gen.inline.max.caller", "5000" }, +{ "jc.gen.inline.max.callee", "12" }, +{ "jc.gen.inline.min.caller", "20" }, +{ "jc.gen.inline.min.callee", "3" }, +{ "jc.gen.inline.verbose", "false" }, +{ "jc.include.line.numbers", "true" }, +{ "jc.object.generation.enabled", "true" }, +{ "jc.object.loader.enabled", "true" }, +{ "jc.without.classfiles", "false" }, +{ "jc.ignore.resolution.failures", "false" }, +{ "jc.resolve.native.directly", "false" }, +{ NULL, NULL } +}; + +/* Internal functions */ +static jint _jc_digest_size(_jc_env *env, size_t *ptr, + _jc_property *prop, size_t max); +static _jc_property *_jc_property_get(_jc_jvm *vm, const char *name); +static int _jc_property_cmp(const void *item1, const void *item2); + +/* + * Set the default system properties. + * + * If unsuccessful an exception is stored. + */ +jint +_jc_set_system_properties(_jc_env *env) +{ + const char *home_dir; + struct utsname uts; + struct passwd *pw; + time_t now; + char *buf; + char *s; + int i; + + /* Set fixed properties */ + for (i = 0; _jc_fixed_properties[i].name != NULL; i++) { + const _jc_property *const prop = &_jc_fixed_properties[i]; + + if (_jc_set_property(env, prop->name, prop->value) != JNI_OK) + return JNI_ERR; + } + + /* Set current working directory */ + for (s = NULL, i = 64; JNI_TRUE; i *= 2) { + if (i < 0) + return JNI_ERR; + _jc_vm_free(&s); + if ((s = _jc_vm_zalloc(env, i)) == NULL) + return JNI_ERR; + if (getcwd(s, i) != NULL) + break; + if (errno != ERANGE) { + _JC_EX_STORE(env, InternalError, "%s: %s", + "getcwd", strerror(errno)); + return JNI_ERR; + } + } + if (_jc_set_property(env, "user.dir", s) != JNI_OK) { + _jc_vm_free(&s); + return JNI_ERR; + } + _jc_vm_free(&s); + + /* Set user name and home directory */ + if ((pw = getpwuid(getuid())) == NULL) { + _JC_EX_STORE(env, InternalError, "%s: %s", + "getpwuid", strerror(errno)); + return JNI_ERR; + } + if (_jc_set_property(env, "user.name", pw->pw_name) != JNI_OK) + return JNI_ERR; + if ((home_dir = getenv("HOME")) == NULL) + home_dir = pw->pw_dir; + if (_jc_set_property(env, "user.home", home_dir) != JNI_OK) + return JNI_ERR; + + /* Set user timezone */ + now = time(NULL); + if (_jc_set_property(env, + "user.timezone", localtime(&now)->tm_zone) != JNI_OK) + return JNI_ERR; + + /* Set source and object directory paths */ + if ((buf = _JC_FORMAT_STRING(env, "%s%s.jc_src%s%s", + home_dir, _JC_FILE_SEPARATOR, _JC_PATH_SEPARATOR, + _JC_BOOT_SOURCE_DIR)) == NULL) + return JNI_ERR; + if (_jc_set_property(env, "jc.source.path", buf) != JNI_OK) + return JNI_ERR; + if ((buf = _JC_FORMAT_STRING(env, "%s%s.jc_obj%s%s", + home_dir, _JC_FILE_SEPARATOR, _JC_PATH_SEPARATOR, + _JC_BOOT_OBJECT_DIR)) == NULL) + return JNI_ERR; + if (_jc_set_property(env, "jc.object.path", buf) != JNI_OK) + return JNI_ERR; + + /* Set operating system info */ + if (uname(&uts) == -1) { + _JC_EX_STORE(env, InternalError, "%s: %s", + "uname", strerror(errno)); + return JNI_ERR; + } + if (strcmp(uts.machine, "i486") == 0 + || strcmp(uts.machine, "i586") == 0 + || strcmp(uts.machine, "i686") == 0) + strcpy(uts.machine, "i386"); + if (_jc_set_property(env, "os.arch", uts.machine) != JNI_OK) + return JNI_ERR; + if (_jc_set_property(env, "os.name", uts.sysname) != JNI_OK) + return JNI_ERR; + if (_jc_set_property(env, "os.version", uts.release) != JNI_OK) + return JNI_ERR; + + /* Done */ + endpwent(); + return JNI_OK; +} + +/* + * Set a system property. + * + * If unsuccessful an exception is stored. + */ +jint +_jc_set_property(_jc_env *env, const char *name, const char *value) +{ + return _jc_set_property2(env, name, strlen(name), value); +} + +/* + * Set a system property, where the name does not have to be + * nul-terminated. + * + * If unsuccessful an exception is stored. + */ +jint +_jc_set_property2(_jc_env *env, const char *name, + size_t name_len, const char *value) +{ + _jc_jvm *const vm = env->vm; + _jc_properties *const props = &vm->system_properties; + _jc_property *prop; + char *new_value; + char *new_name; + int i; + + /* Replace property if it already exists */ + for (i = 0; i < props->length; i++) { + prop = &props->elems[i]; + if (strncmp(prop->name, name, name_len) == 0 + && prop->name[name_len] == '\0') { + if ((new_value = _jc_vm_strdup(env, value)) == NULL) + return JNI_ERR; + _jc_vm_free(&prop->value); + prop->value = new_value; + return JNI_OK; + } + } + + /* Extend properties list if necessary */ + if (props->length == props->allocated) { + u_int new_alloc = props->allocated + 128; + _jc_property *new_elems; + + if ((new_elems = _jc_vm_realloc(env, props->elems, + new_alloc * sizeof(*new_elems))) == NULL) + return JNI_ERR; + props->elems = new_elems; + props->allocated = new_alloc; + } + + /* Add property to list */ + prop = &props->elems[props->length]; + memset(prop, 0, sizeof(*prop)); + if ((new_name = _jc_vm_strndup(env, name, name_len)) == NULL) + return JNI_ERR; + if ((new_value = _jc_vm_strdup(env, value)) == NULL) { + _jc_vm_free(&new_name); + return JNI_ERR; + } + prop->name = new_name; + prop->value = new_value; + + /* Done */ + props->length++; + return JNI_OK; +} + +/* + * Process system properties that have special meanings. + * + * If unsuccessful an exception is stored. + */ +jint +_jc_digest_properties(_jc_env *env) +{ + _jc_jvm *const vm = env->vm; + _jc_property *prop; + size_t loader_size; + int len; + int i; + + /* Sort properties for faster searching */ + qsort(vm->system_properties.elems, vm->system_properties.length, + sizeof(*vm->system_properties.elems), _jc_property_cmp); + + /* Get whether object generation is enabled */ + prop = _jc_property_get(vm, "jc.object.generation.enabled"); + _JC_ASSERT(prop != NULL); + vm->generation_enabled = strcmp(prop->value, "true") == 0; + + /* Get line number support */ + prop = _jc_property_get(vm, "jc.include.line.numbers"); + _JC_ASSERT(prop != NULL); + vm->line_numbers = strcmp(prop->value, "true") == 0; + + /* Sanity check */ + _JC_ASSERT(vm->boot.class_path == NULL); + _JC_ASSERT(vm->boot.class_path_len == 0); + + /* Get boot loader class search path prepends */ + prop = _jc_property_get(vm, "java.boot.class.path.prepend"); + if (prop != NULL && _jc_parse_classpath(env, prop->value, + &vm->boot.class_path, &vm->boot.class_path_len) == JNI_ERR) + return JNI_ERR; + + /* Get boot loader class search path */ + prop = _jc_property_get(vm, "java.boot.class.path"); + _JC_ASSERT(prop != NULL); + if (_jc_parse_classpath(env, prop->value, + &vm->boot.class_path, &vm->boot.class_path_len) == JNI_ERR) + return JNI_ERR; + + /* Get boot loader class search path appends */ + prop = _jc_property_get(vm, "java.boot.class.path.append"); + if (prop != NULL && _jc_parse_classpath(env, prop->value, + &vm->boot.class_path, &vm->boot.class_path_len) == JNI_ERR) + return JNI_ERR; + + /* Get object file search path */ + prop = _jc_property_get(vm, "jc.object.path"); + _JC_ASSERT(prop != NULL); + + /* Sanity check */ + _JC_ASSERT(vm->object_path == NULL); + _JC_ASSERT(vm->object_path_len == 0); + + /* Parse object path */ + if ((len = _jc_parse_objpath(env, prop->value, &vm->object_path)) == -1) + return JNI_ERR; + vm->object_path_len = len; + + /* Get source and object directory paths */ + prop = _jc_property_get(vm, "jc.source.path"); + _JC_ASSERT(prop != NULL); + if ((vm->source_path = _jc_parse_searchpath(env, prop->value)) == NULL) + return JNI_ERR; + + /* Get thread stack minimum, maximum, and default */ + prop = _jc_property_get(vm, "jc.stack.minimum"); + _JC_ASSERT(prop != NULL); + if (_jc_digest_size(env, &vm->threads.stack_minimum, prop, 0) != JNI_OK) + return JNI_ERR; + prop = _jc_property_get(vm, "jc.stack.maximum"); + _JC_ASSERT(prop != NULL); + if (_jc_digest_size(env, &vm->threads.stack_maximum, prop, 0) != JNI_OK) + return JNI_ERR; + prop = _jc_property_get(vm, "jc.stack.default"); + _JC_ASSERT(prop != NULL); + if (_jc_digest_size(env, &vm->threads.stack_default, prop, 0) != JNI_OK) + return JNI_ERR; + + /* Get heap size and granularity factor */ + prop = _jc_property_get(vm, "jc.heap.size"); + _JC_ASSERT(prop != NULL); + if (_jc_digest_size(env, &vm->heap.size, prop, 0) != JNI_OK) + return JNI_ERR; + prop = _jc_property_get(vm, "jc.heap.granularity"); + _JC_ASSERT(prop != NULL); + if (_jc_digest_size(env, &vm->heap.granularity, prop, 99) != JNI_OK) + return JNI_ERR; + prop = _jc_property_get(vm, "jc.loader.size"); + _JC_ASSERT(prop != NULL); + if (_jc_digest_size(env, &loader_size, prop, 0) != JNI_OK) + return JNI_ERR; + vm->max_loader_pages = _JC_HOWMANY(loader_size, _JC_PAGE_SIZE); + vm->avail_loader_pages = vm->max_loader_pages; + + /* Check for native resolution optimization */ + prop = _jc_property_get(vm, "jc.resolve.native.directly"); + _JC_ASSERT(prop != NULL); + vm->resolve_native_directly = strcmp(prop->value, "true") == 0; + + /* Check whether we really need classfiles */ + prop = _jc_property_get(vm, "jc.without.classfiles"); + _JC_ASSERT(prop != NULL); + vm->without_classfiles = strcmp(prop->value, "true") == 0; + + /* Check whether to enable the ELF loader */ + prop = _jc_property_get(vm, "jc.object.loader.enabled"); + _JC_ASSERT(prop != NULL); + vm->loader_enabled = strcmp(prop->value, "true") == 0; + + /* Check whether we should ignore class resolution failures */ + prop = _jc_property_get(vm, "jc.ignore.resolution.failures"); + _JC_ASSERT(prop != NULL); + vm->ignore_resolution_failures = strcmp(prop->value, "true") == 0; + + /* Without classfiles or loader implies no object generation */ + if (vm->without_classfiles || !vm->loader_enabled) + vm->generation_enabled = JNI_FALSE; + + /* Check stack parameters */ + if (vm->threads.stack_minimum > vm->threads.stack_default + || (vm->threads.stack_maximum > 0 + && (vm->threads.stack_default > vm->threads.stack_maximum + || vm->threads.stack_maximum < PTHREAD_STACK_MIN))) { + _JC_EX_STORE(env, InternalError, + "invalid/incompatible values for properties" + " `%s' (%d), `%s' (%d), and/or `%s' (%d)", + "jc.stack.minimum", vm->threads.stack_minimum, + "jc.stack.maximum", vm->threads.stack_maximum, + "jc.stack.default", vm->threads.stack_default); + return JNI_ERR; + } + + /* Set verbose properties from verbose flags */ + for (i = 0; i < _JC_VERBOSE_MAX; i++) { + const char *const flag = _jc_verbose_names[i]; + char name[128]; + + snprintf(name, sizeof(name), "jc.verbose.%s", flag); + if (_jc_set_property(env, name, ((vm->verbose_flags + & (1 << i)) != 0) ? "true" : "false") != JNI_OK) + return JNI_ERR; + } + + /* Done */ + return JNI_OK; +} + +/* + * Free all system properties. + */ +void +_jc_destroy_properties(_jc_jvm *vm) +{ + int i; + + for (i = 0; i < vm->system_properties.length; i++) { + _jc_property *const prop = &vm->system_properties.elems[i]; + + _jc_vm_free(&prop->name); + _jc_vm_free(&prop->value); + } + _jc_vm_free(&vm->system_properties.elems); + vm->system_properties.allocated = 0; + vm->system_properties.length = 0; +} + +/* + * Digest an unsigned numerical property. Recognize suffixes for + * kilobytes, megabytes, etc. and convert the property if found. + */ +static jint +_jc_digest_size(_jc_env *env, size_t *ptr, _jc_property *prop, size_t max) +{ + const char *value = prop->value; + size_t vlen = strlen(value); + unsigned long long size; + unsigned int units = 1; + char buf[64]; + char *eptr; + + /* Get units, if any */ + if (vlen > 0) { + switch (value[vlen - 1]) { + case 'K': + case 'k': + units = 1024; + break; + case 'M': + case 'm': + units = 1024 * 1024; + break; + case 'G': + case 'g': + units = 1024 * 1024 * 1024; + break; + default: + break; + } + if (units != 1) { + if (vlen > sizeof(buf)) + vlen = sizeof(buf); + strncpy(buf, value, vlen - 1); + buf[vlen - 1] = '\0'; + value = buf; + } + } + + /* Parse units */ + if (((size = strtoull(value, &eptr, 0)) == ULLONG_MAX + && errno == ERANGE) + || (eptr == value || *eptr != '\0')) { + _JC_EX_STORE(env, InternalError, + "invalid value `%s' for property `%s'", + prop->value, prop->name); + return JNI_ERR; + } + + /* Multiply by units and update property */ + if (units > 1) { + size *= units; + snprintf(buf, sizeof(buf), "%llu", size); + if ((value = _jc_vm_strdup(env, buf)) == NULL) + return JNI_ERR; + _jc_vm_free(&prop->value); + prop->value = value; + } + + /* Check upper bound */ + if (size > SIZE_T_MAX || (max != 0 && size > max)) { + _JC_EX_STORE(env, InternalError, + "value `%s' for property `%s' is too large", + prop->value, prop->name); + return JNI_ERR; + } + + /* Done */ + *ptr = (size_t)size; + return JNI_OK; +} + +/* + * Find a property in the sorted list. + */ +static _jc_property * +_jc_property_get(_jc_jvm *vm, const char *name) +{ + _jc_property key; + + key.name = name; + return bsearch(&key, vm->system_properties.elems, + vm->system_properties.length, sizeof(*vm->system_properties.elems), + _jc_property_cmp); +} + +/* + * Compare two properties for sorting by name. + */ +static int +_jc_property_cmp(const void *item1, const void *item2) +{ + const _jc_property *const prop1 = item1; + const _jc_property *const prop2 = item2; + + return strcmp(prop1->name, prop2->name); +} + Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/reflect.c URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/reflect.c?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/reflect.c (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/reflect.c Tue Oct 4 19:19:16 2005 @@ -0,0 +1,837 @@ + +/* + * 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: reflect.c,v 1.25 2005/07/10 21:03:54 archiecobbs Exp $ + */ + +#include "libjc.h" + +/* Internal functions */ +static _jc_object *_jc_get_reflected(_jc_env *env, _jc_method *constr, + _jc_type *type, const char *name, jint slot); + +/* + * Resolve a field declared in a specific class. + * + * If unsuccessful a NoSuchFieldError exception is stored. + */ +_jc_field * +_jc_get_declared_field(_jc_env *env, _jc_type *type, const char *name, + const char *sig, int is_static) +{ + _jc_field key_data; + _jc_field *const key = &key_data; + _jc_field **fieldp; + _jc_field *field; + + /* Only normal reference types have declared fields */ + if ((type->flags & (_JC_TYPE_ARRAY|_JC_TYPE_MASK)) + != _JC_TYPE_REFERENCE) + goto fail; + + /* Binary search for field */ + key->name = name; + key->signature = sig; + key->access_flags = is_static ? _JC_ACC_STATIC : 0; + if ((fieldp = bsearch(&key, type->u.nonarray.fields, + type->u.nonarray.num_fields, sizeof(*type->u.nonarray.fields), + _jc_field_compare)) == NULL) + goto fail; + field = *fieldp; + + /* Done */ + return field; + +fail: + /* Not found */ + _JC_EX_STORE(env, NoSuchFieldError, "%s.%s (type `%s', %sstatic)", + type->name, name, sig, is_static ? "" : "non-"); + return NULL; +} + +/* + * Resolve a method declared in a specific class. + * + * The method's access flags in 'flags_mask' must match 'flags'. + * + * If unsuccessful a NoSuchMethodError exception is stored. + */ +_jc_method * +_jc_get_declared_method(_jc_env *env, _jc_type *type, const char *name, + const char *sig, int flags_mask, int flags) +{ + _jc_method key_data; + _jc_method *const key = &key_data; + _jc_method **methodp; + _jc_method *method; + + /* Only normal reference types have declared methods */ + if ((type->flags & (_JC_TYPE_ARRAY|_JC_TYPE_MASK)) + != _JC_TYPE_REFERENCE) + goto fail; + + /* Binary search for method */ + key->name = name; + key->signature = sig; + if ((methodp = bsearch(&key, type->u.nonarray.methods, + type->u.nonarray.num_methods, sizeof(*type->u.nonarray.methods), + _jc_method_compare)) == NULL) + goto fail; + method = *methodp; + + /* Check access flags */ + if ((method->access_flags & flags_mask) != flags) + goto fail; + + /* Done */ + return method; + +fail: + /* Not found */ + _JC_EX_STORE(env, NoSuchMethodError, "%s.%s%s", type->name, name, sig); + return NULL; +} + +/* + * Get the java.lang.reflect.Field object corresponding to 'field'. + */ +_jc_object * +_jc_get_reflected_field(_jc_env *env, _jc_field *field) +{ + _jc_jvm *const vm = env->vm; + _jc_type *const dclass = field->class; + int slot; + + /* Determine slot */ + for (slot = 0; slot < dclass->u.nonarray.num_fields; slot++) { + if (dclass->u.nonarray.fields[slot] == field) + break; + } + _JC_ASSERT(slot < dclass->u.nonarray.num_fields); + + /* Return reflection object */ + return _jc_get_reflected(env, vm->boot.methods.Field.init, + dclass, field->name, slot); +} + +/* + * Get the java.lang.reflect.Method object corresponding to 'method'. + */ +_jc_object * +_jc_get_reflected_method(_jc_env *env, _jc_method *method) +{ + _jc_jvm *const vm = env->vm; + _jc_type *const dclass = method->class; + int slot; + + /* Sanity check */ + _JC_ASSERT(*method->name != '<'); + + /* Determine slot */ + for (slot = 0; slot < dclass->u.nonarray.num_methods; slot++) { + if (dclass->u.nonarray.methods[slot] == method) + break; + } + _JC_ASSERT(slot < dclass->u.nonarray.num_methods); + + /* Return reflection object */ + return _jc_get_reflected(env, vm->boot.methods.Method.init, + dclass, method->name, slot); +} + +/* + * Get the java.lang.reflect.Constructor object corresponding to 'method'. + */ +_jc_object * +_jc_get_reflected_constructor(_jc_env *env, _jc_method *method) +{ + _jc_jvm *const vm = env->vm; + _jc_type *const dclass = method->class; + int slot; + + /* Sanity check */ + _JC_ASSERT(!_JC_ACC_TEST(method, STATIC) && *method->name == '<'); + + /* Determine slot */ + for (slot = 0; slot < dclass->u.nonarray.num_methods; slot++) { + if (dclass->u.nonarray.methods[slot] == method) + break; + } + _JC_ASSERT(slot < dclass->u.nonarray.num_methods); + + /* Return reflection object */ + return _jc_get_reflected(env, vm->boot.methods.Constructor.init, + dclass, NULL, slot); +} + +/* + * Get a java.lang.reflect object. We create a new one each time, + * because these objects are mutable (they inherit AccessibleObject). + */ +static _jc_object * +_jc_get_reflected(_jc_env *env, _jc_method *constr, + _jc_type *type, const char *name, jint slot) +{ + _jc_object *rtn = NULL; + jobject rref = NULL; + _jc_word params[3]; + int nparam = 0; + + /* Create the new reflection instance */ + if ((rref = _jc_new_local_native_ref(env, + _jc_new_object(env, constr->class))) == NULL) + goto done; + + /* Prepare constructor parameters */ + params[nparam++] = (_jc_word)type->instance; + if (name != NULL) { + _jc_object *string; + + if ((string = _jc_new_string(env, name, strlen(name))) == NULL) + goto done; + params[nparam++] = (_jc_word)string; + } + params[nparam++] = (_jc_word)slot; + + /* Initialize it */ + if (_jc_invoke_nonvirtual_a(env, constr, *rref, params) != JNI_OK) + goto done; + + /* Done */ + rtn = *rref; + +done: + /* Free local native refs */ + _jc_free_local_native_ref(&rref); + + /* Done */ + return rtn; +} + +/* + * Find the _jc_method structure corresponding to a Method object. + */ +_jc_method * +_jc_get_method(_jc_env *env, _jc_object *obj) +{ + _jc_jvm *const vm = env->vm; + _jc_method *method; + _jc_object *cl; + _jc_type *type; + jint slot; + + /* Sanity check */ + _JC_ASSERT(obj != NULL && obj->type == vm->boot.types.Method); + + /* Locate declaring class */ + cl = *_JC_VMFIELD(vm, obj, Method, declaringClass, _jc_object *); + type = _jc_get_vm_pointer(cl, vm->boot.fields.Class.vmdata); + _JC_ASSERT(!_JC_FLG_TEST(type, ARRAY)); + + /* Locate method */ + slot = *_JC_VMFIELD(vm, obj, Method, slot, jint); + _JC_ASSERT(slot >= 0 && slot < type->u.nonarray.num_methods); + method = type->u.nonarray.methods[slot]; + + /* Sanity check */ + _JC_ASSERT(*method->name != '<'); + + /* Done */ + return method; +} + +/* + * Find the _jc_method structure corresponding to a Constructor object. + */ +_jc_method * +_jc_get_constructor(_jc_env *env, _jc_object *obj) +{ + _jc_jvm *const vm = env->vm; + _jc_method *method; + _jc_object *cl; + _jc_type *type; + jint slot; + + /* Sanity check */ + _JC_ASSERT(obj != NULL && obj->type == vm->boot.types.Constructor); + + /* Locate declaring class */ + cl = *_JC_VMFIELD(vm, obj, Constructor, clazz, _jc_object *); + type = _jc_get_vm_pointer(cl, vm->boot.fields.Class.vmdata); + _JC_ASSERT(!_JC_FLG_TEST(type, ARRAY)); + + /* Locate constructor method */ + slot = *_JC_VMFIELD(vm, obj, Constructor, slot, jint); + _JC_ASSERT(slot >= 0 && slot < type->u.nonarray.num_methods); + method = type->u.nonarray.methods[slot]; + + /* Sanity check */ + _JC_ASSERT(!_JC_ACC_TEST(method, STATIC) && *method->name == '<'); + + /* Done */ + return method; +} + +/* + * Find the _jc_field structure corresponding to a Field object. + */ +_jc_field * +_jc_get_field(_jc_env *env, _jc_object *obj) +{ + _jc_jvm *const vm = env->vm; + _jc_object *cl; + _jc_type *type; + jint slot; + + /* Sanity check */ + _JC_ASSERT(obj != NULL && obj->type == vm->boot.types.Field); + + /* Locate declaring class */ + cl = *_JC_VMFIELD(vm, obj, Field, declaringClass, _jc_object *); + type = _jc_get_vm_pointer(cl, vm->boot.fields.Class.vmdata); + _JC_ASSERT(!_JC_FLG_TEST(type, ARRAY)); + + /* Locate field */ + slot = *_JC_VMFIELD(vm, obj, Field, slot, jint); + _JC_ASSERT(slot >= 0 && slot < type->u.nonarray.num_fields); + return type->u.nonarray.fields[slot]; +} + + +/* + * Invoke a method or constructor via reflection. + * + * This function is used by both Method.invoke() and Constructor.newInstance(). + */ +jint +_jc_reflect_invoke(_jc_env *env, _jc_method *method, + _jc_object *this, _jc_object_array *pobjs) +{ + const jboolean is_static = _JC_ACC_TEST(method, STATIC); + const jboolean is_constructor = !is_static && *method->name == '<'; + _jc_method *imethod = NULL; + jobject this_ref = NULL; + _jc_word *params; + jint status; + int nwords; + int pi; + int i; + + /* Check class is instantiable (if constructor) */ + if (is_constructor + && (method->class->access_flags + & (_JC_ACC_ABSTRACT|_JC_ACC_INTERFACE)) != 0) { + _jc_post_exception(env, _JC_InstantiationException); + goto fail; + } + + /* Check for null 'this' */ + if (!is_static && !is_constructor && this == NULL) { + _jc_post_exception(env, _JC_NullPointerException); + goto fail; + } + + /* Check that 'this' is the right kind of instance */ + if (!is_static && !is_constructor) { + switch (_jc_instance_of(env, this, method->class)) { + case 1: + break; + case 0: + _jc_post_exception_msg(env, + _JC_IllegalArgumentException, + "`%s' object is not an instance of `%s'", + this->type->name, method->class->name); + goto fail; + case -1: + goto fail; + default: + _JC_ASSERT(JNI_FALSE); + } + } + + /* Resolve interface methods using hash table lookup */ + if (!is_static && !is_constructor + && _JC_ACC_TEST(method->class, INTERFACE)) { + const jlong sig_hash = method->signature_hash; + _jc_type *const type = this->type; + const int bucket = (int)sig_hash & (_JC_IMETHOD_HASHSIZE - 1); + _jc_method *const *methodp; + + _JC_ASSERT(type->imethod_hash_table != NULL); + for (methodp = type->imethod_hash_table[bucket]; + *methodp != NULL; methodp++) { + if ((*methodp)->signature_hash == sig_hash) { + imethod = method; + method = *methodp; + break; + } + } + _JC_ASSERT(*methodp != NULL); + } + + /* Check for exceptions */ + if (method->num_parameters > 0 && pobjs == NULL) { + _jc_post_exception(env, _JC_NullPointerException); + goto fail; + } + if (pobjs != NULL && pobjs->length != method->num_parameters) { + _jc_post_exception_msg(env, _JC_IllegalArgumentException, + "number of parameters (%d) does not match method (%d)", + pobjs->length, method->num_parameters); + goto fail; + } + + /* Count parameter words */ + nwords = method->num_parameters; + for (i = 0; i < method->num_parameters; i++) { + if (_jc_dword_type[method->param_ptypes[i]]) + nwords++; + } + + /* Allocate parameter array */ + if ((params = _JC_STACK_ALLOC(env, nwords * sizeof(*params))) == NULL) { + _jc_post_exception_info(env); + goto fail; + } + + /* Convert parameters */ + for (pi = i = 0; i < method->num_parameters; i++) { + _jc_type *const ptype = method->param_types[i]; + const int dtype = (ptype->flags & _JC_TYPE_MASK); + _jc_value value; + + /* Convert parameter object into a _jc_value */ + if (dtype == _JC_TYPE_REFERENCE) { + _jc_object *const obj = pobjs->elems[~i]; + + /* Check parameter has the proper type */ + if (obj != NULL) { + switch (_jc_instance_of(env, obj, ptype)) { + case 1: + break; + case 0: + _jc_post_exception(env, + _JC_IllegalArgumentException); + goto fail; + case -1: + goto fail; + default: + _JC_ASSERT(JNI_FALSE); + } + } + value.l = obj; + } else { + int stype; + + if ((stype = _jc_unwrap_primitive(env, + pobjs->elems[~i], &value)) == _JC_TYPE_INVALID) + goto fail; + if (_jc_convert_primitive(env, + dtype, stype, &value) != JNI_OK) + goto fail; + } + + /* Add value to parameter list */ + switch (dtype) { + case _JC_TYPE_BOOLEAN: + params[pi++] = (jint)value.z; + break; + case _JC_TYPE_BYTE: + params[pi++] = (jint)value.b; + break; + case _JC_TYPE_CHAR: + params[pi++] = (jint)value.c; + break; + case _JC_TYPE_SHORT: + params[pi++] = (jint)value.s; + break; + case _JC_TYPE_INT: + params[pi++] = value.i; + break; + case _JC_TYPE_FLOAT: + { + const jfloat param = value.f; + + memcpy(params + pi, ¶m, sizeof(param)); + pi++; + break; + } + case _JC_TYPE_LONG: + { + const jlong param = value.j; + + memcpy(params + pi, ¶m, sizeof(param)); + pi += 2; + break; + } + case _JC_TYPE_DOUBLE: + { + const jdouble param = value.d; + + memcpy(params + pi, ¶m, sizeof(param)); + pi += 2; + break; + } + case _JC_TYPE_REFERENCE: + params[pi++] = (_jc_word)value.l; + break; + default: + _JC_ASSERT(JNI_FALSE); + break; + } + } + + /* Create new instance (if constructor) */ + if (is_constructor) { + if ((this_ref = _jc_new_local_native_ref(env, + _jc_new_object(env, method->class))) == NULL) + goto fail; + this = *this_ref; + } + + /* For JDK compatibility, interface methods cause initialization */ + if (imethod != NULL + && !_JC_FLG_TEST(imethod->class, INITIALIZED) + && _jc_initialize_type(env, imethod->class) != JNI_OK) + goto fail; + + /* Invoke method */ + if (is_static) { + if (!_JC_FLG_TEST(method->class, INITIALIZED) + && _jc_initialize_type(env, method->class) != JNI_OK) + goto fail; + status = _jc_invoke_static_a(env, method, params); + } else if (is_constructor) + status = _jc_invoke_nonvirtual_a(env, method, this, params); + else + status = _jc_invoke_virtual_a(env, method, this, params); + + /* Handle exception thrown by method */ + if (status != JNI_OK) { + _jc_object *e; + _jc_word param; + + /* Get thrown exception */ + e = _jc_retrieve_exception(env, NULL); + _JC_ASSERT(e != NULL); + param = (_jc_word)e; + + /* Wrap it in a InvocationTargetException */ + _jc_post_exception_params(env, + _JC_InvocationTargetException, ¶m); + goto fail; + } + + /* For constructors, return value is new object */ + if (is_constructor) { + _JC_ASSERT(this != NULL); + env->retval.l = this; + } + + /* Done */ + _jc_free_local_native_ref(&this_ref); + return JNI_OK; + +fail: + /* Clean up after failure */ + _jc_free_local_native_ref(&this_ref); + return JNI_ERR; +} + +/* + * Return the parameter types of a method or constructor as a Class[] array. + */ +_jc_object_array * +_jc_get_parameter_types(_jc_env *env, _jc_method *method) +{ + _jc_jvm *const vm = env->vm; + _jc_object_array *array; + int i; + + /* Create Class[] array */ + if ((array = (_jc_object_array *)_jc_new_array(env, + vm->boot.types.Class_array, method->num_parameters)) == NULL) + return NULL; + + /* Fill array */ + for (i = 0; i < method->num_parameters; i++) + array->elems[~i] = method->param_types[i]->instance; + + /* Done */ + return array; +} + +/* + * Return the exception types of a method or constructor as a Class[] array. + */ +_jc_object_array * +_jc_get_exception_types(_jc_env *env, _jc_method *method) +{ + _jc_jvm *const vm = env->vm; + _jc_object_array *array; + int i; + + /* Create Class[] array */ + if ((array = (_jc_object_array *)_jc_new_array(env, + vm->boot.types.Class_array, method->num_exceptions)) == NULL) + return NULL; + + /* Fill array */ + for (i = 0; i < method->num_exceptions; i++) + array->elems[~i] = method->exceptions[i]->instance; + + /* Done */ + return array; +} + +/* + * Wrap a primitive value in an instance of the corresponding + * java.lang.X wrapper class. + * + * Returns NULL and posts an exception on error. + */ +_jc_object * +_jc_wrap_primitive(_jc_env *env, int ptype, _jc_value *value) +{ + _jc_jvm *const vm = env->vm; + jobject ref; + + /* Sanity check */ + _JC_ASSERT(ptype >= _JC_TYPE_BOOLEAN && ptype <= _JC_TYPE_DOUBLE); + + /* Create new wrapper object */ + if ((ref = _jc_new_local_native_ref(env, + _jc_new_object(env, vm->boot.types.prim_wrapper[ptype]))) == NULL) + return NULL; + + /* Invoke constructor */ + if (_jc_invoke_nonvirtual_a(env, + vm->boot.methods.prim_wrapper[ptype].init, + *ref, (_jc_word *)value) != JNI_OK) { + _jc_free_local_native_ref(&ref); + return NULL; + } + + /* Done */ + return _jc_free_local_native_ref(&ref); +} + +/* + * Extract the primitive value from a wrapper class object, + * put the result in *value, and return the primitive type. + * + * If an exception is thrown, _JC_TYPE_INVALID is returned. + */ +int +_jc_unwrap_primitive(_jc_env *env, _jc_object *obj, _jc_value *value) +{ + _jc_jvm *const vm = env->vm; + jint status; + int ptype; + + /* Check for null */ + if (obj == NULL) { + _jc_post_exception(env, _JC_IllegalArgumentException); + return _JC_TYPE_INVALID; + } + + /* Check that wrapper object is really a wrapper object */ + for (ptype = _JC_TYPE_BOOLEAN; + ptype <= _JC_TYPE_DOUBLE + && obj->type != vm->boot.types.prim_wrapper[ptype]; + ptype++); + if (ptype > _JC_TYPE_DOUBLE) { + _jc_post_exception_msg(env, _JC_IllegalArgumentException, + "not a primitive wrapper class instance"); + return _JC_TYPE_INVALID; + } + + /* Extract the primitive value from the wrapper object */ + if ((status = _jc_invoke_virtual(env, + vm->boot.methods.prim_wrapper[ptype].value, obj)) != JNI_OK) + return status; + *value = env->retval; + + /* Done */ + return ptype; +} + +/* + * Convert one primitive type to another by a widening conversion. + * If the conversion is illegal, throw an IllegalArgumentException. + */ +jint +_jc_convert_primitive(_jc_env *env, int dtype, int stype, _jc_value *value) +{ + static const void *const + convert_table[_JC_TYPE_DOUBLE + 1][_JC_TYPE_DOUBLE + 1] = { + [_JC_TYPE_SHORT]= { + [_JC_TYPE_BYTE]= &&b2s, + }, + [_JC_TYPE_INT]= { + [_JC_TYPE_BYTE]= &&b2i, + [_JC_TYPE_CHAR]= &&c2i, + [_JC_TYPE_SHORT]= &&s2i, + }, + [_JC_TYPE_LONG]= { + [_JC_TYPE_BYTE]= &&b2j, + [_JC_TYPE_CHAR]= &&c2j, + [_JC_TYPE_SHORT]= &&s2j, + [_JC_TYPE_INT]= &&i2j, + }, + [_JC_TYPE_FLOAT]= { + [_JC_TYPE_BYTE]= &&b2f, + [_JC_TYPE_CHAR]= &&c2f, + [_JC_TYPE_SHORT]= &&s2f, + [_JC_TYPE_INT]= &&i2f, + [_JC_TYPE_LONG]= &&j2f, + }, + [_JC_TYPE_DOUBLE]= { + [_JC_TYPE_BYTE]= &&b2d, + [_JC_TYPE_CHAR]= &&c2d, + [_JC_TYPE_SHORT]= &&s2d, + [_JC_TYPE_INT]= &&i2d, + [_JC_TYPE_LONG]= &&j2d, + [_JC_TYPE_FLOAT]= &&f2d, + }, + }; + const void *target; + _jc_value svalue; + + /* Sanity check */ + _JC_ASSERT(dtype >= _JC_TYPE_BOOLEAN && dtype <= _JC_TYPE_DOUBLE); + _JC_ASSERT(stype >= _JC_TYPE_BOOLEAN && stype <= _JC_TYPE_DOUBLE); + + /* Handle the trivial case */ + if (dtype == stype) + return JNI_OK; + + /* Check that the conversion is allowed */ + if ((target = convert_table[dtype][stype]) == NULL) { + _jc_post_exception_msg(env, _JC_IllegalArgumentException, + "illegal widening conversion"); + return JNI_ERR; + } + + /* Convert the value by a widening conversion */ + svalue = *value; /* XXX is this step necessary? */ + goto *target; + +b2s: value->s = svalue.b; return JNI_OK; +b2i: value->i = svalue.b; return JNI_OK; +c2i: value->i = svalue.c; return JNI_OK; +s2i: value->i = svalue.s; return JNI_OK; +b2j: value->j = svalue.b; return JNI_OK; +c2j: value->j = svalue.c; return JNI_OK; +s2j: value->j = svalue.s; return JNI_OK; +i2j: value->j = svalue.i; return JNI_OK; +b2f: value->f = svalue.b; return JNI_OK; +c2f: value->f = svalue.c; return JNI_OK; +s2f: value->f = svalue.s; return JNI_OK; +i2f: value->f = svalue.i; return JNI_OK; +j2f: value->f = svalue.j; return JNI_OK; +b2d: value->d = svalue.b; return JNI_OK; +c2d: value->d = svalue.c; return JNI_OK; +s2d: value->d = svalue.s; return JNI_OK; +i2d: value->d = svalue.i; return JNI_OK; +j2d: value->d = svalue.j; return JNI_OK; +f2d: value->d = svalue.f; return JNI_OK; +} + +/* + * Determine whether a class member is accessible from the calling method. + * The "calling method" is the first non-reflection method on the stack. + * + * Returns: + * 1 Yes + * 0 No (and *calling_classp points to the calling class) + * -1 Exception posted + */ +int +_jc_reflect_accessible(_jc_env *env, _jc_type *member_class, + uint16_t access, _jc_type **calling_classp) +{ + _jc_jvm *const vm = env->vm; + _jc_type *calling_class = NULL; + _jc_stack_crawl crawl; + int rtn; + + /* If member is public, we're done */ + if ((access & _JC_ACC_PUBLIC) != 0) + return 1; + + /* Lock VM */ + _JC_MUTEX_LOCK(env, vm->mutex); + + /* Crawl the stack until we see a non-bootstrap ClassLoader */ + for (_jc_stack_crawl_first(env, &crawl); + crawl.method != NULL; _jc_stack_crawl_next(vm, &crawl)) { + + /* Skip _jc_invoke_jcni_a() and java.lang.reflect methods */ + if (crawl.method->class == NULL + || strncmp(crawl.method->class->name, "java/lang/reflect/", + sizeof("java/lang/reflect/") - 1) == 0) + continue; + + /* Found calling class */ + calling_class = crawl.method->class; + *calling_classp = calling_class; + break; + } + + /* Unlock VM */ + _JC_MUTEX_UNLOCK(env, vm->mutex); + + /* Sanity check */ + _JC_ASSERT(calling_class != NULL); + + /* Compare calling class with member class and flags */ + switch (access & (_JC_ACC_PRIVATE|_JC_ACC_PROTECTED)) { + case _JC_ACC_PRIVATE: + return calling_class == member_class; + break; + case _JC_ACC_PROTECTED: + if ((rtn = _jc_assignable_from(env, + calling_class, member_class)) != 0) + return rtn; + /* FALLTHROUGH */ + case 0: /* package access */ + { + int calling_plen = 0; + int member_plen = 0; + const char *s; + + /* + * Check for same package. + * XXX We should not say classes loaded by different + * XXX class loaders are in the same package. + */ + if ((s = strrchr(member_class->name, '/')) != NULL) + member_plen = s - member_class->name; + if ((s = strrchr(calling_class->name, '/')) != NULL) + calling_plen = s - calling_class->name; + return calling_plen == member_plen + && strncmp(calling_class->name, + member_class->name, member_plen) == 0; + } + default: + _JC_ASSERT(JNI_FALSE); + return -1; /* silence compiler warning */ + } +} + + Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/resolve.c URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/resolve.c?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/resolve.c (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/resolve.c Tue Oct 4 19:19:16 2005 @@ -0,0 +1,888 @@ + +/* + * 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: resolve.c,v 1.18 2005/05/10 17:21:07 archiecobbs Exp $ + */ + +#include "libjc.h" + +/* Internal functions */ +static void _jc_resolve_vtable(_jc_jvm *vm, _jc_type *type); +static jint _jc_resolve_exec(_jc_env *env, _jc_type *type, + _jc_resolve_info *info); +static jint _jc_resolve_java_symbol(_jc_env *env, + _jc_resolve_info *info, const char *name, + Elf_Addr *result, jboolean *found); + +static _jc_elf_resolver _jc_resolve_symbol; + +/* Empty interface method lookup tables */ +const void *_jc_empty_quick_table[_JC_IMETHOD_HASHSIZE]; +_jc_method **_jc_empty_imethod_table[_JC_IMETHOD_HASHSIZE]; + +/* + * Resolve a type. + * + * This means resolving all symbolic references to other types, + * methods, etc. In addition, update the list of implicit references + * from this class' class loader to other types' Class objects. + * + * This function synchronizes on the type's Class object. + * The type must not be an array type. + */ +jint +_jc_resolve_type(_jc_env *env, _jc_type *type) +{ + _jc_class_loader *const loader = type->loader; + _jc_object *const obj = type->instance; + jboolean class_locked = JNI_FALSE; + _jc_resolve_info info; + jint status = JNI_ERR; + int i; + + /* Optimization for array types */ + if (_JC_FLG_TEST(type, ARRAY)) { + _JC_ASSERT(_JC_FLG_TEST(type, RESOLVED)); + return JNI_OK; + } + + /* Initialize resolve info */ + memset(&info, 0, sizeof(info)); + info.type = type; + info.loader = loader; + + /* Lock class object (except during initial bootstrap) */ + if (obj != NULL) { + if (_jc_lock_object(env, obj) != JNI_OK) + goto fail; + class_locked = JNI_TRUE; + } + + /* Already resolved? */ + if (_JC_FLG_TEST(type, RESOLVED)) { + _JC_ASSERT(_JC_FLG_TEST(type, LOADED)); + _JC_ASSERT(_JC_FLG_TEST(type, VERIFIED)); + _JC_ASSERT(_JC_FLG_TEST(type, PREPARED)); + goto done; + } + + /* Prepare class first */ + if (!_JC_FLG_TEST(type, PREPARED) + && _jc_prepare_type(env, type) != JNI_OK) + goto fail; + + /* For ELF types, resolve type by resolving all ELF symbols */ + if (!_JC_ACC_TEST(type, INTERP) + && _jc_resolve_exec(env, type, &info) != JNI_OK) + goto fail; + + /* Ensure superclass and superinterfaces are resolved */ + if (type->superclass != NULL + && !_JC_FLG_TEST(type->superclass, RESOLVED) + && _jc_resolve_type(env, type->superclass) != JNI_OK) + goto fail; + for (i = 0; i < type->num_interfaces; i++) { + _jc_type *const iftype = type->interfaces[i]; + + if (!_JC_FLG_TEST(iftype, RESOLVED) + && _jc_resolve_type(env, iftype) != JNI_OK) + goto fail; + } + + /* Compute vtable and mtable for this class (as needed) */ + if (!_JC_ACC_TEST(type, INTERFACE)) { + _jc_jvm *const vm = env->vm; + + if (!vm->loader_enabled || !vm->generation_enabled) + _jc_resolve_vtable(vm, type); + } + + /* Resolve interpreted types */ + if (_JC_ACC_TEST(type, INTERP) + && _jc_resolve_interp(env, type, &info) != JNI_OK) + goto fail; + + /* Merge in type's implicit references into class loader's list */ + if (_jc_merge_implicit_refs(env, &info) != JNI_OK) { + _jc_post_exception_info(env); + goto fail; + } + + /* Mark type as resolved */ + type->flags |= _JC_TYPE_RESOLVED; + + /* Free temporary supers info */ + _jc_vm_free(&type->u.nonarray.supers); + +done: + _JC_ASSERT(_JC_FLG_TEST(type, LOADED)); + _JC_ASSERT(_JC_FLG_TEST(type, RESOLVED)); + status = JNI_OK; + goto out; + +fail: + /* Failed */ + status = JNI_ERR; + +out: + /* Unlock class object */ + if (class_locked) { + jint status2; + + if ((status2 = _jc_unlock_object(env, obj)) != JNI_OK) + status = status2; + } + + /* Clean up and return */ + _jc_vm_free(&info.implicit_refs); + return status; +} + +/* + * Resolve vtable and mtable (as needed). + */ +static void +_jc_resolve_vtable(_jc_jvm *vm, _jc_type *type) +{ + _jc_nonarray_type *const ntype = &type->u.nonarray; + int i; + + /* Sanity check */ + _JC_ASSERT(!_JC_ACC_TEST(type, INTERFACE) + && (!vm->loader_enabled || !vm->generation_enabled)); + + /* + * Copy superclass' vtable and mtable. At this point these tables + * are correct except for overridden superclass methods. + */ + if (type->superclass != NULL) { + _jc_type *const stype = type->superclass; + _jc_nonarray_type *const sntype = &stype->u.nonarray; + + /* + * For interpreted types, copy superclass vtable. + * For ELF types, the vtable should already be correct. + */ + if (_JC_ACC_TEST(type, INTERP)) { + memcpy(type->vtable, stype->vtable, + sntype->num_vmethods * sizeof(*type->vtable)); + } + + /* Copy superclass mtable (if needed) */ + memcpy(ntype->mtable, sntype->mtable, + sntype->num_vmethods * sizeof(*ntype->mtable)); + } + + /* Handle overridden methods */ + for (i = 0; i < ntype->num_methods; i++) { + _jc_method *const method = ntype->methods[i]; + _jc_type *stype; + + /* Ignore non-virtual methods */ + if (_JC_ACC_TEST(method, STATIC) || *method->name == '<') + continue; + + /* Override overridden methods */ + _JC_ASSERT(method->function != NULL); + for (stype = type->superclass; + stype != NULL; stype = stype->superclass) { + _jc_method *orm; + + /* Is this method overridden? */ + if ((orm = _jc_method_lookup(stype, method)) == NULL) + continue; + + /* Override in vtable */ + if (_JC_ACC_TEST(type, INTERP)) { + type->vtable[orm->vtable_index] + = method->function; + } else { + _JC_ASSERT(type->vtable[orm->vtable_index] + == method->function); + } + + /* Override in mtable */ + ntype->mtable[orm->vtable_index] = method; + } + } +} + +/* + * Resolve a non-interpreted type, i.e., an ELF-loaded type. + */ +static jint +_jc_resolve_exec(_jc_env *env, _jc_type *type, _jc_resolve_info *info) +{ + _jc_jvm *const vm = env->vm; + _jc_class_loader *const loader = type->loader; + _jc_elf *const elf = type->u.nonarray.u.elf; + jint status; + + /* Sanity check */ + _JC_ASSERT(!_JC_ACC_TEST(type, INTERP)); + + /* Lock loader */ + _JC_MUTEX_LOCK(env, loader->mutex); + +retry: + /* Is this type defined in an ELF file that's already resolved? */ + if (elf->info == NULL) { + _JC_MUTEX_UNLOCK(env, loader->mutex); + return JNI_OK; + } + + /* Is another thread currently resolving this ELF object? */ + if (elf->info->resolver != NULL) { + + /* If recursively resolving, something wierd is happening */ + if (elf->info->resolver == env) { + _JC_MUTEX_UNLOCK(env, loader->mutex); + _jc_post_exception_msg(env, _JC_LinkageError, + "recursively resolving `%s'", elf->pathname); + return JNI_ERR; + } + + /* Wait for the other thread to finish */ + _jc_loader_wait(env, loader); + goto retry; + } + + /* Mark this thread as resolving */ + elf->info->resolver = env; + + /* Unlock loader */ + _JC_MUTEX_UNLOCK(env, loader->mutex); + + /* Verbosity */ + if (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, + loader->instance->type->name, loader->instance); + } + + /* + * Resolve all external symbols in the ELF image. As we resolve + * symbols that refer to other types, update the list of implicit + * references from this type to other types. + */ + status = _jc_elf_link(env, elf, _jc_resolve_symbol, info); + + /* Lock loader */ + _JC_MUTEX_LOCK(env, loader->mutex); + + /* Mark this thread as no longer resolving */ + _JC_ASSERT(elf->info->resolver == env); + elf->info->resolver = NULL; + + /* Free ELF linking info; this signals that the ELF file is resolved */ + if (status == JNI_OK) { + _jc_elf_link_cleanup(elf); + _JC_ASSERT(elf->info == NULL); + } + + /* Wake up other threads waiting on us */ + if (loader->waiters) { + loader->waiters = JNI_FALSE; + _JC_COND_BROADCAST(loader->cond); + } + + /* Unlock loader */ + _JC_MUTEX_UNLOCK(env, loader->mutex); + + /* Done */ + return status; +} + +/* + * Resolve a field per JVMS section 5.4.3.2. + * Note the static-ness of the field is not checked. + * + * Returns NULL and stores a NoSuchFieldError upon failure. + */ +_jc_field * +_jc_resolve_field(_jc_env *env, _jc_type *type, + const char *name, const char *sig, int is_static) +{ + _jc_field *field; + int i; + + /* Sanity check */ + _JC_ASSERT(!_JC_FLG_TEST(type, ARRAY)); + + /* Search for field in class */ + if ((field = _jc_get_declared_field(env, + type, name, sig, is_static)) != NULL) + return field; + + /* Search for field in superinterfaces */ + for (i = 0; i < type->num_interfaces; i++) { + _jc_type *itype; + + itype = (!_JC_FLG_TEST(type, RESOLVED) + && !_JC_ACC_TEST(type, INTERP)) ? + type->u.nonarray.supers->interfaces[i] : + type->interfaces[i]; + if ((field = _jc_resolve_field(env, + itype, name, sig, is_static)) != NULL) + return field; + } + + /* Search for field in superclasses */ + while (JNI_TRUE) { + type = (!_JC_FLG_TEST(type, RESOLVED) + && !_JC_ACC_TEST(type, INTERP)) ? + type->u.nonarray.supers->superclass : type->superclass; + if (type == NULL) + break; + if ((field = _jc_get_declared_field(env, + type, name, sig, is_static)) != NULL) + return field; + } + + /* Not found */ + return NULL; +} + +/* + * Resolve a method per JVMS section 5.4.3.3 and 5.4.3.4. + * Note the static-ness of the method is not checked. + * + * Returns NULL and stores an exception upon failure. + */ +_jc_method * +_jc_resolve_method(_jc_env *env, _jc_type *type, + const char *name, const char *sig) +{ + _jc_method *method; + _jc_type *stype; + jboolean clinit; + int i; + + /* Special case for (don't recurse) */ + clinit = strcmp(name, "") == 0; + + /* Search for method in class and superclasses */ + for (stype = type; stype != NULL; ) { + if ((method = _jc_get_declared_method(env, + stype, name, sig, 0, 0)) != NULL) + return method; + if (clinit) + return NULL; + stype = (!_JC_FLG_TEST(stype, RESOLVED) + && !_JC_ACC_TEST(stype, INTERP)) ? + stype->u.nonarray.supers->superclass : stype->superclass; + } + + /* Search for method in superinterfaces */ + for (i = 0; i < type->num_interfaces; i++) { + _jc_type *itype; + + itype = (!_JC_FLG_TEST(type, RESOLVED) + && !_JC_ACC_TEST(type, INTERP)) ? + type->u.nonarray.supers->interfaces[i] : + type->interfaces[i]; + if ((method = _jc_resolve_method(env, + itype, name, sig)) != NULL) + return method; + } + + /* Not found */ + return NULL; +} + +/* + * Resolve an unresolved external reference in an ELF file. + * + * This function is used as a callback from the ELF linker code. + */ +static jint +_jc_resolve_symbol(_jc_env *env, void *arg, const char *name, Elf_Addr *result) +{ + _jc_jvm *const vm = env->vm; + _jc_resolve_info *const info = arg; + _jc_native_lib *lib; + const void *func; + jboolean found; + + /* If not a symbol we know about? */ + if (strncmp(name, "_jc_", 4) != 0) + goto search_external; + + /* Handle "_jc_check_address" */ + if (strcmp(name, "_jc_check_address") == 0) { + *result = (Elf_Addr)vm->check_address; + return JNI_OK; + } + + /* Handle empty interface method tables */ + if (strcmp(name, "_jc_empty_quick_table") == 0) { + *result = (Elf_Addr)_jc_empty_quick_table; + return JNI_OK; + } + if (strcmp(name, "_jc_empty_imethod_table") == 0) { + *result = (Elf_Addr)_jc_empty_imethod_table; + return JNI_OK; + } + + /* Search C support functions, which all have "_jc_cs_" prefix */ + if (strncmp(name, "_jc_cs_", 7) == 0) { + if ((func = dlsym(NULL, name)) != NULL) { + *result = (Elf_Addr)func; + return JNI_OK; + } + } + + /* Try to parse a Java type, method, etc. symbol */ + if (_jc_resolve_java_symbol(env, info, name, result, &found) != JNI_OK) + return JNI_ERR; + if (found) + return JNI_OK; + + /* + * If symbol started with "_jc_" but was not found, then + * generate an error. Alternately, we could try to search + * for it in external libraries, but that is likely to not + * be correct and means a less clear namespace separation. + */ + goto not_found; + +search_external: + /* + * If we get here, the symbol is not a Java symbol. + * Search for it first in the shared libraries associated with + * the class loader, then in the shared C libraries that are + * loaded on behalf of this process by the normal runtime linker. + */ + + /* Search the loader's shared libraries */ + STAILQ_FOREACH(lib, &info->loader->native_libs, link) { + if ((func = dlsym(lib->handle, name)) != NULL) { + *result = (Elf_Addr)func; + return JNI_OK; + } + } + + /* Search normally loaded shared libraries */ + if ((func = dlsym(RTLD_DEFAULT, name)) != NULL) { + *result = (Elf_Addr)func; + return JNI_OK; + } + +#ifdef __linux__ + { + static void *kludge; + + /* Stupid Linux. dlsym() won't retrieve GCC helper functions */ + if ((kludge != NULL + || (kludge = dlopen("/lib/libgcc_s.so.1", RTLD_NOW)) != NULL) + && (func = dlsym(kludge, name)) != NULL) { + *result = (Elf_Addr)func; + return JNI_OK; + } + } +#endif + +not_found: + /* Symbol not found */ + _jc_post_exception_msg(env, _JC_LinkageError, + "can't resolve symbol `%s' in ELF object `%s'", + name, info->type->u.nonarray.u.elf->pathname); + return JNI_ERR; +} + +/* + * Resolve a symbol that refers to a Java type, method, etc. + * + * Sets *found to true or false depending on the outcome. + * If not found, *result will not be modified and JNI_OK + * is returned. + * + * An error is returned only if an exception is thrown. + */ +static jint +_jc_resolve_java_symbol(_jc_env *env, _jc_resolve_info *info, + const char *name, Elf_Addr *result, jboolean *found) +{ + _jc_jvm *const vm = env->vm; + int ptype = _JC_TYPE_INVALID; + char *class_name; + const char *s; + _jc_type *type; + int dims = 0; + char *p; + int i; + + /* Find dollar sign */ + if (strncmp(name, "_jc_", 4) != 0) + goto unknown; + name += 4; + if ((s = strchr(name, '$')) == NULL) + goto unknown; + s++; + + /* Check for an array type and get dimensions if so */ + if (strncmp(s, "array", 5) == 0) { + s += 5; + for (i = 0; i < 3; i++) { + if (!isdigit(s[i])) + break; + if (dims == 0 && s[i] == '0') + break; + dims = dims * 10 + (s[i] - '0'); + if (dims < 0 || dims > 255) + goto unknown; + } + if (dims == 0) /* "$array$x" abbreviates "$array1$x" */ + dims = 1; + if (s[i] != '$') + goto unknown; + s += i + 1; + } + + /* Check for a primitive base type */ + if (strncmp(s, "prim$", 5) == 0) { + s += 5; /* advance past the "prim$" */ + switch (*name) { + case 'b': /* boolean or byte */ + if (strncmp(name, "boolean$", 8) == 0) + ptype = _JC_TYPE_BOOLEAN; + else if (strncmp(name, "byte$", 5) == 0) + ptype = _JC_TYPE_BYTE; + break; + case 'c': /* char */ + if (strncmp(name, "char$", 5) == 0) + ptype = _JC_TYPE_CHAR; + break; + case 's': /* short */ + if (strncmp(name, "short$", 6) == 0) + ptype = _JC_TYPE_SHORT; + break; + case 'i': /* int */ + if (strncmp(name, "int$", 4) == 0) + ptype = _JC_TYPE_INT; + break; + case 'l': /* long */ + if (strncmp(name, "long$", 5) == 0) + ptype = _JC_TYPE_LONG; + break; + case 'f': /* float */ + if (strncmp(name, "float$", 6) == 0) + ptype = _JC_TYPE_FLOAT; + break; + case 'd': /* double */ + if (strncmp(name, "double$", 7) == 0) + ptype = _JC_TYPE_DOUBLE; + break; + case 'v': /* void */ + if (strncmp(name, "void$", 5) == 0) + ptype = _JC_TYPE_VOID; + break; + default: + break; + } + + /* Bail out if none matched */ + if (ptype == _JC_TYPE_INVALID) + goto unknown; + + /* Handle zero and one dimensional types directly */ + switch (dims) { + case 0: + type = vm->boot.types.prim[ptype]; + goto got_type; + case 1: + if (ptype == _JC_TYPE_VOID) + goto unknown; /* no void[] type */ + type = vm->boot.types.prim_array[ptype]; + goto got_type; + default: + break; + } + + /* Generate multi-dimensional array type name */ + if ((class_name = _JC_STACK_ALLOC(env, dims + 2)) == NULL) { + _jc_post_exception_info(env); + return JNI_ERR; + } + memset(class_name, '[', dims); + class_name[dims] = _jc_prim_chars[ptype]; + class_name[dims + 1] = '\0'; + goto resolve_type; + } + + /* Generate (possibly array) type name */ + if ((class_name = _JC_STACK_ALLOC(env, + dims + (s - name) + 3)) == NULL) { + _jc_post_exception_info(env); + return JNI_ERR; + } + p = class_name; + if (dims > 0) { + memset(p, '[', dims); + p += dims; + *p++ = 'L'; + } + if ((p = _jc_name_decode(name, p)) == NULL) + goto unknown; + if (dims > 0) + *p++ = ';'; + *p = '\0'; + +resolve_type: + /* + * Resolve the type. If we can't, but "jc.ignore.resolution.failures" + * is set to true, then ignore the failure and just return zero. + */ + if ((type = _jc_load_type(env, info->loader, class_name)) == NULL) { + if (vm->ignore_resolution_failures + && _jc_unpost_exception(env, _JC_LinkageError)) { + *result = (Elf_Addr)0; + goto found; + } + return JNI_ERR; + } + +got_type: + /* Sanity check */ + _JC_ASSERT(type->instance != NULL); + + /* Add a reference to the resolved type's class loader */ + if (_jc_resolve_add_loader_ref(env, info, type->loader) != JNI_OK) { + _jc_post_exception_info(env); + return JNI_ERR; + } + + /* Is just the type needed? */ + if (strcmp(s, "type") == 0) { + *result = (Elf_Addr)type; + goto found; + } + + /* Is the type's Class instance needed? */ + if (strcmp(s, "class_object") == 0) { + *result = (Elf_Addr)type->instance; + _JC_ASSERT(*result != 0); + goto found; + } + + /* Is the type's static fields structure needed? */ + if (strcmp(s, "class_fields") == 0) { + if (_JC_FLG_TEST(type, ARRAY) + || type->u.nonarray.class_fields == NULL) + goto unknown; + *result = (Elf_Addr)type->u.nonarray.class_fields; + _JC_ASSERT(*result != 0); + goto found; + } + + /* Is one of the type's methods or method info structures needed? */ + if (strncmp(s, "method$", 7) == 0 + || strncmp(s, "method_info$", 12) == 0) { + const int is_info = (s[6] == '_'); + size_t name_len; + jlong sig_hash; + char *buf; + + /* Sanity check */ + if (_JC_FLG_TEST(type, ARRAY)) + goto unknown; + + /* Parse out and decode method name */ + name = s + (is_info ? 12 : 7); + if ((s = strchr(name, '$')) == NULL) + goto unknown; + name_len = s - name; + s++; + if (strncmp(name, "_init", 5) == 0) + name = ""; + else if (strncmp(name, "_clinit", 7) == 0) + name = ""; + else { + if ((buf = _JC_STACK_ALLOC(env, + name_len + 1)) == NULL) { + _jc_post_exception_info(env); + return JNI_ERR; + } + if (_jc_name_decode(name, buf) == NULL) + goto unknown; + name = buf; + } + + /* Parse out method signature hash */ + for (sig_hash = 0, i = 0; i < 16 && s[i] != '\0'; i++) { + if (!isxdigit(s[i])) + goto unknown; + sig_hash = (sig_hash << 4) | _JC_HEXVAL(s[i]); + } + + /* Intpreted types must be resolved here */ + if (_JC_ACC_TEST(type, INTERP) + && _jc_resolve_type(env, type) != JNI_OK) + return JNI_ERR; + + /* Search for method */ + for (i = 0; i < type->u.nonarray.num_methods; i++) { + _jc_method *const method = type->u.nonarray.methods[i]; + + /* Is this the method? */ + if (method->signature_hash != sig_hash + || strcmp(method->name, name) != 0) + continue; + + /* Return method info if desired */ + if (is_info) { + *result = (Elf_Addr)method; + _JC_ASSERT(*result != 0); + goto found; + } + + /* + * If method is native, JCNI, and resolved, + * optimize by linking directly to it if enabled. + */ + if (_JC_ACC_TEST(method, NATIVE) + && vm->resolve_native_directly) { + + /* Resolve native method if not already */ + if (method->native_function == NULL) { + if (_jc_resolve_native_method(env, + method) != JNI_OK + && !_jc_unpost_exception(env, + _JC_UnsatisfiedLinkError)) + return JNI_ERR; + } + + /* Method must be resolved and a JCNI method */ + if (method->native_function != NULL + && _JC_ACC_TEST(method, JCNI)) { + *result = (Elf_Addr) + method->native_function; + _JC_ASSERT(*result != 0); + goto found; + } + } + + /* Resolve normally to method's C function */ + *result = (Elf_Addr)method->function; + _JC_ASSERT(*result != 0); + goto found; + } + + /* Not found */ + goto unknown; + } + +unknown: + /* Not a recognized symbol */ + *found = JNI_FALSE; + return JNI_OK; + +found: + /* Found it */ + *found = JNI_TRUE; + return JNI_OK; +} + +/* + * Add an implicit reference from one class loader to another. + * + * If the two loaders are the same, or the other loader is the boot + * loader (which is never GC'd), then don't bother adding the reference. + * + * If the other loader is already in the list, don't bother adding it. + * + * Stores an exception on failure. + */ +jint +_jc_resolve_add_loader_ref(_jc_env *env, _jc_resolve_info *info, + _jc_class_loader *loader) +{ + _jc_jvm *const vm = env->vm; + _jc_object *ref; + + /* Avoid unecessary references */ + if (loader == info->loader || loader == vm->boot.loader) + return JNI_OK; + + /* + * Sanity check: the boot loader should never reference other + * class loaders directly, because the boot loader has no parent. + */ + _JC_ASSERT(info->loader != vm->boot.loader); + + /* Sanity check: non-boot loaders have associated ClassLoader objects */ + _JC_ASSERT(loader->instance != NULL); + + /* Get reference to the ClassLoader object */ + ref = loader->instance; + + /* Add reference to loader instance */ + return _jc_resolve_add_ref(env, info, ref); +} + +/* + * Add an implicit reference from one class loader to an object. + * + * Stores an exception on failure. + */ +jint +_jc_resolve_add_ref(_jc_env *env, _jc_resolve_info *info, _jc_object *ref) +{ + int lim; + int i; + + /* Sanity check */ + if (ref == NULL) + return JNI_OK; + + /* See if reference already exists in list (via binary search) */ + for (i = 0, lim = info->num_implicit_refs; lim != 0; lim >>= 1) { + const int j = i + (lim >> 1); + + if (ref == info->implicit_refs[j]) + return JNI_OK; + if (ref > info->implicit_refs[j]) { + i = j + 1; + lim--; + } + } + + /* Make room in the array for the new reference */ + if (info->num_implicit_refs == info->num_implicit_alloc) { + const int new_alloc = info->num_implicit_alloc + + _JC_CL_ALLOC_IMPLICIT_REFS; + _jc_object **new_refs; + + if ((new_refs = _jc_vm_realloc(env, info->implicit_refs, + new_alloc * sizeof(*new_refs))) == NULL) + return JNI_ERR; + info->implicit_refs = new_refs; + info->num_implicit_alloc = new_alloc; + } + + /* Shift higher references over by one */ + memmove(info->implicit_refs + i + 1, info->implicit_refs + i, + (info->num_implicit_refs++ - i) * sizeof(*info->implicit_refs)); + + /* Insert the new reference in its proper place */ + info->implicit_refs[i] = ref; + return JNI_OK; +} +