#include "trace.h" #include "types.h" #include "constants.h" #include "classes.h" #include "interpreter.h" #include "platform_hooks.h" #include "threads.h" #include "opcodes.h" #include "configure.h" #include "memory.h" #include "language.h" #include "exceptions.h" #include "specialclasses.h" #include "fields.h" #include "stack.h" #include "poll.h" #define F_OFFSET_MASK 0x0F #if DEBUG_BYTECODE extern char *OPCODE_NAME[]; #endif // Interpreter globals: volatile boolean gMakeRequest; byte gRequestCode; byte *pc; STACKWORD *localsBase; STACKWORD *stackTop; // Temporary globals: byte tempByte; byte *tempBytePtr; JFLOAT tempFloat; ConstantRecord *tempConstRec; STACKWORD tempStackWord; STACKWORD *tempWordPtr; JSHORT tempInt; /** * Assumes pc points to 2-byte offset, and jumps. */ void do_goto (boolean aCond) { if (aCond) { pc += (JSHORT) (((TWOBYTES) *pc << 8) | *(pc+1)); pc--; } else { pc += 2; } } void do_isub (void) { STACKWORD poppedWord; poppedWord = pop_word(); set_top_word (word2jint(get_top_word()) - word2jint(poppedWord)); } #if FP_ARITHMETIC void do_fcmp (JFLOAT f1, JFLOAT f2, STACKWORD def) { if (f1 > f2) push_word (1); else if (f1 == f2) push_word (0); else if (f1 < f2) push_word (-1); else push_word (def); } #endif /** * @return A String instance, or JNULL if an exception was thrown * or the static initializer of String had to be executed. */ static inline Object *create_string (ConstantRecord *constantRecord, byte *btAddr) { Object *ref; Object *arr; TWOBYTES i; ref = new_object_checked (JAVA_LANG_STRING, btAddr); if (ref == JNULL) return JNULL; arr = new_primitive_array (T_CHAR, constantRecord->constantSize); if (arr == JNULL) { deallocate (obj2ptr(ref), class_size (JAVA_LANG_STRING)); return JNULL; } // printf ("char array at %d\n", (int) arr); store_word ((byte *) &(((String *) ref)->characters), 4, obj2word(arr)); for (i = 0; i < constantRecord->constantSize; i++) { jchar_array(arr)[i] = (JCHAR) get_constant_ptr(constantRecord)[i]; //printf ("char %d: %c\n", i, (char) (jchar_array(arr)[i])); } return ref; } /** * Pops the array index off the stack, assigns * both tempInt and tempBytePtr, and checks * bounds and null reference. The array reference * is the top word on the stack after this operation. * @return True if successful, false if an exception has been scheduled. */ boolean array_load_helper() { tempInt = word2jshort(pop_word()); tempBytePtr = word2ptr(get_top_ref()); if (tempBytePtr == JNULL) throw_exception (nullPointerException); else if (tempInt < 0 || tempInt >= get_array_length ((Object *) tempBytePtr)) throw_exception (arrayIndexOutOfBoundsException); else return true; return false; } /** * Same as array_load_helper, except that it pops * the reference from the stack. */ boolean array_store_helper() { if (array_load_helper()) { pop_ref(); return true; } return false; } /** * Everything runs inside here, essentially. * * To be able use only a single fast test on each instruction * several assumptions are made: * - currentThread is initialized and non-null and * it is not set to null by any bytecode instruction. * - Thus it is not allowed to call schedule_thread() in instructions, * use schedule_request( REQUEST_SWITCH_THREAD) instead. * - Whenever gMakeRequest is false, gRequestCode is REQUEST_TICK. * - Thus anybody who sets gRequestCode must also set gMakeRequest to true * (using schedule_request assures this). * - Only the request handler may set gMakeRequest to false. * - The millisecond timer interrupt must set gMakeRequest to true * for time slices to work. * */ void engine() { byte ticks_until_switch = TICKS_PER_TIME_SLICE; assert( currentThread != null, INTERPRETER0); schedule_request( REQUEST_SWITCH_THREAD); LABEL_ENGINELOOP: instruction_hook(); assert( currentThread != null, INTERPRETER1); while( gMakeRequest) { byte requestCode = gRequestCode; gMakeRequest = false; gRequestCode = REQUEST_TICK; tick_hook(); if( requestCode == REQUEST_EXIT) { return; } if( requestCode == REQUEST_TICK) ticks_until_switch--; if( requestCode == REQUEST_SWITCH_THREAD || ticks_until_switch == 0){ ticks_until_switch = TICKS_PER_TIME_SLICE; #if DEBUG_THREADS printf ("switching thread: %d\n", (int)ticks_until_switch); #endif switch_thread(); #if DEBUG_THREADS printf ("done switching thread\n"); #endif switch_thread_hook(); } if( currentThread == null /* no runnable thread */ && gRequestCode == REQUEST_TICK){ /* no important request */ idle_hook(); schedule_request( REQUEST_SWITCH_THREAD); } } assert( gRequestCode == REQUEST_TICK, INTERPRETER2); assert( currentThread != null, INTERPRETER3); //----------------------------------------------- // SWITCH BEGINS HERE //----------------------------------------------- #if DEBUG_BYTECODE printf ("0x%X: \n", (int) pc); printf ("OPCODE (0x%X) %s\n", (int) *pc, OPCODE_NAME[*pc]); #endif switch (*pc++) { case OP_NOP: goto LABEL_ENGINELOOP; #include "op_stack.hc" #include "op_locals.hc" #include "op_arrays.hc" #include "op_objects.hc" #include "op_control.hc" #include "op_other.hc" #include "op_conversions.hc" #include "op_logical.hc" #include "op_arithmetic.hc" #include "op_methods.hc" /* #ifdef VERIFY default: assert(false, (TWOBYTES)(pc-1) % 10000); break; #endif */ } //----------------------------------------------- // SWITCH ENDS HERE //----------------------------------------------- #if !FP_ARITHMETIC throw_exception (noSuchMethodError); #else // This point should never be reached #ifdef VERIFY assert (false, 1000 + *pc); #endif // VERIFY #endif // FP_ARITHMETIC }