| // interpret.cc - Code for the interpreter |
| |
| /* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation |
| |
| This file is part of libgcj. |
| |
| This software is copyrighted work licensed under the terms of the |
| Libgcj License. Please consult the file "LIBGCJ_LICENSE" for |
| details. */ |
| |
| /* Author: Kresten Krab Thorup <krab@gnu.org> */ |
| |
| #include <config.h> |
| #include <platform.h> |
| |
| #pragma implementation "java-interp.h" |
| |
| #include <jvm.h> |
| #include <java-cpool.h> |
| #include <java-interp.h> |
| #include <java/lang/System.h> |
| #include <java/lang/String.h> |
| #include <java/lang/Integer.h> |
| #include <java/lang/Long.h> |
| #include <java/lang/StringBuffer.h> |
| #include <java/lang/Class.h> |
| #include <java/lang/reflect/Modifier.h> |
| #include <java/lang/InternalError.h> |
| #include <java/lang/NullPointerException.h> |
| #include <java/lang/ArithmeticException.h> |
| #include <java/lang/IncompatibleClassChangeError.h> |
| #include <java/lang/InstantiationException.h> |
| #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> |
| |
| #include <jvmti.h> |
| #include "jvmti-int.h" |
| |
| #include <gnu/gcj/jvmti/Breakpoint.h> |
| #include <gnu/gcj/jvmti/BreakpointManager.h> |
| |
| // Execution engine for interpreted code. |
| _Jv_InterpreterEngine _Jv_soleInterpreterEngine; |
| |
| #include <stdlib.h> |
| |
| using namespace gcj; |
| |
| static void throw_internal_error (const char *msg) |
| __attribute__ ((__noreturn__)); |
| static void throw_incompatible_class_change_error (jstring msg) |
| __attribute__ ((__noreturn__)); |
| static void throw_null_pointer_exception () |
| __attribute__ ((__noreturn__)); |
| |
| static void throw_class_format_error (jstring msg) |
| __attribute__ ((__noreturn__)); |
| static void throw_class_format_error (const char *msg) |
| __attribute__ ((__noreturn__)); |
| |
| static void find_catch_location (jthrowable, jthread, jmethodID *, jlong *); |
| |
| // A macro to facilitate JVMTI exception reporting |
| #define REPORT_EXCEPTION(Jthrowable) \ |
| do { \ |
| if (JVMTI_REQUESTED_EVENT (Exception)) \ |
| _Jv_ReportJVMTIExceptionThrow (Jthrowable); \ |
| } \ |
| while (0) |
| |
| #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 |
| // the Class monitor as user code in another thread could hold it. |
| static _Jv_Mutex_t compile_mutex; |
| |
| // See class ThreadCountAdjuster and REWRITE_INSN for how this is |
| // used. |
| _Jv_Mutex_t _Jv_InterpMethod::rewrite_insn_mutex; |
| |
| void |
| _Jv_InitInterpreter() |
| { |
| _Jv_MutexInit (&compile_mutex); |
| _Jv_MutexInit (&_Jv_InterpMethod::rewrite_insn_mutex); |
| } |
| #else |
| void _Jv_InitInterpreter() {} |
| #endif |
| |
| // The breakpoint instruction. For the direct threaded case, |
| // _Jv_InterpMethod::compile will initialize breakpoint_insn |
| // the first time it is called. |
| #ifdef DIRECT_THREADED |
| insn_slot _Jv_InterpMethod::bp_insn_slot; |
| pc_t _Jv_InterpMethod::breakpoint_insn = NULL; |
| #else |
| unsigned char _Jv_InterpMethod::bp_insn_opcode |
| = static_cast<unsigned char> (op_breakpoint); |
| pc_t _Jv_InterpMethod::breakpoint_insn = &_Jv_InterpMethod::bp_insn_opcode; |
| #endif |
| |
| extern "C" double __ieee754_fmod (double,double); |
| |
| static inline void dupx (_Jv_word *sp, int n, int x) |
| { |
| // first "slide" n+x elements n to the right |
| int top = n-1; |
| for (int i = 0; i < n+x; i++) |
| { |
| sp[(top-i)] = sp[(top-i)-n]; |
| } |
| |
| // next, copy the n top elements, n+x down |
| for (int i = 0; i < n; i++) |
| { |
| sp[top-(n+x)-i] = sp[top-i]; |
| } |
| } |
| |
| // Used to convert from floating types to integral types. |
| template<typename TO, typename FROM> |
| static inline TO |
| convert (FROM val, TO min, TO max) |
| { |
| TO ret; |
| if (val >= (FROM) max) |
| ret = max; |
| else if (val <= (FROM) min) |
| ret = min; |
| else if (val != val) |
| ret = 0; |
| else |
| ret = (TO) val; |
| return ret; |
| } |
| |
| #define PUSHA(V) (sp++)->o = (V) |
| #define PUSHI(V) (sp++)->i = (V) |
| #define PUSHF(V) (sp++)->f = (V) |
| #if SIZEOF_VOID_P == 8 |
| # define PUSHL(V) (sp->l = (V), sp += 2) |
| # define PUSHD(V) (sp->d = (V), sp += 2) |
| #else |
| # define PUSHL(V) do { _Jv_word2 w2; w2.l=(V); \ |
| (sp++)->ia[0] = w2.ia[0]; \ |
| (sp++)->ia[0] = w2.ia[1]; } while (0) |
| # define PUSHD(V) do { _Jv_word2 w2; w2.d=(V); \ |
| (sp++)->ia[0] = w2.ia[0]; \ |
| (sp++)->ia[0] = w2.ia[1]; } while (0) |
| #endif |
| |
| #define POPA() ((--sp)->o) |
| #define POPI() ((jint) (--sp)->i) // cast since it may be promoted |
| #define POPF() ((jfloat) (--sp)->f) |
| #if SIZEOF_VOID_P == 8 |
| # define POPL() (sp -= 2, (jlong) sp->l) |
| # define POPD() (sp -= 2, (jdouble) sp->d) |
| #else |
| # define POPL() ({ _Jv_word2 w2; \ |
| w2.ia[1] = (--sp)->ia[0]; \ |
| w2.ia[0] = (--sp)->ia[0]; w2.l; }) |
| # define POPD() ({ _Jv_word2 w2; \ |
| w2.ia[1] = (--sp)->ia[0]; \ |
| w2.ia[0] = (--sp)->ia[0]; w2.d; }) |
| #endif |
| |
| #define LOADA(I) (sp++)->o = locals[I].o |
| #define LOADI(I) (sp++)->i = locals[I].i |
| #define LOADF(I) (sp++)->f = locals[I].f |
| #if SIZEOF_VOID_P == 8 |
| # define LOADL(I) (sp->l = locals[I].l, sp += 2) |
| # define LOADD(I) (sp->d = locals[I].d, sp += 2) |
| #else |
| # define LOADL(I) do { jint __idx = (I); \ |
| (sp++)->ia[0] = locals[__idx].ia[0]; \ |
| (sp++)->ia[0] = locals[__idx+1].ia[0]; \ |
| } while (0) |
| # define LOADD(I) LOADL(I) |
| #endif |
| |
| #define STOREA(I) \ |
| do \ |
| { \ |
| jint __idx = (I); \ |
| DEBUG_LOCALS_INSN (__idx, 'o'); \ |
| locals[__idx].o = (--sp)->o; \ |
| } \ |
| while (0) |
| #define STOREI(I) \ |
| do \ |
| { \ |
| jint __idx = (I); \ |
| DEBUG_LOCALS_INSN (__idx, 'i'); \ |
| locals[__idx].i = (--sp)->i; \ |
| } while (0) |
| #define STOREF(I) \ |
| do \ |
| { \ |
| jint __idx = (I); \ |
| DEBUG_LOCALS_INSN (__idx, 'f'); \ |
| locals[__idx].f = (--sp)->f; \ |
| } \ |
| while (0) |
| #if SIZEOF_VOID_P == 8 |
| # define STOREL(I) \ |
| do \ |
| { \ |
| jint __idx = (I); \ |
| DEBUG_LOCALS_INSN (__idx, 'l'); \ |
| DEBUG_LOCALS_INSN (__idx + 1, 'x'); \ |
| (sp -= 2, locals[__idx].l = sp->l); \ |
| } \ |
| while (0) |
| # define STORED(I) \ |
| do \ |
| { \ |
| jint __idx = (I); \ |
| DEBUG_LOCALS_INSN (__idx, 'd'); \ |
| DEBUG_LOCALS_INSN (__idx + 1, 'x'); \ |
| (sp -= 2, locals[__idx].d = sp->d); \ |
| } \ |
| while (0) |
| |
| #else |
| # define STOREL(I) \ |
| do \ |
| { \ |
| jint __idx = (I); \ |
| DEBUG_LOCALS_INSN (__idx, 'l'); \ |
| DEBUG_LOCALS_INSN (__idx + 1, 'x'); \ |
| locals[__idx + 1].ia[0] = (--sp)->ia[0]; \ |
| locals[__idx].ia[0] = (--sp)->ia[0]; \ |
| } \ |
| while (0) |
| # define STORED(I) \ |
| do { \ |
| jint __idx = (I); \ |
| DEBUG_LOCALS_INSN (__idx, 'd'); \ |
| DEBUG_LOCALS_INSN (__idx + 1, 'x'); \ |
| locals[__idx + 1].ia[0] = (--sp)->ia[0]; \ |
| locals[__idx].ia[0] = (--sp)->ia[0]; \ |
| } while (0) |
| #endif |
| |
| #define PEEKI(I) (locals+(I))->i |
| #define PEEKA(I) (locals+(I))->o |
| |
| #define POKEI(I,V) \ |
| do \ |
| { \ |
| jint __idx = (I); \ |
| DEBUG_LOCALS_INSN (__idx, 'i'); \ |
| ((locals + __idx)->i = (V)); \ |
| } \ |
| while (0) |
| |
| |
| #define BINOPI(OP) { \ |
| jint value2 = POPI(); \ |
| jint value1 = POPI(); \ |
| PUSHI(value1 OP value2); \ |
| } |
| |
| #define BINOPF(OP) { \ |
| jfloat value2 = POPF(); \ |
| jfloat value1 = POPF(); \ |
| PUSHF(value1 OP value2); \ |
| } |
| |
| #define BINOPL(OP) { \ |
| jlong value2 = POPL(); \ |
| jlong value1 = POPL(); \ |
| PUSHL(value1 OP value2); \ |
| } |
| |
| #define BINOPD(OP) { \ |
| jdouble value2 = POPD(); \ |
| jdouble value1 = POPD(); \ |
| PUSHD(value1 OP value2); \ |
| } |
| |
| static inline jint |
| get1s (unsigned char* loc) |
| { |
| return *(signed char*)loc; |
| } |
| |
| static inline jint |
| get1u (unsigned char* loc) |
| { |
| return *loc; |
| } |
| |
| static inline jint |
| get2s(unsigned char* loc) |
| { |
| return (((jint)*(signed char*)loc) << 8) | ((jint)*(loc+1)); |
| } |
| |
| static inline jint |
| get2u (unsigned char* loc) |
| { |
| return (((jint)(*loc)) << 8) | ((jint)*(loc+1)); |
| } |
| |
| static jint |
| get4 (unsigned char* loc) |
| { |
| return (((jint)(loc[0])) << 24) |
| | (((jint)(loc[1])) << 16) |
| | (((jint)(loc[2])) << 8) |
| | (((jint)(loc[3])) << 0); |
| } |
| |
| #define SAVE_PC() frame_desc.pc = pc |
| |
| // We used to define this conditionally, depending on HANDLE_SEGV. |
| // However, that runs into a problem if a chunk in low memory is |
| // mapped and we try to look at a field near the end of a large |
| // object. See PR 26858 for details. It is, most likely, relatively |
| // inexpensive to simply do this check always. |
| #define NULLCHECK(X) \ |
| do { SAVE_PC(); if ((X)==NULL) throw_null_pointer_exception (); } while (0) |
| |
| // Note that we can still conditionally define NULLARRAYCHECK, since |
| // we know that all uses of an array will first reference the length |
| // field, which is first -- and thus will trigger a SEGV. |
| #ifdef HANDLE_SEGV |
| #define NULLARRAYCHECK(X) SAVE_PC() |
| #else |
| #define NULLARRAYCHECK(X) \ |
| do \ |
| { \ |
| SAVE_PC(); \ |
| if ((X) == NULL) { throw_null_pointer_exception (); } \ |
| } while (0) |
| #endif |
| |
| #define ARRAYBOUNDSCHECK(array, index) \ |
| do \ |
| { \ |
| if (((unsigned) index) >= (unsigned) (array->length)) \ |
| _Jv_ThrowBadArrayIndex (index); \ |
| } while (0) |
| |
| void |
| _Jv_InterpMethod::run_normal (ffi_cif *, |
| void *ret, |
| INTERP_FFI_RAW_TYPE *args, |
| void *__this) |
| { |
| _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this; |
| run (ret, args, _this); |
| } |
| |
| void |
| _Jv_InterpMethod::run_normal_debug (ffi_cif *, |
| void *ret, |
| INTERP_FFI_RAW_TYPE *args, |
| void *__this) |
| { |
| _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this; |
| run_debug (ret, args, _this); |
| } |
| |
| void |
| _Jv_InterpMethod::run_synch_object (ffi_cif *, |
| void *ret, |
| INTERP_FFI_RAW_TYPE *args, |
| void *__this) |
| { |
| _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this; |
| |
| jobject rcv = (jobject) args[0].ptr; |
| JvSynchronize mutex (rcv); |
| |
| run (ret, args, _this); |
| } |
| |
| void |
| _Jv_InterpMethod::run_synch_object_debug (ffi_cif *, |
| void *ret, |
| INTERP_FFI_RAW_TYPE *args, |
| void *__this) |
| { |
| _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this; |
| |
| jobject rcv = (jobject) args[0].ptr; |
| JvSynchronize mutex (rcv); |
| |
| run_debug (ret, args, _this); |
| } |
| |
| void |
| _Jv_InterpMethod::run_class (ffi_cif *, |
| void *ret, |
| INTERP_FFI_RAW_TYPE *args, |
| void *__this) |
| { |
| _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this; |
| _Jv_InitClass (_this->defining_class); |
| run (ret, args, _this); |
| } |
| |
| void |
| _Jv_InterpMethod::run_class_debug (ffi_cif *, |
| void *ret, |
| INTERP_FFI_RAW_TYPE *args, |
| void *__this) |
| { |
| _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this; |
| _Jv_InitClass (_this->defining_class); |
| run_debug (ret, args, _this); |
| } |
| |
| void |
| _Jv_InterpMethod::run_synch_class (ffi_cif *, |
| void *ret, |
| INTERP_FFI_RAW_TYPE *args, |
| void *__this) |
| { |
| _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this; |
| |
| jclass sync = _this->defining_class; |
| _Jv_InitClass (sync); |
| JvSynchronize mutex (sync); |
| |
| run (ret, args, _this); |
| } |
| |
| void |
| _Jv_InterpMethod::run_synch_class_debug (ffi_cif *, |
| void *ret, |
| INTERP_FFI_RAW_TYPE *args, |
| void *__this) |
| { |
| _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this; |
| |
| jclass sync = _this->defining_class; |
| _Jv_InitClass (sync); |
| JvSynchronize mutex (sync); |
| |
| run_debug (ret, args, _this); |
| } |
| |
| #ifdef DIRECT_THREADED |
| // "Compile" a method by turning it from bytecode to direct-threaded |
| // code. |
| void |
| _Jv_InterpMethod::compile (const void * const *insn_targets) |
| { |
| insn_slot *insns = NULL; |
| int next = 0; |
| unsigned char *codestart = bytecode (); |
| unsigned char *end = codestart + code_length; |
| _Jv_word *pool_data = defining_class->constants.data; |
| |
| #define SET_ONE(Field, Value) \ |
| do \ |
| { \ |
| if (first_pass) \ |
| ++next; \ |
| else \ |
| insns[next++].Field = Value; \ |
| } \ |
| while (0) |
| |
| #define SET_INSN(Value) SET_ONE (insn, (void *) Value) |
| #define SET_INT(Value) SET_ONE (int_val, Value) |
| #define SET_DATUM(Value) SET_ONE (datum, Value) |
| |
| // Map from bytecode PC to slot in INSNS. |
| int *pc_mapping = (int *) __builtin_alloca (sizeof (int) * code_length); |
| for (int i = 0; i < code_length; ++i) |
| pc_mapping[i] = -1; |
| |
| for (int i = 0; i < 2; ++i) |
| { |
| jboolean first_pass = i == 0; |
| |
| if (! first_pass) |
| { |
| insns = (insn_slot *) _Jv_AllocBytes (sizeof (insn_slot) * next); |
| number_insn_slots = next; |
| next = 0; |
| } |
| |
| unsigned char *pc = codestart; |
| while (pc < end) |
| { |
| int base_pc_val = pc - codestart; |
| if (first_pass) |
| pc_mapping[base_pc_val] = next; |
| |
| java_opcode opcode = (java_opcode) *pc++; |
| // Just elide NOPs. |
| if (opcode == op_nop) |
| continue; |
| SET_INSN (insn_targets[opcode]); |
| |
| switch (opcode) |
| { |
| case op_nop: |
| case op_aconst_null: |
| case op_iconst_m1: |
| case op_iconst_0: |
| case op_iconst_1: |
| case op_iconst_2: |
| case op_iconst_3: |
| case op_iconst_4: |
| case op_iconst_5: |
| case op_lconst_0: |
| case op_lconst_1: |
| case op_fconst_0: |
| case op_fconst_1: |
| case op_fconst_2: |
| case op_dconst_0: |
| case op_dconst_1: |
| case op_iload_0: |
| case op_iload_1: |
| case op_iload_2: |
| case op_iload_3: |
| case op_lload_0: |
| case op_lload_1: |
| case op_lload_2: |
| case op_lload_3: |
| case op_fload_0: |
| case op_fload_1: |
| case op_fload_2: |
| case op_fload_3: |
| case op_dload_0: |
| case op_dload_1: |
| case op_dload_2: |
| case op_dload_3: |
| case op_aload_0: |
| case op_aload_1: |
| case op_aload_2: |
| case op_aload_3: |
| case op_iaload: |
| case op_laload: |
| case op_faload: |
| case op_daload: |
| case op_aaload: |
| case op_baload: |
| case op_caload: |
| case op_saload: |
| case op_istore_0: |
| case op_istore_1: |
| case op_istore_2: |
| case op_istore_3: |
| case op_lstore_0: |
| case op_lstore_1: |
| case op_lstore_2: |
| case op_lstore_3: |
| case op_fstore_0: |
| case op_fstore_1: |
| case op_fstore_2: |
| case op_fstore_3: |
| case op_dstore_0: |
| case op_dstore_1: |
| case op_dstore_2: |
| case op_dstore_3: |
| case op_astore_0: |
| case op_astore_1: |
| case op_astore_2: |
| case op_astore_3: |
| case op_iastore: |
| case op_lastore: |
| case op_fastore: |
| case op_dastore: |
| case op_aastore: |
| case op_bastore: |
| case op_castore: |
| case op_sastore: |
| case op_pop: |
| case op_pop2: |
| case op_dup: |
| case op_dup_x1: |
| case op_dup_x2: |
| case op_dup2: |
| case op_dup2_x1: |
| case op_dup2_x2: |
| case op_swap: |
| case op_iadd: |
| case op_isub: |
| case op_imul: |
| case op_idiv: |
| case op_irem: |
| case op_ishl: |
| case op_ishr: |
| case op_iushr: |
| case op_iand: |
| case op_ior: |
| case op_ixor: |
| case op_ladd: |
| case op_lsub: |
| case op_lmul: |
| case op_ldiv: |
| case op_lrem: |
| case op_lshl: |
| case op_lshr: |
| case op_lushr: |
| case op_land: |
| case op_lor: |
| case op_lxor: |
| case op_fadd: |
| case op_fsub: |
| case op_fmul: |
| case op_fdiv: |
| case op_frem: |
| case op_dadd: |
| case op_dsub: |
| case op_dmul: |
| case op_ddiv: |
| case op_drem: |
| case op_ineg: |
| case op_i2b: |
| case op_i2c: |
| case op_i2s: |
| case op_lneg: |
| case op_fneg: |
| case op_dneg: |
| case op_i2l: |
| case op_i2f: |
| case op_i2d: |
| case op_l2i: |
| case op_l2f: |
| case op_l2d: |
| case op_f2i: |
| case op_f2l: |
| case op_f2d: |
| case op_d2i: |
| case op_d2l: |
| case op_d2f: |
| case op_lcmp: |
| case op_fcmpl: |
| case op_fcmpg: |
| case op_dcmpl: |
| case op_dcmpg: |
| case op_monitorenter: |
| case op_monitorexit: |
| case op_ireturn: |
| case op_lreturn: |
| case op_freturn: |
| case op_dreturn: |
| case op_areturn: |
| case op_return: |
| case op_athrow: |
| case op_arraylength: |
| // No argument, nothing else to do. |
| break; |
| |
| case op_bipush: |
| SET_INT (get1s (pc)); |
| ++pc; |
| break; |
| |
| case op_ldc: |
| { |
| int index = get1u (pc); |
| ++pc; |
| // For an unresolved class we want to delay resolution |
| // until execution. |
| if (defining_class->constants.tags[index] == JV_CONSTANT_Class) |
| { |
| --next; |
| SET_INSN (insn_targets[int (op_jsr_w) + 1]); |
| SET_INT (index); |
| } |
| else |
| SET_DATUM (pool_data[index].o); |
| } |
| break; |
| |
| case op_ret: |
| case op_iload: |
| case op_lload: |
| case op_fload: |
| case op_dload: |
| case op_aload: |
| case op_istore: |
| case op_lstore: |
| case op_fstore: |
| case op_dstore: |
| case op_astore: |
| case op_newarray: |
| SET_INT (get1u (pc)); |
| ++pc; |
| break; |
| |
| case op_iinc: |
| SET_INT (get1u (pc)); |
| SET_INT (get1s (pc + 1)); |
| pc += 2; |
| break; |
| |
| case op_ldc_w: |
| { |
| int index = get2u (pc); |
| pc += 2; |
| // For an unresolved class we want to delay resolution |
| // until execution. |
| if (defining_class->constants.tags[index] == JV_CONSTANT_Class) |
| { |
| --next; |
| SET_INSN (insn_targets[int (op_jsr_w) + 1]); |
| SET_INT (index); |
| } |
| else |
| SET_DATUM (pool_data[index].o); |
| } |
| break; |
| |
| case op_ldc2_w: |
| { |
| int index = get2u (pc); |
| pc += 2; |
| SET_DATUM (&pool_data[index]); |
| } |
| break; |
| |
| case op_sipush: |
| SET_INT (get2s (pc)); |
| pc += 2; |
| break; |
| |
| case op_new: |
| case op_getstatic: |
| case op_getfield: |
| case op_putfield: |
| case op_putstatic: |
| case op_anewarray: |
| case op_instanceof: |
| case op_checkcast: |
| case op_invokespecial: |
| case op_invokestatic: |
| case op_invokevirtual: |
| SET_INT (get2u (pc)); |
| pc += 2; |
| break; |
| |
| case op_multianewarray: |
| SET_INT (get2u (pc)); |
| SET_INT (get1u (pc + 2)); |
| pc += 3; |
| break; |
| |
| case op_jsr: |
| case op_ifeq: |
| case op_ifne: |
| case op_iflt: |
| case op_ifge: |
| case op_ifgt: |
| case op_ifle: |
| case op_if_icmpeq: |
| case op_if_icmpne: |
| case op_if_icmplt: |
| case op_if_icmpge: |
| case op_if_icmpgt: |
| case op_if_icmple: |
| case op_if_acmpeq: |
| case op_if_acmpne: |
| case op_ifnull: |
| case op_ifnonnull: |
| case op_goto: |
| { |
| int offset = get2s (pc); |
| pc += 2; |
| |
| int new_pc = base_pc_val + offset; |
| |
| bool orig_was_goto = opcode == op_goto; |
| |
| // Thread jumps. We limit the loop count; this lets |
| // us avoid infinite loops if the bytecode contains |
| // such. `10' is arbitrary. |
| int count = 10; |
| while (codestart[new_pc] == op_goto && count-- > 0) |
| new_pc += get2s (&codestart[new_pc + 1]); |
| |
| // If the jump takes us to a `return' instruction and |
| // the original branch was an unconditional goto, then |
| // we hoist the return. |
| opcode = (java_opcode) codestart[new_pc]; |
| if (orig_was_goto |
| && (opcode == op_ireturn || opcode == op_lreturn |
| || opcode == op_freturn || opcode == op_dreturn |
| || opcode == op_areturn || opcode == op_return)) |
| { |
| --next; |
| SET_INSN (insn_targets[opcode]); |
| } |
| else |
| SET_DATUM (&insns[pc_mapping[new_pc]]); |
| } |
| break; |
| |
| case op_tableswitch: |
| { |
| while ((pc - codestart) % 4 != 0) |
| ++pc; |
| |
| jint def = get4 (pc); |
| SET_DATUM (&insns[pc_mapping[base_pc_val + def]]); |
| pc += 4; |
| |
| int low = get4 (pc); |
| SET_INT (low); |
| pc += 4; |
| int high = get4 (pc); |
| SET_INT (high); |
| pc += 4; |
| |
| for (int i = low; i <= high; ++i) |
| { |
| SET_DATUM (&insns[pc_mapping[base_pc_val + get4 (pc)]]); |
| pc += 4; |
| } |
| } |
| break; |
| |
| case op_lookupswitch: |
| { |
| while ((pc - codestart) % 4 != 0) |
| ++pc; |
| |
| jint def = get4 (pc); |
| SET_DATUM (&insns[pc_mapping[base_pc_val + def]]); |
| pc += 4; |
| |
| jint npairs = get4 (pc); |
| pc += 4; |
| SET_INT (npairs); |
| |
| while (npairs-- > 0) |
| { |
| jint match = get4 (pc); |
| jint offset = get4 (pc + 4); |
| SET_INT (match); |
| SET_DATUM (&insns[pc_mapping[base_pc_val + offset]]); |
| pc += 8; |
| } |
| } |
| break; |
| |
| case op_invokeinterface: |
| { |
| jint index = get2u (pc); |
| pc += 2; |
| // We ignore the next two bytes. |
| pc += 2; |
| SET_INT (index); |
| } |
| break; |
| |
| case op_wide: |
| { |
| opcode = (java_opcode) get1u (pc); |
| pc += 1; |
| jint val = get2u (pc); |
| pc += 2; |
| |
| // We implement narrow and wide instructions using the |
| // same code in the interpreter. So we rewrite the |
| // instruction slot here. |
| if (! first_pass) |
| insns[next - 1].insn = (void *) insn_targets[opcode]; |
| SET_INT (val); |
| |
| if (opcode == op_iinc) |
| { |
| SET_INT (get2s (pc)); |
| pc += 2; |
| } |
| } |
| break; |
| |
| case op_jsr_w: |
| case op_goto_w: |
| { |
| jint offset = get4 (pc); |
| pc += 4; |
| SET_DATUM (&insns[pc_mapping[base_pc_val + offset]]); |
| } |
| break; |
| |
| // Some "can't happen" cases that we include for |
| // error-checking purposes. |
| case op_putfield_1: |
| case op_putfield_2: |
| case op_putfield_4: |
| case op_putfield_8: |
| case op_putfield_a: |
| case op_putstatic_1: |
| case op_putstatic_2: |
| case op_putstatic_4: |
| case op_putstatic_8: |
| case op_putstatic_a: |
| case op_getfield_1: |
| case op_getfield_2s: |
| case op_getfield_2u: |
| case op_getfield_4: |
| case op_getfield_8: |
| case op_getfield_a: |
| case op_getstatic_1: |
| case op_getstatic_2s: |
| case op_getstatic_2u: |
| case op_getstatic_4: |
| case op_getstatic_8: |
| case op_getstatic_a: |
| case op_breakpoint: |
| default: |
| // Fail somehow. |
| break; |
| } |
| } |
| } |
| |
| // Now update exceptions. |
| _Jv_InterpException *exc = exceptions (); |
| for (int i = 0; i < exc_count; ++i) |
| { |
| 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]]; |
| // FIXME: resolve_pool_entry can throw - we shouldn't be doing this |
| // during compilation. |
| jclass handler |
| = (_Jv_Linker::resolve_pool_entry (defining_class, |
| exc[i].handler_type.i)).clazz; |
| exc[i].handler_type.p = handler; |
| } |
| |
| // Translate entries in the LineNumberTable from bytecode PC's to direct |
| // threaded interpreter instruction values. |
| for (int i = 0; i < line_table_len; i++) |
| { |
| int byte_pc = line_table[i].bytecode_pc; |
| // It isn't worth throwing an exception if this table is |
| // corrupted, but at the same time we don't want a crash. |
| if (byte_pc < 0 || byte_pc >= code_length) |
| byte_pc = 0; |
| line_table[i].pc = &insns[pc_mapping[byte_pc]]; |
| } |
| |
| prepared = insns; |
| |
| // Now remap the variable table for this method. |
| for (int i = 0; i < local_var_table_len; ++i) |
| { |
| int start_byte = local_var_table[i].bytecode_pc; |
| if (start_byte < 0 || start_byte >= code_length) |
| start_byte = 0; |
| jlocation start = pc_mapping[start_byte]; |
| |
| int end_byte = start_byte + local_var_table[i].length; |
| if (end_byte < 0) |
| end_byte = 0; |
| jlocation end = ((end_byte >= code_length) |
| ? number_insn_slots |
| : pc_mapping[end_byte]); |
| |
| local_var_table[i].pc = &insns[start]; |
| local_var_table[i].length = end - start + 1; |
| } |
| |
| if (breakpoint_insn == NULL) |
| { |
| bp_insn_slot.insn = const_cast<void *> (insn_targets[op_breakpoint]); |
| breakpoint_insn = &bp_insn_slot; |
| } |
| } |
| #endif /* DIRECT_THREADED */ |
| |
| /* Run the given method. |
| When args is NULL, don't run anything -- just compile it. */ |
| void |
| _Jv_InterpMethod::run (void *retp, INTERP_FFI_RAW_TYPE *args, |
| _Jv_InterpMethod *meth) |
| { |
| #undef __GCJ_DEBUG |
| #undef DEBUG_LOCALS_INSN |
| #define DEBUG_LOCALS_INSN(s, t) do {} while (0) |
| |
| #include "interpret-run.cc" |
| } |
| |
| void |
| _Jv_InterpMethod::run_debug (void *retp, INTERP_FFI_RAW_TYPE *args, |
| _Jv_InterpMethod *meth) |
| { |
| #define __GCJ_DEBUG |
| #undef DEBUG_LOCALS_INSN |
| #define DEBUG_LOCALS_INSN(s, t) \ |
| do \ |
| { \ |
| frame_desc.locals_type[s] = t; \ |
| } \ |
| while (0) |
| |
| #include "interpret-run.cc" |
| } |
| |
| static void |
| throw_internal_error (const char *msg) |
| { |
| jthrowable t = new java::lang::InternalError (JvNewStringLatin1 (msg)); |
| REPORT_EXCEPTION (t); |
| throw t; |
| } |
| |
| static void |
| throw_incompatible_class_change_error (jstring msg) |
| { |
| jthrowable t = new java::lang::IncompatibleClassChangeError (msg); |
| REPORT_EXCEPTION (t); |
| throw t; |
| } |
| |
| static void |
| throw_null_pointer_exception () |
| { |
| jthrowable t = new java::lang::NullPointerException; |
| REPORT_EXCEPTION (t); |
| throw t; |
| } |
| |
| /* Look up source code line number for given bytecode (or direct threaded |
| interpreter) PC. */ |
| int |
| _Jv_InterpMethod::get_source_line(pc_t mpc) |
| { |
| int line = line_table_len > 0 ? line_table[0].line : -1; |
| for (int i = 1; i < line_table_len; i++) |
| if (line_table[i].pc > mpc) |
| break; |
| else |
| line = line_table[i].line; |
| |
| return line; |
| } |
| |
| /** 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. |
| */ |
| |
| int |
| _Jv_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; |
| } |
| |
| /* 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. */ |
| |
| #if FFI_NATIVE_RAW_API |
| # define FFI_PREP_RAW_CLOSURE ffi_prep_raw_closure_loc |
| # define FFI_RAW_SIZE ffi_raw_size |
| typedef struct { |
| ffi_raw_closure closure; |
| _Jv_ClosureList list; |
| ffi_cif cif; |
| ffi_type *arg_types[0]; |
| } ncode_closure; |
| typedef void (*ffi_closure_fun) (ffi_cif*,void*,INTERP_FFI_RAW_TYPE*,void*); |
| #else |
| # define FFI_PREP_RAW_CLOSURE ffi_prep_java_raw_closure_loc |
| # define FFI_RAW_SIZE ffi_java_raw_size |
| typedef struct { |
| ffi_java_raw_closure closure; |
| _Jv_ClosureList list; |
| ffi_cif cif; |
| ffi_type *arg_types[0]; |
| } ncode_closure; |
| typedef void (*ffi_closure_fun) (ffi_cif*,void*,ffi_java_raw*,void*); |
| #endif |
| |
| void * |
| _Jv_InterpMethod::ncode (jclass klass) |
| { |
| 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); |
| |
| void *code; |
| ncode_closure *closure = |
| (ncode_closure*)ffi_closure_alloc (sizeof (ncode_closure) |
| + arg_count * sizeof (ffi_type*), |
| &code); |
| closure->list.registerClosure (klass, closure); |
| |
| _Jv_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) |
| { |
| if (JVMTI::enabled) |
| fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class_debug; |
| else |
| fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class; |
| } |
| else |
| { |
| if (JVMTI::enabled) |
| fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object_debug; |
| else |
| fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object; |
| } |
| } |
| else |
| { |
| if (staticp) |
| { |
| if (JVMTI::enabled) |
| fun = (ffi_closure_fun)&_Jv_InterpMethod::run_class_debug; |
| else |
| fun = (ffi_closure_fun)&_Jv_InterpMethod::run_class; |
| } |
| else |
| { |
| if (JVMTI::enabled) |
| fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal_debug; |
| else |
| fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal; |
| } |
| } |
| |
| FFI_PREP_RAW_CLOSURE (&closure->closure, |
| &closure->cif, |
| fun, |
| (void*)this, |
| code); |
| |
| self->ncode = code; |
| |
| return self->ncode; |
| } |
| |
| /* Find the index of the given insn in the array of insn slots |
| for this method. Returns -1 if not found. */ |
| jlong |
| _Jv_InterpMethod::insn_index (pc_t pc) |
| { |
| jlong left = 0; |
| #ifdef DIRECT_THREADED |
| jlong right = number_insn_slots; |
| pc_t insns = prepared; |
| #else |
| jlong right = code_length; |
| pc_t insns = bytecode (); |
| #endif |
| |
| while (right >= 0) |
| { |
| jlong mid = (left + right) / 2; |
| if (&insns[mid] == pc) |
| return mid; |
| |
| if (pc < &insns[mid]) |
| right = mid - 1; |
| else |
| left = mid + 1; |
| } |
| |
| return -1; |
| } |
| |
| // Method to check if an exception is caught at some location in a method |
| // (meth). Returns true if this method (meth) contains a catch block for the |
| // exception (ex). False otherwise. If there is a catch block, it sets the pc |
| // to the location of the beginning of the catch block. |
| jboolean |
| _Jv_InterpMethod::check_handler (pc_t *pc, _Jv_InterpMethod *meth, |
| java::lang::Throwable *ex) |
| { |
| #ifdef DIRECT_THREADED |
| void *logical_pc = (void *) ((insn_slot *) (*pc) - 1); |
| #else |
| int logical_pc = (*pc) - 1 - meth->bytecode (); |
| #endif |
| _Jv_InterpException *exc = meth->exceptions (); |
| jclass exc_class = ex->getClass (); |
| |
| for (int i = 0; i < meth->exc_count; i++) |
| { |
| if (PCVAL (exc[i].start_pc) <= logical_pc |
| && logical_pc < PCVAL (exc[i].end_pc)) |
| { |
| #ifdef DIRECT_THREADED |
| jclass handler = (jclass) exc[i].handler_type.p; |
| #else |
| jclass handler = NULL; |
| if (exc[i].handler_type.i != 0) |
| handler |
| = (_Jv_Linker::resolve_pool_entry (meth->defining_class, |
| ex$ |
| #endif /* DIRECT_THREADED */ |
| if (handler == NULL || handler->isAssignableFrom (exc_class)) |
| { |
| #ifdef DIRECT_THREADED |
| (*pc) = (insn_slot *) exc[i].handler_pc.p; |
| #else |
| (*pc) = meth->bytecode () + exc[i].handler_pc.i; |
| #endif /* DIRECT_THREADED */ |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| void |
| _Jv_InterpMethod::get_line_table (jlong& start, jlong& end, |
| jintArray& line_numbers, |
| jlongArray& code_indices) |
| { |
| #ifdef DIRECT_THREADED |
| /* For the DIRECT_THREADED case, if the method has not yet been |
| * compiled, the linetable will change to insn slots instead of |
| * bytecode PCs. It is probably easiest, in this case, to simply |
| * compile the method and guarantee that we are using insn |
| * slots. |
| */ |
| _Jv_CompileMethod (this); |
| |
| if (line_table_len > 0) |
| { |
| start = 0; |
| end = number_insn_slots; |
| line_numbers = JvNewIntArray (line_table_len); |
| code_indices = JvNewLongArray (line_table_len); |
| |
| jint* lines = elements (line_numbers); |
| jlong* indices = elements (code_indices); |
| for (int i = 0; i < line_table_len; ++i) |
| { |
| lines[i] = line_table[i].line; |
| indices[i] = insn_index (line_table[i].pc); |
| } |
| } |
| #else // !DIRECT_THREADED |
| if (line_table_len > 0) |
| { |
| start = 0; |
| end = code_length; |
| line_numbers = JvNewIntArray (line_table_len); |
| code_indices = JvNewLongArray (line_table_len); |
| |
| jint* lines = elements (line_numbers); |
| jlong* indices = elements (code_indices); |
| for (int i = 0; i < line_table_len; ++i) |
| { |
| lines[i] = line_table[i].line; |
| indices[i] = (jlong) line_table[i].bytecode_pc; |
| } |
| } |
| #endif // !DIRECT_THREADED |
| } |
| |
| int |
| _Jv_InterpMethod::get_local_var_table (char **name, char **sig, |
| char **generic_sig, jlong *startloc, |
| jint *length, jint *slot, |
| int table_slot) |
| { |
| #ifdef DIRECT_THREADED |
| _Jv_CompileMethod (this); |
| #endif |
| |
| if (local_var_table == NULL) |
| return -2; |
| if (table_slot >= local_var_table_len) |
| return -1; |
| else |
| { |
| *name = local_var_table[table_slot].name; |
| *sig = local_var_table[table_slot].descriptor; |
| *generic_sig = local_var_table[table_slot].descriptor; |
| |
| #ifdef DIRECT_THREADED |
| *startloc = insn_index (local_var_table[table_slot].pc); |
| #else |
| *startloc = static_cast<jlong> (local_var_table[table_slot].bytecode_pc); |
| #endif |
| *length = static_cast<jint> (local_var_table[table_slot].length); |
| *slot = static_cast<jint> (local_var_table[table_slot].slot); |
| } |
| return local_var_table_len - table_slot - 1; |
| } |
| |
| pc_t |
| _Jv_InterpMethod::install_break (jlong index) |
| { |
| return set_insn (index, breakpoint_insn); |
| } |
| |
| pc_t |
| _Jv_InterpMethod::get_insn (jlong index) |
| { |
| pc_t code; |
| |
| #ifdef DIRECT_THREADED |
| if (index >= number_insn_slots || index < 0) |
| return NULL; |
| |
| code = prepared; |
| #else // !DIRECT_THREADED |
| if (index >= code_length || index < 0) |
| return NULL; |
| |
| code = reinterpret_cast<pc_t> (bytecode ()); |
| #endif // !DIRECT_THREADED |
| |
| return &code[index]; |
| } |
| |
| pc_t |
| _Jv_InterpMethod::set_insn (jlong index, pc_t insn) |
| { |
| #ifdef DIRECT_THREADED |
| if (index >= number_insn_slots || index < 0) |
| return NULL; |
| |
| pc_t code = prepared; |
| code[index].insn = insn->insn; |
| #else // !DIRECT_THREADED |
| if (index >= code_length || index < 0) |
| return NULL; |
| |
| pc_t code = reinterpret_cast<pc_t> (bytecode ()); |
| code[index] = *insn; |
| #endif // !DIRECT_THREADED |
| |
| return &code[index]; |
| } |
| |
| bool |
| _Jv_InterpMethod::breakpoint_at (jlong index) |
| { |
| pc_t insn = get_insn (index); |
| if (insn != NULL) |
| { |
| #ifdef DIRECT_THREADED |
| return (insn->insn == breakpoint_insn->insn); |
| #else |
| pc_t code = reinterpret_cast<pc_t> (bytecode ()); |
| return (code[index] == breakpoint_insn); |
| #endif |
| } |
| |
| return false; |
| } |
| |
| void * |
| _Jv_JNIMethod::ncode (jclass klass) |
| { |
| 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); |
| |
| void *code; |
| ncode_closure *closure = |
| (ncode_closure*)ffi_closure_alloc (sizeof (ncode_closure) |
| + arg_count * sizeof (ffi_type*), |
| &code); |
| closure->list.registerClosure (klass, closure); |
| |
| ffi_type *rtype; |
| _Jv_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, |
| code); |
| |
| self->ncode = code; |
| return self->ncode; |
| } |
| |
| static void |
| throw_class_format_error (jstring msg) |
| { |
| jthrowable t = (msg |
| ? new java::lang::ClassFormatError (msg) |
| : new java::lang::ClassFormatError); |
| REPORT_EXCEPTION (t); |
| throw t; |
| } |
| |
| static void |
| throw_class_format_error (const char *msg) |
| { |
| throw_class_format_error (JvNewStringLatin1 (msg)); |
| } |
| |
| /* This function finds the method and location where the exception EXC |
| is caught in the stack frame. On return, it sets CATCH_METHOD and |
| CATCH_LOCATION with the method and location where the catch will |
| occur. If the exception is not caught, these are set to 0. |
| |
| This function should only be used with the __GCJ_DEBUG interpreter. */ |
| static void |
| find_catch_location (::java::lang::Throwable *exc, jthread thread, |
| jmethodID *catch_method, jlong *catch_loc) |
| { |
| *catch_method = 0; |
| *catch_loc = 0; |
| |
| _Jv_InterpFrame *frame |
| = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame); |
| while (frame != NULL) |
| { |
| pc_t pc = frame->get_pc (); |
| _Jv_InterpMethod *imeth |
| = reinterpret_cast<_Jv_InterpMethod *> (frame->self); |
| if (imeth->check_handler (&pc, imeth, exc)) |
| { |
| // This method handles the exception. |
| *catch_method = imeth->get_method (); |
| *catch_loc = imeth->insn_index (pc); |
| return; |
| } |
| |
| frame = frame->next_interp; |
| } |
| } |
| |
| /* This method handles JVMTI notifications of thrown exceptions. It |
| calls find_catch_location to figure out where the exception is |
| caught (if it is caught). |
| |
| Like find_catch_location, this should only be called with the |
| __GCJ_DEBUG interpreter. Since a few exceptions occur outside the |
| interpreter proper, it is important to not call this function |
| without checking JVMTI_REQUESTED_EVENT(Exception) first. */ |
| void |
| _Jv_ReportJVMTIExceptionThrow (jthrowable ex) |
| { |
| jthread thread = ::java::lang::Thread::currentThread (); |
| _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); |
| jmethodID throw_meth = frame->self->get_method (); |
| jlocation throw_loc = -1; |
| if (frame->frame_type == frame_interpreter) |
| { |
| _Jv_InterpFrame * iframe |
| = reinterpret_cast<_Jv_InterpFrame *> (frame); |
| _Jv_InterpMethod *imeth |
| = reinterpret_cast<_Jv_InterpMethod *> (frame->self); |
| throw_loc = imeth->insn_index (iframe->get_pc ()); |
| } |
| |
| jlong catch_loc; |
| jmethodID catch_method; |
| find_catch_location (ex, thread, &catch_method, &catch_loc); |
| _Jv_JVMTI_PostEvent (JVMTI_EVENT_EXCEPTION, thread, |
| _Jv_GetCurrentJNIEnv (), throw_meth, throw_loc, |
| ex, catch_method, catch_loc); |
| } |
| |
| |
| |
| 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 (klass); |
| } |
| else if (imeth != 0) // it could be abstract |
| { |
| _Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (imeth); |
| klass->methods[i].ncode = im->ncode (klass); |
| } |
| } |
| } |
| |
| _Jv_ClosureList ** |
| _Jv_InterpreterEngine::do_get_closure_list (jclass klass) |
| { |
| _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info; |
| |
| if (!iclass->closures) |
| iclass->closures = _Jv_ClosureListFinalizer (); |
| |
| return iclass->closures; |
| } |
| |
| void |
| _Jv_InterpreterEngine::do_allocate_static_fields (jclass klass, |
| int pointer_size, |
| int other_size) |
| { |
| _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info; |
| |
| // Splitting the allocations here lets us scan reference fields and |
| // avoid scanning non-reference fields. How reference fields are |
| // scanned is a bit tricky: we allocate using _Jv_AllocRawObj, which |
| // means that this memory will be scanned conservatively (same |
| // difference, since we know all the contents here are pointers). |
| // Then we put pointers into this memory into the 'fields' |
| // structure. Most of these are interior pointers, which is ok (but |
| // even so the pointer to the first reference field will be used and |
| // that is not an interior pointer). The 'fields' array is also |
| // allocated with _Jv_AllocRawObj (see defineclass.cc), so it will |
| // be scanned. A pointer to this array is held by Class and thus |
| // seen by the collector. |
| char *reference_fields = (char *) _Jv_AllocRawObj (pointer_size); |
| char *non_reference_fields = (char *) _Jv_AllocBytes (other_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) |
| continue; |
| |
| char *base = field->isRef() ? reference_fields : non_reference_fields; |
| field->u.addr = base + 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) |
| { |
| 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 |
| = _Jv_init_cif (method->signature, |
| arg_count, |
| staticp, |
| &result->cif, |
| &result->arg_types[0], |
| NULL); |
| |
| 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]; |
| } |
| } |
| |
| #ifdef DIRECT_THREADED |
| void |
| _Jv_CompileMethod (_Jv_InterpMethod* method) |
| { |
| if (method->prepared == NULL) |
| { |
| if (JVMTI::enabled) |
| _Jv_InterpMethod::run_debug (NULL, NULL, method); |
| else |
| _Jv_InterpMethod::run (NULL, NULL, method); |
| } |
| } |
| #endif // DIRECT_THREADED |