diff options
Diffstat (limited to 'libjava/interpret.cc')
-rw-r--r-- | libjava/interpret.cc | 609 |
1 files changed, 587 insertions, 22 deletions
diff --git a/libjava/interpret.cc b/libjava/interpret.cc index 0446c729623..91d4b194774 100644 --- a/libjava/interpret.cc +++ b/libjava/interpret.cc @@ -11,6 +11,7 @@ details. */ /* Author: Kresten Krab Thorup <krab@gnu.org> */ #include <config.h> +#include <platform.h> // Define this to get the direct-threaded interpreter. If undefined, // we revert to a basic bytecode interpreter. The former is faster @@ -38,9 +39,15 @@ details. */ #include <java/lang/Thread.h> #include <java-insns.h> #include <java-signal.h> +#include <java/lang/ClassFormatError.h> +#include <execution.h> +#include <java/lang/reflect/Modifier.h> #ifdef INTERPRETER +// Execution engine for interpreted code. +_Jv_InterpreterEngine _Jv_soleInterpreterEngine; + #include <stdlib.h> using namespace gcj; @@ -54,6 +61,11 @@ static void throw_null_pointer_exception () __attribute__ ((__noreturn__)); #endif +static void throw_class_format_error (jstring msg) + __attribute__ ((__noreturn__)); +static void throw_class_format_error (char *msg) + __attribute__ ((__noreturn__)); + #ifdef DIRECT_THREADED // Lock to ensure that methods are not compiled concurrently. // We could use a finer-grained lock here, however it is not safe to use @@ -765,8 +777,9 @@ _Jv_InterpMethod::compile (const void * const *insn_targets) exc[i].start_pc.p = &insns[pc_mapping[exc[i].start_pc.i]]; exc[i].end_pc.p = &insns[pc_mapping[exc[i].end_pc.i]]; exc[i].handler_pc.p = &insns[pc_mapping[exc[i].handler_pc.i]]; - jclass handler = (_Jv_ResolvePoolEntry (defining_class, - exc[i].handler_type.i)).clazz; + jclass handler + = (_Jv_Linker::resolve_pool_entry (defining_class, + exc[i].handler_type.i)).clazz; exc[i].handler_type.p = handler; } @@ -1113,13 +1126,14 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) { int index = GET2U (); - /* _Jv_ResolvePoolEntry returns immediately if the value already - * is resolved. If we want to clutter up the code here to gain - * a little performance, then we can check the corresponding bit - * JV_CONSTANT_ResolvedFlag in the tag directly. For now, I - * don't think it is worth it. */ + /* _Jv_Linker::resolve_pool_entry returns immediately if the + * value already is resolved. If we want to clutter up the + * code here to gain a little performance, then we can check + * the corresponding bit JV_CONSTANT_ResolvedFlag in the tag + * directly. For now, I don't think it is worth it. */ - rmeth = (_Jv_ResolvePoolEntry (defining_class, index)).rmethod; + rmeth = (_Jv_Linker::resolve_pool_entry (defining_class, + index)).rmethod; sp -= rmeth->stack_item_count; // We don't use NULLCHECK here because we can't rely on that @@ -2409,7 +2423,7 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) insn_getstatic: { jint fieldref_index = GET2U (); - _Jv_ResolvePoolEntry (defining_class, fieldref_index); + _Jv_Linker::resolve_pool_entry (defining_class, fieldref_index); _Jv_Field *field = pool_data[fieldref_index].field; if ((field->flags & Modifier::STATIC) == 0) @@ -2496,7 +2510,7 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) insn_getfield: { jint fieldref_index = GET2U (); - _Jv_ResolvePoolEntry (defining_class, fieldref_index); + _Jv_Linker::resolve_pool_entry (defining_class, fieldref_index); _Jv_Field *field = pool_data[fieldref_index].field; if ((field->flags & Modifier::STATIC) != 0) @@ -2612,7 +2626,7 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) insn_putstatic: { jint fieldref_index = GET2U (); - _Jv_ResolvePoolEntry (defining_class, fieldref_index); + _Jv_Linker::resolve_pool_entry (defining_class, fieldref_index); _Jv_Field *field = pool_data[fieldref_index].field; jclass type = field->type; @@ -2699,7 +2713,7 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) insn_putfield: { jint fieldref_index = GET2U (); - _Jv_ResolvePoolEntry (defining_class, fieldref_index); + _Jv_Linker::resolve_pool_entry (defining_class, fieldref_index); _Jv_Field *field = pool_data[fieldref_index].field; jclass type = field->type; @@ -2825,7 +2839,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) { int index = GET2U (); - rmeth = (_Jv_ResolvePoolEntry (defining_class, index)).rmethod; + rmeth = (_Jv_Linker::resolve_pool_entry (defining_class, + index)).rmethod; sp -= rmeth->stack_item_count; @@ -2863,7 +2878,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) { int index = GET2U (); - rmeth = (_Jv_ResolvePoolEntry (defining_class, index)).rmethod; + rmeth = (_Jv_Linker::resolve_pool_entry (defining_class, + index)).rmethod; sp -= rmeth->stack_item_count; @@ -2892,7 +2908,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) { int index = GET2U (); - rmeth = (_Jv_ResolvePoolEntry (defining_class, index)).rmethod; + rmeth = (_Jv_Linker::resolve_pool_entry (defining_class, + index)).rmethod; sp -= rmeth->stack_item_count; @@ -2935,7 +2952,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) insn_new: { int index = GET2U (); - jclass klass = (_Jv_ResolvePoolEntry (defining_class, index)).clazz; + jclass klass = (_Jv_Linker::resolve_pool_entry (defining_class, + index)).clazz; jobject res = _Jv_AllocObject (klass); PUSHA (res); @@ -2968,7 +2986,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) insn_anewarray: { int index = GET2U (); - jclass klass = (_Jv_ResolvePoolEntry (defining_class, index)).clazz; + jclass klass = (_Jv_Linker::resolve_pool_entry (defining_class, + index)).clazz; int size = POPI(); jobject result = _Jv_NewObjectArray (size, klass, 0); PUSHA (result); @@ -3010,7 +3029,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) { jobject value = POPA(); jint index = GET2U (); - jclass to = (_Jv_ResolvePoolEntry (defining_class, index)).clazz; + jclass to = (_Jv_Linker::resolve_pool_entry (defining_class, + index)).clazz; if (value != NULL && ! to->isInstance (value)) throw new java::lang::ClassCastException (to->getName()); @@ -3040,7 +3060,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) { jobject value = POPA(); jint index = GET2U (); - jclass to = (_Jv_ResolvePoolEntry (defining_class, index)).clazz; + jclass to = (_Jv_Linker::resolve_pool_entry (defining_class, + index)).clazz; PUSHI (to->isInstance (value)); #ifdef DIRECT_THREADED @@ -3102,7 +3123,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) int dim = GET1U (); jclass type - = (_Jv_ResolvePoolEntry (defining_class, kind_index)).clazz; + = (_Jv_Linker::resolve_pool_entry (defining_class, + kind_index)).clazz; jint *sizes = (jint*) __builtin_alloca (sizeof (jint)*dim); for (int i = dim - 1; i >= 0; i--) @@ -3203,8 +3225,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args) #else jclass handler = NULL; if (exc[i].handler_type.i != 0) - handler = (_Jv_ResolvePoolEntry (defining_class, - exc[i].handler_type.i)).clazz; + handler = (_Jv_Linker::resolve_pool_entry (defining_class, + exc[i].handler_type.i)).clazz; #endif /* DIRECT_THREADED */ if (handler == NULL || handler->isAssignableFrom (exc_class)) @@ -3250,4 +3272,547 @@ throw_null_pointer_exception () } #endif +/** Do static initialization for fields with a constant initializer */ +void +_Jv_InitField (jobject obj, jclass klass, int index) +{ + using namespace java::lang::reflect; + + if (obj != 0 && klass == 0) + klass = obj->getClass (); + + if (!_Jv_IsInterpretedClass (klass)) + return; + + _Jv_InterpClass *iclass = (_Jv_InterpClass*)klass->aux_info; + + _Jv_Field * field = (&klass->fields[0]) + index; + + if (index > klass->field_count) + throw_internal_error ("field out of range"); + + int init = iclass->field_initializers[index]; + if (init == 0) + return; + + _Jv_Constants *pool = &klass->constants; + int tag = pool->tags[init]; + + if (! field->isResolved ()) + throw_internal_error ("initializing unresolved field"); + + if (obj==0 && ((field->flags & Modifier::STATIC) == 0)) + throw_internal_error ("initializing non-static field with no object"); + + void *addr = 0; + + if ((field->flags & Modifier::STATIC) != 0) + addr = (void*) field->u.addr; + else + addr = (void*) (((char*)obj) + field->u.boffset); + + switch (tag) + { + case JV_CONSTANT_String: + { + jstring str; + str = _Jv_NewStringUtf8Const (pool->data[init].utf8); + pool->data[init].string = str; + pool->tags[init] = JV_CONSTANT_ResolvedString; + } + /* fall through */ + + case JV_CONSTANT_ResolvedString: + if (! (field->type == &java::lang::String::class$ + || field->type == &java::lang::Class::class$)) + throw_class_format_error ("string initialiser to non-string field"); + + *(jstring*)addr = pool->data[init].string; + break; + + case JV_CONSTANT_Integer: + { + int value = pool->data[init].i; + + if (field->type == JvPrimClass (boolean)) + *(jboolean*)addr = (jboolean)value; + + else if (field->type == JvPrimClass (byte)) + *(jbyte*)addr = (jbyte)value; + + else if (field->type == JvPrimClass (char)) + *(jchar*)addr = (jchar)value; + + else if (field->type == JvPrimClass (short)) + *(jshort*)addr = (jshort)value; + + else if (field->type == JvPrimClass (int)) + *(jint*)addr = (jint)value; + + else + throw_class_format_error ("erroneous field initializer"); + } + break; + + case JV_CONSTANT_Long: + if (field->type != JvPrimClass (long)) + throw_class_format_error ("erroneous field initializer"); + + *(jlong*)addr = _Jv_loadLong (&pool->data[init]); + break; + + case JV_CONSTANT_Float: + if (field->type != JvPrimClass (float)) + throw_class_format_error ("erroneous field initializer"); + + *(jfloat*)addr = pool->data[init].f; + break; + + case JV_CONSTANT_Double: + if (field->type != JvPrimClass (double)) + throw_class_format_error ("erroneous field initializer"); + + *(jdouble*)addr = _Jv_loadDouble (&pool->data[init]); + break; + + default: + throw_class_format_error ("erroneous field initializer"); + } +} + +inline static unsigned char* +skip_one_type (unsigned char* ptr) +{ + int ch = *ptr++; + + while (ch == '[') + { + ch = *ptr++; + } + + if (ch == 'L') + { + do { ch = *ptr++; } while (ch != ';'); + } + + return ptr; +} + +static ffi_type* +get_ffi_type_from_signature (unsigned char* ptr) +{ + switch (*ptr) + { + case 'L': + case '[': + return &ffi_type_pointer; + break; + + case 'Z': + // On some platforms a bool is a byte, on others an int. + if (sizeof (jboolean) == sizeof (jbyte)) + return &ffi_type_sint8; + else + { + JvAssert (sizeof (jbyte) == sizeof (jint)); + return &ffi_type_sint32; + } + break; + + case 'B': + return &ffi_type_sint8; + break; + + case 'C': + return &ffi_type_uint16; + break; + + case 'S': + return &ffi_type_sint16; + break; + + case 'I': + return &ffi_type_sint32; + break; + + case 'J': + return &ffi_type_sint64; + break; + + case 'F': + return &ffi_type_float; + break; + + case 'D': + return &ffi_type_double; + break; + + case 'V': + return &ffi_type_void; + break; + } + + throw_internal_error ("unknown type in signature"); +} + +/* this function yields the number of actual arguments, that is, if the + * function is non-static, then one is added to the number of elements + * found in the signature */ + +int +_Jv_count_arguments (_Jv_Utf8Const *signature, + jboolean staticp) +{ + unsigned char *ptr = (unsigned char*) signature->chars(); + int arg_count = staticp ? 0 : 1; + + /* first, count number of arguments */ + + // skip '(' + ptr++; + + // count args + while (*ptr != ')') + { + ptr = skip_one_type (ptr); + arg_count += 1; + } + + return arg_count; +} + +/* This beast will build a cif, given the signature. Memory for + * the cif itself and for the argument types must be allocated by the + * caller. + */ + +static int +init_cif (_Jv_Utf8Const* signature, + int arg_count, + jboolean staticp, + ffi_cif *cif, + ffi_type **arg_types, + ffi_type **rtype_p) +{ + unsigned char *ptr = (unsigned char*) signature->chars(); + + int arg_index = 0; // arg number + int item_count = 0; // stack-item count + + // setup receiver + if (!staticp) + { + arg_types[arg_index++] = &ffi_type_pointer; + item_count += 1; + } + + // skip '(' + ptr++; + + // assign arg types + while (*ptr != ')') + { + arg_types[arg_index++] = get_ffi_type_from_signature (ptr); + + if (*ptr == 'J' || *ptr == 'D') + item_count += 2; + else + item_count += 1; + + ptr = skip_one_type (ptr); + } + + // skip ')' + ptr++; + ffi_type *rtype = get_ffi_type_from_signature (ptr); + + ptr = skip_one_type (ptr); + if (ptr != (unsigned char*)signature->chars() + signature->len()) + throw_internal_error ("did not find end of signature"); + + if (ffi_prep_cif (cif, FFI_DEFAULT_ABI, + arg_count, rtype, arg_types) != FFI_OK) + throw_internal_error ("ffi_prep_cif failed"); + + if (rtype_p != NULL) + *rtype_p = rtype; + + return item_count; +} + +#if FFI_NATIVE_RAW_API +# define FFI_PREP_RAW_CLOSURE ffi_prep_raw_closure +# define FFI_RAW_SIZE ffi_raw_size +#else +# define FFI_PREP_RAW_CLOSURE ffi_prep_java_raw_closure +# define FFI_RAW_SIZE ffi_java_raw_size +#endif + +/* we put this one here, and not in interpret.cc because it + * calls the utility routines _Jv_count_arguments + * which are static to this module. The following struct defines the + * layout we use for the stubs, it's only used in the ncode method. */ + +typedef struct { + ffi_raw_closure closure; + ffi_cif cif; + ffi_type *arg_types[0]; +} ncode_closure; + +typedef void (*ffi_closure_fun) (ffi_cif*,void*,ffi_raw*,void*); + +void * +_Jv_InterpMethod::ncode () +{ + using namespace java::lang::reflect; + + if (self->ncode != 0) + return self->ncode; + + jboolean staticp = (self->accflags & Modifier::STATIC) != 0; + int arg_count = _Jv_count_arguments (self->signature, staticp); + + ncode_closure *closure = + (ncode_closure*)_Jv_AllocBytes (sizeof (ncode_closure) + + arg_count * sizeof (ffi_type*)); + + init_cif (self->signature, + arg_count, + staticp, + &closure->cif, + &closure->arg_types[0], + NULL); + + ffi_closure_fun fun; + + args_raw_size = FFI_RAW_SIZE (&closure->cif); + + JvAssert ((self->accflags & Modifier::NATIVE) == 0); + + if ((self->accflags & Modifier::SYNCHRONIZED) != 0) + { + if (staticp) + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class; + else + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object; + } + else + { + if (staticp) + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_class; + else + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal; + } + + FFI_PREP_RAW_CLOSURE (&closure->closure, + &closure->cif, + fun, + (void*)this); + + self->ncode = (void*)closure; + return self->ncode; +} + +void * +_Jv_JNIMethod::ncode () +{ + using namespace java::lang::reflect; + + if (self->ncode != 0) + return self->ncode; + + jboolean staticp = (self->accflags & Modifier::STATIC) != 0; + int arg_count = _Jv_count_arguments (self->signature, staticp); + + ncode_closure *closure = + (ncode_closure*)_Jv_AllocBytes (sizeof (ncode_closure) + + arg_count * sizeof (ffi_type*)); + + ffi_type *rtype; + init_cif (self->signature, + arg_count, + staticp, + &closure->cif, + &closure->arg_types[0], + &rtype); + + ffi_closure_fun fun; + + args_raw_size = FFI_RAW_SIZE (&closure->cif); + + // Initialize the argument types and CIF that represent the actual + // underlying JNI function. + int extra_args = 1; + if ((self->accflags & Modifier::STATIC)) + ++extra_args; + jni_arg_types = (ffi_type **) _Jv_AllocBytes ((extra_args + arg_count) + * sizeof (ffi_type *)); + int offset = 0; + jni_arg_types[offset++] = &ffi_type_pointer; + if ((self->accflags & Modifier::STATIC)) + jni_arg_types[offset++] = &ffi_type_pointer; + memcpy (&jni_arg_types[offset], &closure->arg_types[0], + arg_count * sizeof (ffi_type *)); + + if (ffi_prep_cif (&jni_cif, _Jv_platform_ffi_abi, + extra_args + arg_count, rtype, + jni_arg_types) != FFI_OK) + throw_internal_error ("ffi_prep_cif failed for JNI function"); + + JvAssert ((self->accflags & Modifier::NATIVE) != 0); + + // FIXME: for now we assume that all native methods for + // interpreted code use JNI. + fun = (ffi_closure_fun) &_Jv_JNIMethod::call; + + FFI_PREP_RAW_CLOSURE (&closure->closure, + &closure->cif, + fun, + (void*) this); + + self->ncode = (void *) closure; + return self->ncode; +} + +static void +throw_class_format_error (jstring msg) +{ + throw (msg + ? new java::lang::ClassFormatError (msg) + : new java::lang::ClassFormatError); +} + +static void +throw_class_format_error (char *msg) +{ + throw_class_format_error (JvNewStringLatin1 (msg)); +} + + + +void +_Jv_InterpreterEngine::do_verify (jclass klass) +{ + _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info; + for (int i = 0; i < klass->method_count; i++) + { + using namespace java::lang::reflect; + _Jv_MethodBase *imeth = iclass->interpreted_methods[i]; + _Jv_ushort accflags = klass->methods[i].accflags; + if ((accflags & (Modifier::NATIVE | Modifier::ABSTRACT)) == 0) + { + _Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (imeth); + _Jv_VerifyMethod (im); + } + } +} + +void +_Jv_InterpreterEngine::do_create_ncode (jclass klass) +{ + _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info; + for (int i = 0; i < klass->method_count; i++) + { + // Just skip abstract methods. This is particularly important + // because we don't resize the interpreted_methods array when + // miranda methods are added to it. + if ((klass->methods[i].accflags + & java::lang::reflect::Modifier::ABSTRACT) + != 0) + continue; + + _Jv_MethodBase *imeth = iclass->interpreted_methods[i]; + + if ((klass->methods[i].accflags & java::lang::reflect::Modifier::NATIVE) + != 0) + { + // You might think we could use a virtual `ncode' method in + // the _Jv_MethodBase and unify the native and non-native + // cases. Well, we can't, because we don't allocate these + // objects using `new', and thus they don't get a vtable. + _Jv_JNIMethod *jnim = reinterpret_cast<_Jv_JNIMethod *> (imeth); + klass->methods[i].ncode = jnim->ncode (); + } + else if (imeth != 0) // it could be abstract + { + _Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (imeth); + klass->methods[i].ncode = im->ncode (); + } + } +} + +void +_Jv_InterpreterEngine::do_allocate_static_fields (jclass klass, + int static_size) +{ + _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info; + + char *static_data = (char *) _Jv_AllocBytes (static_size); + memset (static_data, 0, static_size); + + for (int i = 0; i < klass->field_count; i++) + { + _Jv_Field *field = &klass->fields[i]; + + if ((field->flags & java::lang::reflect::Modifier::STATIC) != 0) + { + field->u.addr = static_data + field->u.boffset; + + if (iclass->field_initializers[i] != 0) + { + _Jv_Linker::resolve_field (field, klass->loader); + _Jv_InitField (0, klass, i); + } + } + } + + // Now we don't need the field_initializers anymore, so let the + // collector get rid of it. + iclass->field_initializers = 0; +} + +_Jv_ResolvedMethod * +_Jv_InterpreterEngine::do_resolve_method (_Jv_Method *method, jclass klass, + jboolean staticp, jint vtable_index) +{ + int arg_count = _Jv_count_arguments (method->signature, staticp); + + _Jv_ResolvedMethod* result = (_Jv_ResolvedMethod*) + _Jv_AllocBytes (sizeof (_Jv_ResolvedMethod) + + arg_count*sizeof (ffi_type*)); + + result->stack_item_count + = init_cif (method->signature, + arg_count, + staticp, + &result->cif, + &result->arg_types[0], + NULL); + + result->vtable_index = vtable_index; + result->method = method; + result->klass = klass; + + return result; +} + +void +_Jv_InterpreterEngine::do_post_miranda_hook (jclass klass) +{ + _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info; + for (int i = 0; i < klass->method_count; i++) + { + // Just skip abstract methods. This is particularly important + // because we don't resize the interpreted_methods array when + // miranda methods are added to it. + if ((klass->methods[i].accflags + & java::lang::reflect::Modifier::ABSTRACT) + != 0) + continue; + // Miranda method additions mean that the `methods' array moves. + // We cache a pointer into this array, so we have to update. + iclass->interpreted_methods[i]->self = &klass->methods[i]; + } +} + #endif // INTERPRETER |