void set_debug(Debug *_debug) { debug = _debug; debugThread = currentThread; debug->methodBase = ptr2word(get_method_table(get_class_record(0))); debug->classBase = ptr2word(get_class_base()); debug->fieldBase = ptr2word(get_field_table(get_class_record(0))); debug->threads = threads; int i; for(i = 0; i < sizeof(debugEventOptions); i++) debugEventOptions[i] = DBG_EVENT_DISABLE; }
boolean debug_event(int event, Object *exception, const Thread *thread, const MethodRecord *method, byte *pc, int frame) { // Inform the debug thread (if any) that there has been a debug event. // return false if no debug thread is waiting. switch(debugEventOptions[event]) { case DBG_EVENT_DISABLE: return false; case DBG_EVENT_IGNORE: return true; default: break; } // Check that we have a debugger attached and that it is ready to go... if (!debug || get_monitor_count((&(debug->_super.sync))) != 0 || debugThread->state != CONDVAR_WAITING || (Debug *)debugThread->waitingOn != debug) return false; // Setup the state debug->typ = event; debug->exception = ptr2ref(exception); debug->thread = ptr2ref(thread); debug->pc = (pc ? pc - get_binary_base() : 0); debug->method = (method ? method - get_method_table(get_class_record(0)) : 0); debug->frame = frame; // Suspend all threads (except current) suspend_thread(null); // Make sure current thread is also suspended suspend_thread(currentThread); // Allow the debug thread to run resume_thread(debugThread); monitor_notify_unchecked(&debug->_super, 1); return true; }
/** * @return 1 or 0. */ STACKWORD instance_of (Object *obj, byte classIndex) { byte rtType; if (obj == null) return 0; rtType = get_class_index(obj); // TBD: support for interfaces if (is_interface (get_class_record(classIndex))) return 1; LABEL_INSTANCE: if (rtType == classIndex) return 1; if (rtType == JAVA_LANG_OBJECT) return 0; rtType = get_class_record(rtType)->parentClass; goto LABEL_INSTANCE; }
/** * Check to see if obj is a sub type of the type described by * cls. */ static boolean sub_type_of(byte obj, const byte cls) { if (cls == JAVA_LANG_OBJECT) return true; while (obj != cls) { obj = get_class_record(obj)->parentClass; if (obj == JAVA_LANG_OBJECT) return false; } return true; }
/** * Check to see if it is allowed to assign the an object of type srcCls * to an object of type dstCls. */ boolean is_assignable(const byte srcCls, const byte dstCls) { ClassRecord *dstRec; // Check common cases if (srcCls == dstCls || dstCls == JAVA_LANG_OBJECT) return true; dstRec = get_class_record(dstCls); if (is_interface(dstRec)) { // we are testing against an interface. So we use the associated interface // map to test if the src implements it... int base = get_interface_map_base(dstRec); // Special case all arrays implement cloneable if (dstCls == JAVA_LANG_CLONEABLE && is_array_class(get_class_record(srcCls))) return true; if (srcCls < base) return false; if (srcCls - base >= get_interface_map_len(dstRec)) return false; base = srcCls - base; return ((get_interface_map(dstRec)[base/8]) & (1 << (base%8))) != 0; } return sub_type_of(srcCls, dstCls); }
/** * Calls static initializer if necessary before * dispatching with dispatch_special(). * @param retAddr Return bytecode address. * @param btAddr Backtrack bytecode address (in case * static initializer is executed). */ void dispatch_special_checked (byte classIndex, byte methodIndex, byte *retAddr, byte *btAddr) { ClassRecord *classRecord; #if DEBUG_METHODS printf ("dispatch_special_checked: %d, %d, %d, %d\n", classIndex, methodIndex, (int) retAddr, (int) btAddr); #endif classRecord = get_class_record (classIndex); if (dispatch_static_initializer (classRecord, btAddr)) return; dispatch_special (get_method_record (classRecord, methodIndex), retAddr); }
void mark (Object *obj) { if (obj == JNULL) return; #ifdef VERIFY_GC assert (is_allocated (obj), GC0); #endif if (is_gc_marked (obj)) return; set_gc_marked (obj); if (is_array (obj)) { if (get_element_type (obj) == T_REFERENCE) { unsigned short i; unsigned short length = get_array_length (obj); REFERENCE *refarr = ref_array (obj); for (i = 0; i < length; i++) mark (refarr[i]); } } else { ClassRecord *classRecord; byte classIndex; classIndex = get_na_class_index (obj); for (;;) { classRecord = get_class_record (classIndex); // Mark fields of type REFERENCE. mark_reference_fields (obj, classRecord); if (classIndex == JAVA_LANG_OBJECT) break; classIndex = classRecord -> parentClass; } } }
void dispatch_virtual (Object *ref, int signature, byte *retAddr) { ClassRecord *classRecord; MethodRecord *methodRecord; int classIndex; #if DEBUG_METHODS printf("dispatch_virtual %d\n", signature); #endif if (ref == JNULL) { throw_new_exception (JAVA_LANG_NULLPOINTEREXCEPTION); return; } // When calling methods on arrays, we use the methods for the Object class... classIndex = get_class_index(ref); LABEL_METHODLOOKUP: classRecord = get_class_record (classIndex); methodRecord = find_method (classRecord, signature); if (methodRecord == null) { #if SAFE if (classIndex == JAVA_LANG_OBJECT) { throw_new_exception (JAVA_LANG_NOSUCHMETHODERROR); return; } #endif classIndex = classRecord->parentClass; goto LABEL_METHODLOOKUP; } if (dispatch_special (methodRecord, retAddr)) { if (is_synchronized(methodRecord)) { current_stackframe()->monitor = ref; enter_monitor (currentThread, ref); } } }
void dispatch_virtual (Object *ref, TWOBYTES signature, byte *retAddr) { MethodRecord *auxMethodRecord; byte auxByte; #if DEBUG_METHODS printf("dispatch_virtual %d\n", signature); #endif if (ref == JNULL) { throw_exception (nullPointerException); return; } auxByte = get_class_index(ref); LABEL_METHODLOOKUP: tempClassRecord = get_class_record (auxByte); auxMethodRecord = find_method (tempClassRecord, signature); if (auxMethodRecord == null) { #if SAFE if (auxByte == JAVA_LANG_OBJECT) { throw_exception (noSuchMethodError); return; } #endif auxByte = tempClassRecord->parentClass; goto LABEL_METHODLOOKUP; } if (dispatch_special (auxMethodRecord, retAddr)) { if (is_synchronized(auxMethodRecord)) { current_stackframe()->monitor = ref; enter_monitor (currentThread, ref); } } }
/** * Create a compact form of the specified call stack. * One int per frame containing * the method number and current offset. If the ignore parameter is non null * then frames that have a matching this field will not be included in the * trace. This allow the frames for the creation of an exception object to * be ignored. */ Object * create_stack_trace(Thread *thread, Object *ignore) { int frameCnt = thread->stackFrameIndex; Object *stackArray; JINT *data; int i; StackFrame *topFrame = ((StackFrame *)array_start(thread->stackFrameArray)) + frameCnt; StackFrame *stackFrame = topFrame; MethodRecord *methodBase = get_method_table(get_class_record(0)); byte *pcBase = get_binary_base() + 2; // Ignore frames if required. if (ignore) { while ((STACKWORD)ignore == *(stackFrame->localsBase)) { stackFrame--; frameCnt--; } } // Try and allocate the space for the trace. stackArray = new_single_array(AI, frameCnt); if (stackArray == JNULL) return JNULL; if (thread == currentThread) topFrame->pc = getPc(); // adjust top most pc to allow for return address hack topFrame->pc += 2; // Fill in the trace. data = jint_array(stackArray); for(i = 0; i < frameCnt; i++) { data[i] = ((stackFrame->methodRecord - methodBase) << 16) | (stackFrame->pc - pcBase - stackFrame->methodRecord->codeOffset); stackFrame--; } // restore correct pc topFrame->pc -= 2; return stackArray; }
/** * Calls static initializer if necessary before * dispatching with dispatch_special(). * @param retAddr Return bytecode address. * @param btAddr Backtrack bytecode address (in case * static initializer is executed). */ void dispatch_special_checked (byte classIndex, byte methodIndex, byte *retAddr, byte *btAddr) { ClassRecord *classRecord; MethodRecord *methodRecord; #if DEBUG_METHODS printf ("dispatch_special_checked: %d, %d, %d, %d\n", classIndex, methodIndex, (int) retAddr, (int) btAddr); #endif // If we need to run the initializer then the real method will get called // later, when we re-run the current instruction. classRecord = get_class_record (classIndex); if (!is_initialized_idx (classIndex)) if (dispatch_static_initializer (classRecord, btAddr) != EXEC_CONTINUE) return; methodRecord = get_method_record (classRecord, methodIndex); if(dispatch_special (methodRecord, retAddr)) { if (is_synchronized(methodRecord)) { if (!is_static(methodRecord)) { Object *ref = (Object *)curLocalsBase[0]; current_stackframe()->monitor = ref; enter_monitor (currentThread, ref); } else { Object *ref = (Object *)classRecord; current_stackframe()->monitor = ref; enter_monitor (currentThread, ref); } } } }
/** * @param classRecord Record for method class. * @param methodRecord Calle's method record. * @param retAddr What the PC should be upon return. * @return true iff the stack frame was pushed. */ boolean dispatch_special (MethodRecord *methodRecord, byte *retAddr) { /** * Note: This code is a little tricky, particularly when used with * a garbage collector. It manipulates the stack frame and in some cases * may need to perform memory allocation. In all cases we must take care * to ensure that if an allocation can be made then any live objects * on the stack must be below the current stack pointer. * In addition to the above we take great care so that this function can * be restarted (to allow us to wait for available memory). To enable this * we avoid making any commitments to changes to global state until both * stacks have been commited. */ #if DEBUG_METHODS int debug_ctr; #endif Object *stackFrameArray; StackFrame *stackFrame; StackFrame *stackBase; int newStackFrameIndex; STACKWORD *newStackTop; #if DEBUG_BYTECODE printf("call method %d ret %x\n", methodRecord - get_method_table(get_class_record(0)), retAddr); printf ("\n------ dispatch special - %d ------------------\n\n", methodRecord->signatureId); #endif #if DEBUG_METHODS printf ("dispatch_special: %d, %d\n", (int) methodRecord, (int) retAddr); printf ("-- signature id = %d\n", methodRecord->signatureId); printf ("-- code offset = %d\n", methodRecord->codeOffset); printf ("-- flags = %d\n", methodRecord->mflags); printf ("-- num params = %d\n", methodRecord->numParameters); //printf ("-- stack ptr = %d\n", (int) get_stack_ptr()); //printf ("-- max stack ptr= %d\n", (int) (currentThread->stackArray + (get_array_size(currentThread->stackArray))*2)); #endif // First deal with the easy case of a native call... if (is_native (methodRecord)) { #if DEBUG_METHODS printf ("-- native\n"); #endif // WARNING: Once the instruction below has been executed we may have // references on the stack that are above the stack pointer. If a GC // gets run when in this state the reference may get collected as // grabage. This means that any native functions that take a reference // parameter and that may end up allocating memory *MUST* protect that // reference before calling the allocator... pop_words_cur (methodRecord->numParameters); switch(dispatch_native (methodRecord->signatureId, get_stack_ptr_cur() + 1)) { case EXEC_RETRY: // Need to re-start the instruction, so reset the state of the stack curStackTop += methodRecord->numParameters; break; case EXEC_CONTINUE: // Normal completion return to the requested point. curPc = retAddr; break; case EXEC_RUN: // We are running new code, curPc will be set. Nothing to do. break; case EXEC_EXCEPTION: // An exception has been thrown. The PC will be set correctly and // the stack may have been adjusted... break; } // Stack frame not pushed return false; } // Now start to build the new stack frames. We start by placing the // the new stack pointer below any params. The params will become locals // in the new frame. newStackTop = get_stack_ptr_cur() - methodRecord->numParameters; newStackFrameIndex = (int)(byte)currentThread->stackFrameIndex; if (newStackFrameIndex >= 255) { throw_new_exception (JAVA_LANG_STACKOVERFLOWERROR); return false; } #if DEBUG_METHODS //for (debug_ctr = 0; debug_ctr < methodRecord->numParameters; debug_ctr++) // printf ("-- param[%d] = %ld\n", debug_ctr, (long) get_stack_ptr()[debug_ctr+1]); #endif stackFrameArray = ref2obj(currentThread->stackFrameArray); stackBase = (StackFrame *)array_start(stackFrameArray); // Setup OLD stackframe ready for return stackFrame = stackBase + (newStackFrameIndex); stackFrame->stackTop = newStackTop; stackFrame->pc = retAddr; // Push NEW stack frame // Increment size of stack frame array but do not commit to it until we have // completely built both new stacks. newStackFrameIndex++; stackFrame++; if (((byte *)stackFrame - (byte *)stackBase) >= get_array_length(stackFrameArray)) { #if FIXED_STACK_SIZE throw_new_exception (JAVA_LANG_STACKOVERFLOWERROR); return false; #else if (expand_call_stack(currentThread) < 0) return false; stackFrame = (StackFrame *)array_start(currentThread->stackFrameArray) + newStackFrameIndex; #endif } // Initialize rest of new stack frame stackFrame->methodRecord = methodRecord; stackFrame->monitor = null; stackFrame->localsBase = newStackTop + 1; // Allocate space for locals etc. newStackTop = init_sp(stackFrame, methodRecord); stackFrame->stackTop = newStackTop; currentThread->stackFrameIndex = newStackFrameIndex; // Check for stack overflow if (is_stack_overflow (newStackTop, methodRecord)) { #if FIXED_STACK_SIZE throw_new_exception (JAVA_LANG_STACKOVERFLOWERROR); return false; #else if (expand_value_stack(currentThread, methodRecord->maxOperands+methodRecord->numLocals) < 0) { currentThread->stackFrameIndex--; return false; } // NOTE at this point newStackTop is no longer valid! newStackTop = stackFrame->stackTop; #endif } // All set. So now we can finally commit to the new stack frames update_constant_registers (stackFrame); curStackTop = newStackTop; // and jump to the start of the new code curPc = get_code_ptr(methodRecord); return true; }
/** * Call a java static method from the VM * NOTE: This can only be used to call methods that have a signature * value that is less than 255. Normally it will be used to call * one of the specialsignature entries (which are all less than 255). */ void dispatch_java(byte classIndex, byte sig, byte *retAddr, byte *btAddr) { ClassRecord *classRecord = get_class_record (classIndex); MethodRecord *mRec = find_method (classRecord, sig); dispatch_special_checked(classIndex, mRec - get_method_table(classRecord), retAddr, btAddr); }
/** * Exceute the static initializer if required. Note that the ret address used * here is set such that the current instruction will be re-started when the * initialization completes. * @return An indication of how the VM should proceed */ int dispatch_static_initializer (ClassRecord *aRec, byte *retAddr) { int state = get_init_state(aRec); ClassRecord *init = aRec; ClassRecord *super = get_class_record(init->parentClass); MethodRecord *method; // Are we needed? if (state & C_INITIALIZED) return EXEC_CONTINUE; // We need to initialize all of the super classes first. So we find the // highest one that has not been initialized and deal with that. This code // will then be called again and we will init the next highest and so on // until all of the classes in the chain are done. for(;;) { // find first super class that has not been initialized while (init != super && (get_init_state(super) & C_INITIALIZED) == 0) { init = super; super = get_class_record(init->parentClass); } // Do we have an initilizer if so we have found our class if (has_clinit (init)) break; // no initializer so mark as now initialized set_init_state (init, C_INITIALIZED); // If we are at the start of the list we are done if (init == aRec) return EXEC_CONTINUE; // Otherwise go do it all again init = aRec; } state = get_init_state(init); // are we already initializing ? if (state & C_INITIALIZING) { // Is it this thread that is doing the init? if (get_sync(init)->threadId == currentThread->threadId) return EXEC_CONTINUE; // No so we must retry the current instruction curPc = retAddr; sleep_thread(1); schedule_request(REQUEST_SWITCH_THREAD); return EXEC_RETRY; } #if DEBUG_METHODS printf ("dispatch_static_initializer: has clinit: %d, %d\n", (int) aRec, (int) retAddr); #endif // Static initializer is always the first method method = get_method_table(init); if ((byte *)method == get_binary_base() || method->signatureId != _6clinit_7_4_5V) { throw_new_exception (JAVA_LANG_NOSUCHMETHODERROR); return EXEC_EXCEPTION; } // Can we run it? if (!dispatch_special (method, retAddr)) return EXEC_RETRY; // Mark for next time set_init_state(init, C_INITIALIZING); // and claim the monitor current_stackframe()->monitor = (Object *)init; enter_monitor (currentThread, (Object *)init); return EXEC_RUN; }