/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This file is part of SableVM. * * See the file "LICENSE" for Copyright information and the * * terms and conditions for copying, distribution and * * modification of SableVM. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* ---------------------------------------------------------------------- _svmf_resolve_super_interfaces ---------------------------------------------------------------------- */ static jint _svmf_resolve_super_interfaces (_svmt_JNIEnv *env, _svmt_class_info *class) { _svmt_JavaVM *vm = env->vm; jint i; assert (class->class_loader_info->class_loader != NULL); for (i = 0; i < class->interfaces_count; i++) { _svmt_CONSTANT_Class_info **cp_super_interface = class->interfaces[i]; if (CANNOT_DREF (cp_super_interface) || DREF (cp_super_interface, tag) != SVM_CONSTANT_Class || CANNOT_DREF (DREF (cp_super_interface, name)) || DREF (DREF (cp_super_interface, name), tag) != SVM_CONSTANT_Utf8 || DREF (DREF (cp_super_interface, name), value)[0] == '[') { _svmf_error_ClassFormatError (env); return JNI_ERR; } if (_svmf_resolve_CONSTANT_Class (env, class, *cp_super_interface) != JNI_OK) { return JNI_ERR; } assert (!(DREF (cp_super_interface, type)->is_array)); if (!_svmf_is_interface (_svmf_cast_class (DREF (cp_super_interface, type)))) { _svmf_error_IncompatibleClassChangeError (env); return JNI_ERR; } } return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_resolve_super_class ---------------------------------------------------------------------- */ static jint _svmf_resolve_super_class (_svmt_JNIEnv *env, _svmt_class_info *class) { _svmt_JavaVM *vm = env->vm; _svmt_CONSTANT_Class_info **cp_super_class = class->super_class; assert (class->class_loader_info->class_loader != NULL); /* must have a super class */ if (CANNOT_DREF (cp_super_class)) { _svmf_error_VerifyError (env); return JNI_ERR; } if (DREF (cp_super_class, tag) != SVM_CONSTANT_Class || CANNOT_DREF (DREF (cp_super_class, name)) || DREF (DREF (cp_super_class, name), tag) != SVM_CONSTANT_Utf8 || DREF (DREF (cp_super_class, name), value)[0] == '[') { _svmf_error_ClassFormatError (env); return JNI_ERR; } if (_svmf_resolve_CONSTANT_Class (env, class, *cp_super_class) != JNI_OK) { return JNI_ERR; } assert (!(DREF (cp_super_class, type)->is_array)); if (_svmf_is_interface (class) && DREF (cp_super_class, type) != _svmf_cast_type_class (vm->class_loading.boot_loader.classes.jlobject)) { _svmf_error_IncompatibleClassChangeError (env); return JNI_ERR; } if (_svmf_is_interface (_svmf_cast_class (DREF (cp_super_class, type)))) { _svmf_error_IncompatibleClassChangeError (env); return JNI_ERR; } return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_bootcl_resolve_super_interfaces ---------------------------------------------------------------------- */ static jint _svmf_bootcl_resolve_super_interfaces (_svmt_JNIEnv *env, _svmt_class_info *class) { _svmt_JavaVM *vm = env->vm; jint i; assert (class->class_loader_info->class_loader == NULL); for (i = 0; i < class->interfaces_count; i++) { _svmt_CONSTANT_Class_info **cp_super_interface = class->interfaces[i]; if (CANNOT_DREF (cp_super_interface) || DREF (cp_super_interface, tag) != SVM_CONSTANT_Class || CANNOT_DREF (DREF (cp_super_interface, name)) || DREF (DREF (cp_super_interface, name), tag) != SVM_CONSTANT_Utf8 || DREF (DREF (cp_super_interface, name), value)[0] == '[') { _svmf_error_ClassFormatError (env); return JNI_ERR; } { _svmt_type_node type = { NULL, NULL, NULL, NULL, NULL }; type.name = DREF (DREF (cp_super_interface, name), value); if (_svmm_tree_find_type (vm->class_loading.boot_loader.partially_derived_type_tree, &type) != NULL) { _svmf_error_ClassCircularityError (env); return JNI_ERR; } } if (_svmf_resolve_CONSTANT_Class (env, class, *cp_super_interface) != JNI_OK) { return JNI_ERR; } assert (!(DREF (cp_super_interface, type)->is_array)); if (!_svmf_is_interface (_svmf_cast_class (DREF (cp_super_interface, type)))) { _svmf_error_IncompatibleClassChangeError (env); return JNI_ERR; } } return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_bootcl_resolve_super_class ---------------------------------------------------------------------- */ static jint _svmf_bootcl_resolve_super_class (_svmt_JNIEnv *env, _svmt_class_info *class) { _svmt_JavaVM *vm = env->vm; _svmt_CONSTANT_Class_info **cp_super_class = class->super_class; assert (class->class_loader_info->class_loader == NULL); /* no super class -> we're done */ if (CANNOT_DREF (cp_super_class)) { /* this must be "java/lang/Object" */ /* and it must be a public non-abstract non-final class */ if (strcmp (class->name, "java/lang/Object") != 0 || class->class_loader_info->class_loader != NULL || (!_svmf_is_set_flag (class->access_flags, SVM_ACC_PUBLIC)) || _svmf_is_set_flag (class->access_flags, SVM_ACC_FINAL) || _svmf_is_set_flag (class->access_flags, SVM_ACC_INTERFACE) || _svmf_is_set_flag (class->access_flags, SVM_ACC_ABSTRACT)) { _svmf_error_VerifyError (env); return JNI_ERR; } return JNI_OK; } if (DREF (cp_super_class, tag) != SVM_CONSTANT_Class || CANNOT_DREF (DREF (cp_super_class, name)) || DREF (DREF (cp_super_class, name), tag) != SVM_CONSTANT_Utf8 || DREF (DREF (cp_super_class, name), value)[0] == '[') { _svmf_error_ClassFormatError (env); return JNI_ERR; } { _svmt_type_node type = { NULL, NULL, NULL, NULL, NULL }; type.name = DREF (DREF (cp_super_class, name), value); if (_svmm_tree_find_type (vm->class_loading.boot_loader.partially_derived_type_tree, &type) != NULL) { _svmf_error_ClassCircularityError (env); return JNI_ERR; } } if (_svmf_resolve_CONSTANT_Class (env, class, *cp_super_class) != JNI_OK) { return JNI_ERR; } assert (!(DREF (cp_super_class, type)->is_array)); if (_svmf_is_interface (class) && DREF (cp_super_class, type) != _svmf_cast_type_class (vm->class_loading.boot_loader.classes.jlobject)) { _svmf_error_IncompatibleClassChangeError (env); return JNI_ERR; } if (_svmf_is_interface (_svmf_cast_class (DREF (cp_super_class, type)))) { _svmf_error_IncompatibleClassChangeError (env); return JNI_ERR; } return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_bootcl_derive_class ---------------------------------------------------------------------- */ static jint _svmf_bootcl_derive_class (_svmt_JNIEnv *env, const char *class_name, _svmt_class_file *class_file, _svmt_class_info **pclass, jboolean free_bytes) { _svmt_JavaVM *vm = env->vm; _svmt_class_info *class; /* has the boot loader been recorded as an initiating loader of "name"? */ { _svmt_type_node type = { NULL, NULL, NULL, NULL, NULL }; type.name = class_name; if (_svmm_tree_find_type (vm->class_loading.boot_loader.initiated_type_tree, &type) != NULL) { /* yes it has! */ _svmf_error_LinkageError (env); return JNI_ERR; } } /* parse the bytes */ if (_svmm_parse_class_file (env, vm->class_loading.boot_loader.class_loader_info, class_file->length, class_file->bytes, class) != JNI_OK) { return JNI_ERR; } /* were're done with the bytes, so free them to allow recursive calls */ if (free_bytes) { _svmm_gmfree_ubytes (class_file->bytes); class_file->length = 0; } else { class_file->bytes = NULL; class_file->length = 0; } /* check version and name */ if (!(class->major_version == 45 || (class->major_version == 46 && class->minor_version == 0))) { _svmf_error_UnsupportedClassVersionError (env); return JNI_ERR; } if (strcmp (class_name, class->name) != 0) { _svmf_error_NoClassDefFoundError (env); return JNI_ERR; } { _svmt_type_node *partially_derived_type; if (_svmm_gzalloc_type_node (env, partially_derived_type) != JNI_OK) { return JNI_ERR; } partially_derived_type->name = class->name; partially_derived_type->type = _svmf_cast_type_class (class); _svmm_tree_insert_type (vm->class_loading.boot_loader. partially_derived_type_tree, partially_derived_type); } if (_svmf_bootcl_resolve_super_class (env, class) != JNI_OK || _svmf_bootcl_resolve_super_interfaces (env, class) != JNI_OK) { _svmt_type_node type = { NULL, NULL, NULL, NULL, NULL }; _svmt_type_node *partially_derived_type; type.name = class->name; partially_derived_type = _svmm_tree_find_type (vm->class_loading.boot_loader. partially_derived_type_tree, &type); _svmm_tree_remove_type (vm->class_loading.boot_loader. partially_derived_type_tree, partially_derived_type); assert (partially_derived_type->type == _svmf_cast_type_class (class)); _svmm_gzfree_type_node (partially_derived_type); return JNI_ERR; } if (vm->class_loading.boot_loader.classes.jlclass != NULL) { if (_svmf_new_class (env, _svmf_cast_type_class (class)) != JNI_OK) { _svmt_type_node type = { NULL, NULL, NULL, NULL, NULL }; _svmt_type_node *partially_derived_type; type.name = class->name; partially_derived_type = _svmm_tree_find_type (vm->class_loading.boot_loader. partially_derived_type_tree, &type); _svmm_tree_remove_type (vm->class_loading.boot_loader. partially_derived_type_tree, partially_derived_type); assert (partially_derived_type->type == _svmf_cast_type_class (class)); _svmm_gzfree_type_node (partially_derived_type); return JNI_ERR; } } { _svmt_type_node type = { NULL, NULL, NULL, NULL, NULL }; _svmt_type_node *derived_type; type.name = class->name; derived_type = _svmm_tree_find_type (vm->class_loading.boot_loader. partially_derived_type_tree, &type); _svmm_tree_remove_type (vm->class_loading.boot_loader. partially_derived_type_tree, derived_type); assert (derived_type->type == _svmf_cast_type_class (class)); _svmm_tree_insert_type (vm->class_loading.boot_loader.initiated_type_tree, derived_type); } *pclass = class; return JNI_OK; } /* ---------------------------------------------------------------------- _svmh_bootcl_internal_load_class_file ---------------------------------------------------------------------- */ static jint _svmh_bootcl_internal_load_class_file (_svmt_JNIEnv *env, const char *class_name, _svmt_class_file *class_file) { _svmt_JavaVM *vm = env->vm; int fd; struct stat stats; void *content; { size_t path_length = strlen (vm->class_loading.boot_loader.boot_class_path) + 1 + strlen (class_name) + 6 + 1; char *path; path = _svmf_malloc (path_length); if (path == NULL) { _svmf_error_OutOfMemoryError (env); return JNI_ERR; } strcpy (path, vm->class_loading.boot_loader.boot_class_path); if (path[strlen (path) - 1] != '/') { strcat (path, "/"); } strcat (path, class_name); strcat (path, ".class"); if ((fd = open (path, O_RDONLY)) == -1) { _svmf_free (path); _svmf_error_NoClassDefFoundError (env); return JNI_ERR; } _svmf_free (path); path = NULL; } if (fstat (fd, &stats) == -1) { close (fd); _svmf_error_NoClassDefFoundError (env); return JNI_ERR; } class_file->length = stats.st_size; /* make sure length > 0 and fits into a "jint" */ if ((class_file->length <= 0) || (class_file->length != stats.st_size)) { class_file->length = 0; close (fd); _svmf_error_ClassFormatError (env); return JNI_ERR; } content = mmap (NULL, class_file->length, PROT_READ, MAP_PRIVATE, fd, 0); if (content == MAP_FAILED) { class_file->length = 0; close (fd); _svmf_error_OutOfMemoryError (env); return JNI_ERR; } if (_svmm_gmalloc_ubytes (env, class_file->length, class_file->bytes) != JNI_OK) { munmap (content, class_file->length); class_file->length = 0; close (fd); _svmf_error_OutOfMemoryError (env); return JNI_ERR; } memcpy (class_file->bytes, content, class_file->length); munmap (content, class_file->length); close (fd); return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_bootcl_create_class ---------------------------------------------------------------------- */ static jint _svmf_bootcl_create_class (_svmt_JNIEnv *env, const char *class_name, _svmt_class_info **pclass) { _svmt_JavaVM *vm = env->vm; /* has the boot loader been recorded as an initiating loader of "name"? */ { _svmt_type_node type = { NULL, NULL, NULL, NULL, NULL }; _svmt_type_node *result; type.name = class_name; if ((result = _svmm_tree_find_type (vm->class_loading.boot_loader. initiated_type_tree, &type)) != NULL) { /* yes it has! */ *pclass = _svmf_cast_class (result->type); return JNI_OK; } } if (vm->verbose_class) { _svmf_printf (env, stdout, "[verbose class: loading \"%s\"]\n", class_name); } assert (vm->class_loading.boot_loader.current_class_file.bytes == NULL); if (_svmm_bootcl_internal_load_class_file (env, class_name, vm->class_loading.boot_loader.current_class_file) != JNI_OK) { return JNI_ERR; } if (_svmf_bootcl_derive_class (env, class_name, &vm->class_loading.boot_loader.current_class_file, pclass, JNI_TRUE) != JNI_OK) { if (vm->class_loading.boot_loader.current_class_file.bytes != NULL) { _svmm_gmfree_ubytes (vm->class_loading.boot_loader. current_class_file.bytes); vm->class_loading.boot_loader.current_class_file.length = 0; } return JNI_ERR; } if (vm->class_loading.boot_loader.current_class_file.bytes != NULL) { _svmm_gmfree_ubytes (vm->class_loading.boot_loader.current_class_file. bytes); vm->class_loading.boot_loader.current_class_file.length = 0; } return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_usercl_create_class ---------------------------------------------------------------------- */ static jint _svmf_usercl_create_class (_svmt_JNIEnv *env, _svmt_class_loader_info *class_loader_info, const char *class_name, _svmt_class_info **pclass) { jobject class_instance; jobject name; if (_svmm_new_native_local (env, class_instance) != JNI_OK) { return JNI_ERR; } if (_svmm_new_native_local (env, name) != JNI_OK) { _svmm_free_native_local (env, class_instance); return JNI_ERR; } if (_svmf_get_string (env, class_name, name) != JNI_OK) { _svmm_free_native_local (env, name); _svmm_free_native_local (env, class_instance); return JNI_ERR; } if (_svmm_invoke_static_virtualmachine_createclass (env, class_loader_info->class_loader, name, class_instance) != JNI_OK) { _svmm_free_native_local (env, name); _svmm_free_native_local (env, class_instance); return JNI_ERR; } *pclass = _svmf_cast_class (_svmf_unwrap_class_instance (env, class_instance)); assert (!((*pclass)->is_array)); _svmm_free_native_local (env, name); _svmm_free_native_local (env, class_instance); return JNI_OK; } /* ---------------------------------------------------------------------- _svmh_create_class ---------------------------------------------------------------------- */ static jint _svmh_create_class (_svmt_JNIEnv *env, _svmt_class_loader_info *class_loader_info, const char *class_name, _svmt_class_info **pclass) { _svmt_JavaVM *vm = env->vm; jboolean monitor_acquired = JNI_FALSE; if (vm->initialization == NULL) { if (_svmf_enter_object_monitor (env, *(vm->class_loading.boot_loader.classes.virtualmachine-> class_instance)) != JNI_OK) { goto error; } monitor_acquired = JNI_TRUE; } assert (class_name[0] != '['); if (class_loader_info->class_loader != NULL) { /* user-defined class loader */ if (_svmf_usercl_create_class (env, class_loader_info, class_name, pclass) != JNI_OK) { goto error; } } else { assert (class_loader_info == vm->class_loading.boot_loader.class_loader_info); /* bootstrap class loader */ if (_svmf_bootcl_create_class (env, class_name, pclass) != JNI_OK) { goto error; } } if (monitor_acquired) { monitor_acquired = JNI_FALSE; if (_svmf_exit_object_monitor (env, *(vm->class_loading.boot_loader.classes.virtualmachine-> class_instance)) != JNI_OK) { goto error; } } return JNI_OK; error: if (monitor_acquired) { monitor_acquired = JNI_FALSE; if (_svmf_exit_object_monitor (env, *(vm->class_loading.boot_loader.classes.virtualmachine-> class_instance)) != JNI_OK) { goto error; } } return JNI_ERR; } /* ---------------------------------------------------------------------- _svmf_bootcl_create_array ---------------------------------------------------------------------- */ static jint _svmf_bootcl_create_array (_svmt_JNIEnv *env, const char *array_name, _svmt_array_info **parray) { _svmt_JavaVM *vm = env->vm; _svmt_array_info *array; /* has the boot loader been recorded as an initiating loader of "name"? */ { _svmt_type_node type = { NULL, NULL, NULL, NULL, NULL }; _svmt_type_node *result; type.name = array_name; if ((result = _svmm_tree_find_type (vm->class_loading.boot_loader. initiated_type_tree, &type)) != NULL) { /* yes it has! */ *parray = _svmf_cast_array (result->type); return JNI_OK; } } if (vm->verbose_class) { _svmf_printf (env, stdout, "[verbose class: creating \"%s\"]\n", array_name); } if (_svmm_cl_zalloc_array_info (env, vm->class_loading.boot_loader.class_loader_info, array) != JNI_OK) { return JNI_ERR; } array->is_array = JNI_TRUE; if (_svmm_cl_malloc_chars (env, vm->class_loading.boot_loader.class_loader_info, strlen (array_name) + 2, array->array_type_name) != JNI_OK) { return JNI_ERR; } array->array_type_name[0] = '['; array->array_type_name[1] = 0; strcat (array->array_type_name, array_name); array->name = &array->array_type_name[1]; { char *base_name = array->name; jint dimensions = 0; size_t length; while (*base_name == '[') { dimensions++; base_name++; if (dimensions > 255) { _svmf_error_VerifyError (env); return JNI_ERR; } } assert (dimensions != 0); array->dimensions = dimensions; if (dimensions > 1) { if (_svmm_create_array (env, vm->class_loading.boot_loader.class_loader_info, &array->name[1], array->array_element) != JNI_OK) { return JNI_ERR; } } length = strlen (base_name); if (length == 1) { array->class_loader_info = vm->class_loading.boot_loader.class_loader_info; array->access_flags = SVM_ACC_PUBLIC; switch (*base_name) { case 'B': { array->base_type = SVM_TYPE_BYTE; } break; case 'C': { array->base_type = SVM_TYPE_CHAR; } break; case 'D': { array->base_type = SVM_TYPE_DOUBLE; } break; case 'F': { array->base_type = SVM_TYPE_FLOAT; } break; case 'I': { array->base_type = SVM_TYPE_INT; } break; case 'J': { array->base_type = SVM_TYPE_LONG; } break; case 'S': { array->base_type = SVM_TYPE_SHORT; } break; case 'Z': { array->base_type = SVM_TYPE_BOOLEAN; } break; default: { _svmf_error_VerifyError (env); return JNI_ERR; } break; } } else { if (length < 3 || base_name[0] != 'L' || base_name[length - 1] != ';') { _svmf_error_VerifyError (env); return JNI_ERR; } base_name++[length-- - 1] = 0; array->base_type = SVM_TYPE_REFERENCE; if (_svmm_create_class (env, vm->class_loading.boot_loader.class_loader_info, base_name, array->base_class) != JNI_OK) { return JNI_ERR; } base_name[length - 1] = ';'; array->class_loader_info = array->base_class->class_loader_info; array->access_flags = array->base_class->access_flags; } } if (vm->class_loading.boot_loader.classes.jlclass != NULL) { if (_svmf_new_class (env, _svmf_cast_type_array (array)) != JNI_OK) { return JNI_ERR; } } { _svmt_type_node *derived_type; if (_svmm_gzalloc_type_node (env, derived_type) != JNI_OK) { return JNI_ERR; } derived_type->name = array->name; derived_type->type = _svmf_cast_type_array (array); _svmm_tree_insert_type (vm->class_loading.boot_loader.initiated_type_tree, derived_type); } *parray = array; return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_usercl_create_array ---------------------------------------------------------------------- */ static jint _svmf_usercl_create_array (_svmt_JNIEnv *env, _svmt_class_loader_info *class_loader_info, const char *array_name, _svmt_array_info **parray) { jobject class_instance; jobject name; if (_svmm_new_native_local (env, class_instance) != JNI_OK) { return JNI_ERR; } if (_svmm_new_native_local (env, name) != JNI_OK) { _svmm_free_native_local (env, class_instance); return JNI_ERR; } if (_svmf_get_string (env, array_name, name) != JNI_OK) { _svmm_free_native_local (env, name); _svmm_free_native_local (env, class_instance); return JNI_ERR; } if (_svmm_invoke_static_virtualmachine_createarray (env, class_loader_info->class_loader, name, class_instance) != JNI_OK) { _svmm_free_native_local (env, name); _svmm_free_native_local (env, class_instance); return JNI_ERR; } *parray = _svmf_cast_array (_svmf_unwrap_class_instance (env, class_instance)); assert ((*parray)->is_array); _svmm_free_native_local (env, name); _svmm_free_native_local (env, class_instance); return JNI_OK; } /* ---------------------------------------------------------------------- _svmh_create_array ---------------------------------------------------------------------- */ static jint _svmh_create_array (_svmt_JNIEnv *env, _svmt_class_loader_info *class_loader_info, const char *array_name, _svmt_array_info **parray) { _svmt_JavaVM *vm = env->vm; jboolean monitor_acquired = JNI_FALSE; if (vm->initialization == NULL) { if (_svmf_enter_object_monitor (env, *(vm->class_loading.boot_loader.classes.virtualmachine-> class_instance)) != JNI_OK) { goto error; } monitor_acquired = JNI_TRUE; } assert (array_name[0] == '['); if (class_loader_info->class_loader != NULL) { /* user-defined class loader */ if (_svmf_usercl_create_array (env, class_loader_info, array_name, parray) != JNI_OK) { goto error; } } else { assert (class_loader_info == vm->class_loading.boot_loader.class_loader_info); /* bootstrap class loader */ if (_svmf_bootcl_create_array (env, array_name, parray) != JNI_OK) { goto error; } } if (monitor_acquired) { monitor_acquired = JNI_FALSE; if (_svmf_exit_object_monitor (env, *(vm->class_loading.boot_loader.classes.virtualmachine-> class_instance)) != JNI_OK) { goto error; } } return JNI_OK; error: if (monitor_acquired) { monitor_acquired = JNI_FALSE; if (_svmf_exit_object_monitor (env, *(vm->class_loading.boot_loader.classes.virtualmachine-> class_instance)) != JNI_OK) { goto error; } } return JNI_ERR; } /* ---------------------------------------------------------------------- _svmh_create_type ---------------------------------------------------------------------- */ static jint _svmh_create_type (_svmt_JNIEnv *env, _svmt_class_loader_info *class_loader_info, const char *type_name, _svmt_type_info **ptype) { if (type_name[0] == '[') { /* array */ return _svmm_create_array (env, class_loader_info, type_name, *_svmf_cast_parray (ptype)); } else { /* nonarray class or interface */ return _svmm_create_class (env, class_loader_info, type_name, *_svmf_cast_pclass (ptype)); } }