/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.runtime; import org.jikesrvm.ArchitectureSpecific; import org.jikesrvm.SubordinateArchitecture; import org.jikesrvm.VM_Registers; import org.jikesrvm.VM; import org.jikesrvm.VM_Constants; import org.jikesrvm.annotations.NoSubArchCompile; import org.jikesrvm.classloader.VM_Array; import org.jikesrvm.classloader.VM_Class; import org.jikesrvm.classloader.VM_DynamicTypeCheck; import org.jikesrvm.classloader.VM_Field; import org.jikesrvm.classloader.VM_MemberReference; import org.jikesrvm.classloader.VM_Method; import org.jikesrvm.classloader.VM_Type; import org.jikesrvm.classloader.VM_TypeReference; import org.jikesrvm.compilers.common.VM_CompiledMethod; import org.jikesrvm.compilers.common.VM_CompiledMethods; import org.jikesrvm.memorymanagers.mminterface.MM_Interface; import org.jikesrvm.objectmodel.VM_ObjectModel; import org.jikesrvm.scheduler.VM_Scheduler; import org.jikesrvm.scheduler.VM_Thread; import org.vmmagic.pragma.Entrypoint; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.LogicallyUninterruptible; import org.vmmagic.pragma.NoInline; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.LocalAddress; import org.vmmagic.unboxed.Offset; /** * Entrypoints into the runtime of the virtual machine. * * <p> These are "helper functions" called from machine code * emitted by VM_Compiler. * They implement functionality that cannot be mapped directly * into a small inline * sequence of machine instructions. See also: VM_Linker. * * <p> Note #1: If you add, remove, or change the signature of * any of these methods you may need to change VM_Entrypoints to match. * * <p> Note #2: Code here must be carefully written to be gc-safe * while manipulating * stackframe and instruction addresses. * * <p> Any time we are holding interior pointers to objects that * could be moved by a garbage * collection cycle we must either avoid passing through gc-sites * (by writing * straight line code with no "non-magic" method invocations) or we * must turn off the * collector (so that a gc request initiated by another thread will * not run until we're * done manipulating the bare pointers). Furthermore, while * the collector is turned off, * we must be careful not to make any allocation requests ("new"). * * <p> The interior pointers that we must worry about are: * <ul> * <li> "ip" values that point to interiors of "code" objects * <li> "fp" values that point to interior of "stack" objects * </ul> */ public class VM_Runtime implements VM_Constants, ArchitectureSpecific.VM_StackframeLayoutConstants { // Trap codes for communication with C trap handler. // public static final int TRAP_UNKNOWN = -1; public static final int TRAP_NULL_POINTER = 0; public static final int TRAP_ARRAY_BOUNDS = 1; public static final int TRAP_DIVIDE_BY_ZERO = 2; public static final int TRAP_STACK_OVERFLOW = 3; public static final int TRAP_CHECKCAST = 4; // opt-compiler public static final int TRAP_REGENERATE = 5; // opt-compiler public static final int TRAP_JNI_STACK = 6; // jni public static final int TRAP_MUST_IMPLEMENT = 7; public static final int TRAP_STORE_CHECK = 8; // opt-compiler public static final int TRAP_STACK_OVERFLOW_FATAL = 9; // assertion checking //---------------------------------------------------------------// // Type Checking. // //---------------------------------------------------------------// /** * Test if object is instance of target class/array or * implements target interface. * @param object object to be tested * @param targetID type reference id corresponding to target * class/array/interface * @return true iff is object instance of target type? */ @Entrypoint static boolean instanceOf(Object object, int targetID) throws NoClassDefFoundError { /* Here, LHS and RHS refer to the way we would treat these if they were arguments to an assignment operator and we were testing for assignment-compatibility. In Java, "rhs instanceof lhs" means that the operation "lhs = rhs" would succeed. This of course is backwards if one is looking at it from the point of view of the "instanceof" operator. */ VM_TypeReference tRef = VM_TypeReference.getTypeRef(targetID); VM_Type lhsType = tRef.peekType(); boolean forSubArch = VM_Magic.runningOnSubArch(); if (lhsType == null) { lhsType = tRef.resolve(forSubArch); } if (!lhsType.isResolved(forSubArch)) { lhsType.resolve(forSubArch); // forces loading/resolution of super class/interfaces } /* Test for null only AFTER we have resolved the type of targetID. */ if (object == null) { return false; // null is not an instance of any type } VM_Type rhsType = VM_ObjectModel.getObjectType(object); /* RHS must already be resolved, since we have a non-null object that is an instance of RHS */ if (VM.VerifyAssertions) VM._assert(rhsType.isResolved(forSubArch)); if (VM.VerifyAssertions) VM._assert(lhsType.isResolved(forSubArch)); return lhsType == rhsType || VM_DynamicTypeCheck.instanceOfResolved(lhsType, rhsType); } /** * Uninterruptible version for fully resolved proper classes. * @param object object to be tested * @param id type id corresponding to target class. * @return true iff is object instance of target type? */ @Uninterruptible @Entrypoint static boolean instanceOfResolvedClass(Object object, int id) { if (object == null) { return false; // null is not an instance of any type } VM_Class lhsType = VM_Type.getType(id).asClass(); Object[] rhsTIB = VM_ObjectModel.getTIB(object); return VM_DynamicTypeCheck.instanceOfClass(lhsType, rhsTIB); } /** * Quick version for final classes, array of final class or array of * primitives * * @param object Object to be tested * @param targetTibOffset JTOC offset of TIB of target type * * @return <code>true</code> iff <code>object</code> is instance of the * target type */ @Uninterruptible @Entrypoint static boolean instanceOfFinal(Object object, Offset targetTibOffset) { if (object == null) { return false; // null is not an instance of any type } Object lhsTib = VM_Magic.getObjectAtOffset(VM_Magic.getJTOC(), targetTibOffset); Object rhsTib = VM_ObjectModel.getTIB(object); return lhsTib == rhsTib; } /** * Throw exception unless object is instance of target * class/array or implements target interface. * @param object object to be tested * @param id of type reference corresponding to target class/array/interface */ @Entrypoint static void checkcast(Object object, int id) throws ClassCastException, NoClassDefFoundError { if (object == null) return; boolean forSubarch = VM_Magic.runningOnSubArch(); VM_TypeReference tRef = VM_TypeReference.getTypeRef(id); VM_Type lhsType = tRef.peekType(); if (lhsType == null) { lhsType = tRef.resolve(forSubarch); } VM_Type rhsType = VM_ObjectModel.getObjectType(object); if (lhsType == rhsType) { return; // exact match } // not an exact match, do more involved lookups // if (!isAssignableWith(lhsType, rhsType)) { raiseCheckcastException(lhsType, rhsType); } } /** * Throw exception unless object is instance of target resolved proper class. * @param object object to be tested * @param id of type corresponding to target class */ @Uninterruptible @Entrypoint static void checkcastResolvedClass(Object object, int id) { if (object == null) return; // null can be cast to any type VM_Class lhsType = VM_Type.getType(id).asClass(); Object[] rhsTIB = VM_ObjectModel.getTIB(object); if (VM.VerifyAssertions) { VM._assert(rhsTIB != null); } if (!VM_DynamicTypeCheck.instanceOfClass(lhsType, rhsTIB)) { VM_Type rhsType = VM_ObjectModel.getObjectType(object); raiseCheckcastException(lhsType, rhsType); } } /** * quick version for final classes, array of final class or array of primitives */ @Uninterruptible @Entrypoint static void checkcastFinal(Object object, Offset targetTibOffset) { if (object == null) return; // null can be cast to any type Object lhsTib = VM_Magic.getObjectAtOffset(VM_Magic.getJTOC(), targetTibOffset); Object rhsTib = VM_ObjectModel.getTIB(object); if (lhsTib != rhsTib) { VM_Type lhsType = VM_Magic.objectAsType(VM_Magic.getObjectAtOffset(lhsTib, Offset.fromIntZeroExtend(TIB_TYPE_INDEX << LOG_BYTES_IN_ADDRESS))); VM_Type rhsType = VM_Magic.objectAsType(VM_Magic.getObjectAtOffset(rhsTib, Offset.fromIntZeroExtend(TIB_TYPE_INDEX << LOG_BYTES_IN_ADDRESS))); raiseCheckcastException(lhsType, rhsType); } } @LogicallyUninterruptible @Uninterruptible private static void raiseCheckcastException(VM_Type lhsType, VM_Type rhsType) { VM.sysWriteln("Checkcast exception!"); throw new ClassCastException("Cannot cast a(n) " + rhsType + " to a(n) " + lhsType); } /** * Throw exception iff array assignment is illegal. */ @Entrypoint static void checkstore(Object array, Object arrayElement) throws ArrayStoreException { if (arrayElement == null) { return; // null may be assigned to any type } VM_Type lhsType = VM_Magic.getObjectType(array); VM_Type elmType = lhsType.asArray().getElementType(); if (elmType == VM_Type.JavaLangObjectType) { return; // array of Object can receive anything } VM_Type rhsType = VM_Magic.getObjectType(arrayElement); if (elmType == rhsType) { return; // exact type match } if (isAssignableWith(elmType, rhsType)) { return; } throw new ArrayStoreException(); } /** * May a variable of type "lhs" be assigned a value of type "rhs"? * @param lhs type of variable * @param rhs type of value * @return true --> assignment is legal * false --> assignment is illegal * <strong>Assumption</strong>: caller has already tested "trivial" case * (exact type match) * so we need not repeat it here */ public static boolean isAssignableWith(VM_Type lhs, VM_Type rhs) { boolean forSubArch = VM_Magic.runningOnSubArch(); if (!lhs.isResolved(forSubArch)) { lhs.resolve(forSubArch); } if (!rhs.isResolved(forSubArch)) { rhs.resolve(forSubArch); } return VM_DynamicTypeCheck.instanceOfResolved(lhs, rhs); } //---------------------------------------------------------------// // Object Allocation. // //---------------------------------------------------------------// /** * Allocate something like "new Foo()". * @param id id of type reference of class to create. * @return object with header installed and all fields set to zero/null * (ready for initializer to be run on it) * See also: bytecode 0xbb ("new") */ @Entrypoint static Object unresolvedNewScalar(int id, int site) throws NoClassDefFoundError, OutOfMemoryError { VM_TypeReference tRef = VM_TypeReference.getTypeRef(id); VM_Type t = tRef.peekType(); boolean forSubArch = VM_Magic.runningOnSubArch(); if (t == null) { t = tRef.resolve(forSubArch); } VM_Class cls = t.asClass(); if (!cls.isInitialized(forSubArch)) { initializeClassForDynamicLink(cls); } int allocator = MM_Interface.pickAllocator(cls); int align = VM_ObjectModel.getAlignment(cls); int offset = VM_ObjectModel.getOffsetForAlignment(cls); return resolvedNewScalar(cls.getInstanceSize(), cls.getTypeInformationBlock(), cls.hasFinalizer(), allocator, align, offset, site); } /** * Allocate something like "new Foo()". * @param cls VM_Class of array to create * @return object with header installed and all fields set to zero/null * (ready for initializer to be run on it) * See also: bytecode 0xbb ("new") */ public static Object resolvedNewScalar(VM_Class cls) { int allocator = MM_Interface.pickAllocator(cls); int site = MM_Interface.getAllocationSite(false); int align = VM_ObjectModel.getAlignment(cls); int offset = VM_ObjectModel.getOffsetForAlignment(cls); return resolvedNewScalar(cls.getInstanceSize(), cls.getTypeInformationBlock(), cls.hasFinalizer(), allocator, align, offset, site); } /** * Allocate something like "new Foo()". * @param size size of object (including header), in bytes * @param tib type information block for object * @param hasFinalizer does this type have a finalizer? * @param allocator int that encodes which allocator should be used * @param align the alignment requested; must be a power of 2. * @param offset the offset at which the alignment is desired. * @param site the site id of the calling allocation site * @return object with header installed and all fields set to zero/null * (ready for initializer to be run on it) * See also: bytecode 0xbb ("new") */ @Entrypoint public static Object resolvedNewScalar(int size, Object[] tib, boolean hasFinalizer, int allocator, int align, int offset, int site) throws OutOfMemoryError { // GC stress testing if (VM.ForceFrequentGC) checkAllocationCountDownToGC(); // Allocate the object and initialize its header Object newObj = MM_Interface.allocateScalar(size, tib, allocator, align, offset, site); // Deal with finalization if (hasFinalizer) MM_Interface.addFinalizer(newObj); return newObj; } /** * Allocate something like "new Foo[]". * @param numElements number of array elements * @param id id of type reference of array to create. * @param site the site id of the calling allocation site * @return array with header installed and all fields set to zero/null * See also: bytecode 0xbc ("anewarray") */ @Entrypoint public static Object unresolvedNewArray(int numElements, int id, int site) throws NoClassDefFoundError, OutOfMemoryError, NegativeArraySizeException { VM_TypeReference tRef = VM_TypeReference.getTypeRef(id); VM_Type t = tRef.peekType(); boolean forSubArch = VM_Magic.runningOnSubArch(); if (t == null) { t = tRef.resolve(forSubArch); } VM_Array array = t.asArray(); if (!array.isInitialized(forSubArch)) { array.resolve(forSubArch); array.instantiate(forSubArch); } return resolvedNewArray(numElements, array, site); } /** * Allocate something like "new Foo[]". * @param numElements number of array elements * @param array VM_Array of array to create * @return array with header installed and all fields set to zero/null * See also: bytecode 0xbc ("anewarray") */ public static Object resolvedNewArray(int numElements, VM_Array array) throws OutOfMemoryError, NegativeArraySizeException { return resolvedNewArray(numElements, array, MM_Interface.getAllocationSite(false)); } public static Object resolvedNewArray(int numElements, VM_Array array, int site) throws OutOfMemoryError, NegativeArraySizeException { return resolvedNewArray(numElements, array.getLogElementSize(), VM_ObjectModel.computeArrayHeaderSize(array), array.getTypeInformationBlock(), MM_Interface.pickAllocator(array), VM_ObjectModel.getAlignment(array), VM_ObjectModel.getOffsetForAlignment(array), site); } /** * Allocate something like "new int[cnt]" or "new Foo[cnt]". * @param numElements number of array elements * @param logElementSize size in bytes of an array element, log base 2. * @param headerSize size in bytes of array header * @param tib type information block for array object * @param allocator int that encodes which allocator should be used * @param align the alignment requested; must be a power of 2. * @param offset the offset at which the alignment is desired. * @return array object with header installed and all elements set * to zero/null * See also: bytecode 0xbc ("newarray") and 0xbd ("anewarray") */ @Entrypoint public static Object resolvedNewArray(int numElements, int logElementSize, int headerSize, Object[] tib, int allocator, int align, int offset, int site) throws OutOfMemoryError, NegativeArraySizeException { if (numElements < 0) raiseNegativeArraySizeException(); // GC stress testing if (VM.ForceFrequentGC) checkAllocationCountDownToGC(); // Allocate the array and initialize its header return MM_Interface.allocateArray(numElements, logElementSize, headerSize, tib, allocator, align, offset, site); } /** * clone a Scalar or Array Object * called from java/lang/Object.clone() * * For simplicity, we just code this more or less in Java using * internal reflective operations and some magic. * This is inefficient for large scalar objects, but until that * is proven to be a performance problem, we won't worry about it. * By keeping this in Java instead of dropping into VM_Memory.copy, * we avoid having to add special case code to deal with write barriers, * and other such things. * * @param obj the object to clone * @return the cloned object */ public static Object clone(Object obj) throws OutOfMemoryError, CloneNotSupportedException { VM_Type type = VM_Magic.getObjectType(obj); if (type.isArrayType()) { VM_Array ary = type.asArray(); int nelts = VM_ObjectModel.getArrayLength(obj); Object newObj = resolvedNewArray(nelts, ary); System.arraycopy(obj, 0, newObj, 0, nelts); return newObj; } else { if (!(obj instanceof Cloneable)) { throw new CloneNotSupportedException(); } VM_Class cls = type.asClass(); Object newObj = resolvedNewScalar(cls); for (VM_Field f : cls.getInstanceFields()) { VM_TypeReference ft = f.getType(); if (ft.isReferenceType()) { // Do via slower "VM-internal reflection" to enable // collectors to do the right thing wrt reference counting // and write barriers. f.setObjectValueUnchecked(newObj, f.getObjectValueUnchecked(obj)); } else { Offset offset = f.getOffset(); switch (ft.getMemoryBytes()) { case BYTES_IN_BYTE: { byte bits = VM_Magic.getByteAtOffset(obj, offset); VM_Magic.setByteAtOffset(newObj, offset, bits); break; } case BYTES_IN_CHAR: { char bits = VM_Magic.getCharAtOffset(obj, offset); VM_Magic.setCharAtOffset(newObj, offset, bits); break; } case BYTES_IN_LONG: { long bits = VM_Magic.getLongAtOffset(obj, offset); VM_Magic.setLongAtOffset(newObj, offset, bits); break; } default: { if (VM.VerifyAssertions) VM._assert(ft.getMemoryBytes() == BYTES_IN_INT); int bits = VM_Magic.getIntAtOffset(obj, offset); VM_Magic.setIntAtOffset(newObj, offset, bits); break; } } } } return newObj; } } /** * Helper function to actually throw the required exception. * Keep out of line to mitigate code space when quickNewArray is inlined. */ @NoInline private static void raiseNegativeArraySizeException() throws NegativeArraySizeException { throw new NegativeArraySizeException(); } /** * Get an object's "hashcode" value. * * Side effect: hash value is generated and stored into object's * status word. * * @return object's hashcode. * @see java.lang.Object#hashCode(). */ public static int getObjectHashCode(Object object) { return VM_ObjectModel.getObjectHashCode(object); } //---------------------------------------------------------------// // Dynamic linking. // //---------------------------------------------------------------// /** * Prepare a class for use prior to first allocation, * field access, or method invocation. * Made public so that it is accessible from java.lang.reflect.*. * @see VM_MemberReference#needsDynamicLink */ public static void initializeClassForDynamicLink(VM_Class cls) { if (VM.TraceClassLoading) { VM.sysWrite("VM_Runtime.initializeClassForDynamicLink: (begin) " + cls + "\n"); } boolean forSubArch = VM_Magic.runningOnSubArch(); cls.resolve(forSubArch); cls.instantiate(forSubArch); cls.initialize(forSubArch); // throws ExceptionInInitializerError if (cls.isDeclaredForSubArch()) { cls.load(true); cls.resolve(true); cls.instantiate(true); cls.initialize(true); } if (VM.TraceClassLoading) { VM.sysWrite("VM_Runtime.initializeClassForDynamicLink: (end) " + cls + "\n"); } } //---------------------------------------------------------------// // Implementation Errors. // //---------------------------------------------------------------// /** * Report unexpected method call: interface method * (virtual machine dispatching error, shouldn't happen). */ static void unexpectedInterfaceMethodCall() { VM.sysFail("interface method dispatching error"); } /** * Report unexpected method call: abstract method (verification error). */ @Entrypoint static void unexpectedAbstractMethodCall() { VM.sysWrite("VM_Runtime.unexpectedAbstractMethodCall\n"); throw new AbstractMethodError(); } //---------------------------------------------------------------// // Exception Handling. // //---------------------------------------------------------------// /** * Deliver a software exception to current java thread. * @param exceptionObject exception object to deliver * (null --> deliver NullPointerException). * does not return * (stack is unwound and execution resumes in a catch block) * * This method is public so that it can be invoked by java.lang.VMClass. */ @NoInline @Entrypoint @NoSubArchCompile public static void athrow(Throwable exceptionObject) { boolean forSubArch = VM_Magic.runningOnSubArch(); VM_Registers registers = !forSubArch ? new ArchitectureSpecific.VM_Registers() : new SubordinateArchitecture.VM_Registers(); VM.disableGC(); // VM.enableGC() is called when the exception is delivered. VM_Magic.saveThreadState(registers); registers.inuse = true; deliverException(exceptionObject, registers, forSubArch); } /** * Deliver a hardware exception to current java thread. * @param trapCode code indicating kind of exception that was trapped * (see TRAP_xxx, above) * @param trapInfo array subscript (for array bounds trap, only) * does not return * (stack is unwound, starting at trap site, and * execution resumes in a catch block somewhere up the stack) * /or/ execution resumes at instruction following trap * (for TRAP_STACK_OVERFLOW) * * <p> Note: Control reaches here by the actions of an * external "C" signal handler * which saves the register state of the trap site into the * "hardwareExceptionRegisters" field of the current * VM_Thread object. * The signal handler also inserts a <hardware trap> frame * onto the stack immediately above this frame, for use by * VM_HardwareTrapGCMapIterator during garbage collection. */ @Entrypoint @NoSubArchCompile static void deliverHardwareException(int trapCode, int trapInfo) { VM_Thread myThread = VM_Scheduler.getCurrentThread(); VM_Registers exceptionRegisters = myThread.getHardwareExceptionRegisters(); boolean forSubArch = VM_Magic.runningOnSubArch(); if ((trapCode == TRAP_STACK_OVERFLOW || trapCode == TRAP_JNI_STACK) && myThread.getStack().length < (STACK_SIZE_MAX >> LOG_BYTES_IN_ADDRESS) && !myThread.hasNativeStackFrame()) { // expand stack by the size appropriate for normal or native frame // and resume execution at successor to trap instruction // (C trap handler has set register.ip to the instruction following the trap). if (trapCode == TRAP_JNI_STACK) { VM_Thread.resizeCurrentStack(myThread.getStackLength() + STACK_SIZE_JNINATIVE_GROW, exceptionRegisters); } else { VM_Thread.resizeCurrentStack(myThread.getStackLength() + STACK_SIZE_GROW, exceptionRegisters); } if (VM.VerifyAssertions) VM._assert(exceptionRegisters.inuse); exceptionRegisters.inuse = false; VM_Magic.restoreHardwareExceptionState(exceptionRegisters); if (VM.VerifyAssertions) VM._assert(NOT_REACHED); } // GC stress testing if (canForceGC()) { //VM.sysWrite("FORCING GC: in deliverHardwareException\n"); System.gc(); } // Sanity checking. // Hardware traps in uninterruptible code should be considered hard failures. if (!VM.sysFailInProgress()) { LocalAddress fp = exceptionRegisters.getInnermostFramePointer(); int compiledMethodId = VM_Magic.getCompiledMethodID(fp); if (compiledMethodId != INVISIBLE_METHOD_ID) { VM_CompiledMethod compiledMethod = VM_CompiledMethods.getCompiledMethod(compiledMethodId); LocalAddress ip = exceptionRegisters.getInnermostInstructionAddress(); Offset instructionOffset = compiledMethod.getInstructionOffset(ip); if (compiledMethod.isWithinUninterruptibleCode(instructionOffset)) { switch (trapCode) { case TRAP_NULL_POINTER: VM.sysWriteln("\nFatal error: NullPointerException within uninterruptible region."); break; case TRAP_ARRAY_BOUNDS: VM.sysWriteln("\nFatal error: ArrayIndexOutOfBoundsException within uninterruptible region (index was ", trapInfo, ")."); break; case TRAP_DIVIDE_BY_ZERO: VM.sysWriteln("\nFatal error: DivideByZero within uninterruptible region."); break; case TRAP_STACK_OVERFLOW: case TRAP_JNI_STACK: VM.sysWriteln("\nFatal error: StackOverflowError within uninterruptible region."); break; case TRAP_CHECKCAST: VM.sysWriteln("\nFatal error: ClassCastException within uninterruptible region."); break; case TRAP_MUST_IMPLEMENT: VM.sysWriteln("\nFatal error: IncompatibleClassChangeError within uninterruptible region."); break; case TRAP_STORE_CHECK: VM.sysWriteln("\nFatal error: ArrayStoreException within uninterruptible region."); break; default: VM.sysWriteln("\nFatal error: Unknown hardware trap within uninterruptible region."); break; } VM.sysFail("Exiting virtual machine due to uninterruptibility violation."); } } } Throwable exceptionObject; switch (trapCode) { case TRAP_NULL_POINTER: exceptionObject = new java.lang.NullPointerException(); break; case TRAP_ARRAY_BOUNDS: exceptionObject = new java.lang.ArrayIndexOutOfBoundsException(trapInfo); break; case TRAP_DIVIDE_BY_ZERO: exceptionObject = new java.lang.ArithmeticException(); break; case TRAP_STACK_OVERFLOW: case TRAP_JNI_STACK: exceptionObject = new java.lang.StackOverflowError(); break; case TRAP_CHECKCAST: exceptionObject = new java.lang.ClassCastException(); break; case TRAP_MUST_IMPLEMENT: exceptionObject = new java.lang.IncompatibleClassChangeError(); break; case TRAP_STORE_CHECK: exceptionObject = new java.lang.ArrayStoreException(); break; default: exceptionObject = new java.lang.UnknownError(); VM_Scheduler.traceback("UNKNOWN ERROR"); break; } VM.disableGC(); // VM.enableGC() is called when the exception is delivered. deliverException(exceptionObject, exceptionRegisters, forSubArch); } /** * Unlock an object and then deliver a software exception * to current java thread. * @param objToUnlock object to unlock * @param objToThrow exception object to deliver * (null --> deliver NullPointerException). * does not return (stack is unwound and execution resumes in a catch block) */ @NoInline @Entrypoint static void unlockAndThrow(Object objToUnlock, Throwable objToThrow) { VM_ObjectModel.genericUnlock(objToUnlock); athrow(objToThrow); } /** * Create and throw a java.lang.ArrayIndexOutOfBoundsException * Only used in some configurations where it is easier to make a call * then recover the array index from a trap instruction. */ @NoInline @Entrypoint static void raiseArrayIndexOutOfBoundsException(int index) { throw new java.lang.ArrayIndexOutOfBoundsException(index); } /** * Create and throw a java.lang.ArrayIndexOutOfBoundsException * Used (rarely) by the opt compiler when it has determined that * an array access will unconditionally raise an array bounds check * error, but it has lost track of exactly what the index is going to be. */ @NoInline static void raiseArrayIndexOutOfBoundsException() { throw new java.lang.ArrayIndexOutOfBoundsException(); } /** * Create and throw a java.lang.NullPointerException * Used in a few circumstances to reduce code space costs * of inlining (see java.lang.System.arraycopy()). Could also * be used to raise a null pointer exception without going through * the hardware trap handler; currently this is only done when the * opt compiler has determined that an instruction will unconditionally * raise a null pointer exception. */ @NoInline @Entrypoint public static void raiseNullPointerException() { throw new java.lang.NullPointerException(); } /** * Create and throw a java.lang.ArrayStoreException * Used in a few circumstances to reduce code space costs * of inlining (see java.lang.System.arraycopy()). */ @NoInline public static void raiseArrayStoreException() { throw new java.lang.ArrayStoreException(); } /** * Create and throw a java.lang.ArithmeticException * Used to raise an arithmetic exception without going through * the hardware trap handler; currently this is only done when the * opt compiler has determined that an instruction will unconditionally * raise an arithmetic exception. */ @NoInline @Entrypoint static void raiseArithmeticException() { throw new java.lang.ArithmeticException(); } /** * Create and throw a java.lang.AbstractMethodError. * Used to handle error cases in invokeinterface dispatching. */ @NoInline @Entrypoint static void raiseAbstractMethodError() { throw new java.lang.AbstractMethodError(); } /** * Create and throw a java.lang.IllegalAccessError. * Used to handle error cases in invokeinterface dispatching. */ @NoInline @Entrypoint static void raiseIllegalAccessError() { throw new java.lang.IllegalAccessError(); } //----------------// // implementation // //----------------// @NoSubArchCompile public static void init() { // tell "RunBootImage.C" to pass control to // "VM_Runtime.deliverHardwareException()" // whenever the host operating system detects a hardware trap // VM_BootRecord.the_boot_record.hardwareTrapMethodId = VM_CompiledMethods.createHardwareTrapCompiledMethod(false).getId(); VM_BootRecord.the_boot_record.deliverHardwareExceptionOffset = VM_Entrypoints.deliverHardwareExceptionMethod.getOffset(); // tell "RunBootImage.C" to set "VM_Scheduler.debugRequested" flag // whenever the host operating system detects a debug request signal // VM_BootRecord.the_boot_record.debugRequestedOffset = VM_Entrypoints.debugRequestedField.getOffset(); } /** * Build a multi-dimensional array. * @param methodId Apparently unused (!) * @param numElements number of elements to allocate for each dimension * @param arrayType type of array that will result * @return array object */ @NoSubArchCompile public static Object buildMultiDimensionalArray(int methodId, int[] numElements, VM_Array arrayType) { boolean forSubArch = VM_Magic.runningOnSubArch(); VM_Method method = VM_MemberReference.getMemberRef(methodId).asMethodReference().peekResolvedMethod(forSubArch); if (VM.VerifyAssertions) VM._assert(method != null); return buildMDAHelper(method, numElements, 0, arrayType); } /** * @param method Apparently unused (!) * @param numElements Number of elements to allocate for each dimension * @param dimIndex Current dimension to build * @param arrayType type of array that will result */ public static Object buildMDAHelper(VM_Method method, int[] numElements, int dimIndex, VM_Array arrayType) { boolean forSubArch = VM_Magic.runningOnSubArch(); if (!arrayType.isInstantiated(forSubArch)) { arrayType.resolve(forSubArch); arrayType.instantiate(forSubArch); } int nelts = numElements[dimIndex]; Object newObject = resolvedNewArray(nelts, arrayType); if (++dimIndex == numElements.length) { return newObject; // all dimensions have been built } Object[] newArray = (Object[]) newObject; VM_Array newArrayType = arrayType.getElementType().asArray(); for (int i = 0; i < nelts; ++i) { newArray[i] = buildMDAHelper(method, numElements, dimIndex, newArrayType); } return newArray; } /** * Deliver an exception to current java thread. * <STRONG> Precondition: </STRONG> VM.disableGC has already been called. * <ol> * <li> exceptionRegisters may not match any reasonable stack * frame at this point. * <li> we're going to be playing with raw addresses (fp, ip). * </ol> * @param exceptionObject exception object to deliver * @param exceptionRegisters register state corresponding to exception site * does not return * <ul> * <li> stack is unwound and execution resumes in a catch block * <li> <em> or </em> current thread is terminated if no catch block is found * </ul> */ @NoSubArchCompile private static void deliverException(Throwable exceptionObject, VM_Registers exceptionRegisters, boolean forSubArch) { if (VM.TraceExceptionDelivery) { VM.sysWriteln("VM_Runtime.deliverException() entered; just got an exception object."); } // walk stack and look for a catch block // if (VM.TraceExceptionDelivery) { VM.sysWrite("Hunting for a catch block..."); } VM_Type exceptionType = VM_Magic.getObjectType(exceptionObject); LocalAddress fp = exceptionRegisters.getInnermostFramePointer(); while (VM_Magic.getCallerFramePointer(fp).NE(VM_Magic.addressAsLocalAddress(STACKFRAME_SENTINEL_FP))) { int compiledMethodId = VM_Magic.getCompiledMethodID(fp); if (compiledMethodId != INVISIBLE_METHOD_ID) { VM_CompiledMethod compiledMethod = VM_CompiledMethods.getCompiledMethod(compiledMethodId); VM_ExceptionDeliverer exceptionDeliverer = compiledMethod.getExceptionDeliverer(); LocalAddress ip = exceptionRegisters.getInnermostInstructionAddress(); Offset ipOffset = compiledMethod.getInstructionOffset(ip); int catchBlockOffset = compiledMethod.findCatchBlockForInstruction(ipOffset, exceptionType); if (catchBlockOffset >= 0) { // found an appropriate catch block if (VM.TraceExceptionDelivery) { VM.sysWriteln("found one; delivering."); } LocalAddress catchBlockStart = compiledMethod.getInstructionAddress(Offset.fromIntSignExtend(catchBlockOffset)); exceptionDeliverer.deliverException(compiledMethod, catchBlockStart, exceptionObject, exceptionRegisters); if (VM.VerifyAssertions) VM._assert(NOT_REACHED); } exceptionDeliverer.unwindStackFrame(compiledMethod, exceptionRegisters); } else { unwindInvisibleStackFrame(exceptionRegisters); } fp = exceptionRegisters.getInnermostFramePointer(); } if (VM.TraceExceptionDelivery) { VM.sysWriteln("Nope."); VM.sysWriteln("VM_Runtime.deliverException() found no catch block."); } /* No appropriate catch block found. */ VM_Scheduler.getCurrentThread().handleUncaughtException(exceptionObject); } /** * Skip over all frames below currfp with saved code pointers outside of heap * (C frames), stopping at the native frame immediately preceding the glue * frame which contains the method ID of the native method (this is necessary * to allow retrieving the return address of the glue frame) * * @param currfp The current frame is expected to be one of the JNI functions * called from C, below which is one or more native stack frames */ @Uninterruptible @NoSubArchCompile public static LocalAddress unwindNativeStackFrame(LocalAddress currfp) { // Remembered address of previous FP LocalAddress callee_fp; // Address of native frame LocalAddress fp = VM_Magic.getCallerFramePointer(currfp); // Instruction pointer for current native frame LocalAddress ip; // Loop until either we fall off the stack or we find an instruction address // in one of our heaps do { callee_fp = fp; ip = VM_Magic.getReturnAddress(fp); fp = VM_Magic.getCallerFramePointer(fp); } while (!MM_Interface.addressInVM(VM_Magic.localAddressAsAddress(ip)) && fp.NE(VM_Magic.addressAsLocalAddress(STACKFRAME_SENTINEL_FP))); if (VM.BuildForPowerPC) { if (VM.BuildForSVR4ABI || VM.BuildForMachOABI) { // for SVR4 convention, a Java-to-C frame has two mini frames, // stop at mini frame (2) whose saved ip is in VM (out of line machine // code), in the case of sentinel fp, it has to return the callee's fp // because GC ScanThread uses it to get return address and so on. if (MM_Interface.addressInVM(VM_Magic.localAddressAsAddress(ip))) { return fp; } else { return callee_fp; } // AIX and 64-bit Linux PPC use PowerOpen ABI } else { return callee_fp; } } else { return callee_fp; } } /** * The current frame is expected to be one of the JNI functions * called from C, * below which is one or more native stack frames * Skip over all frames below which do not contain any object * references. */ @Uninterruptible @NoSubArchCompile public static LocalAddress unwindNativeStackFrameForGC(LocalAddress currfp) { if (VM.BuildForMachOABI) { // Unlike on AIX, there are two glue frames. The frame the // VM_JNICompiler refers to as "glue frame 1" will contain saved // volatile GPRs, so we must return that frame pointer and let a // JNIGCMapIterator have a chance to examine it. LocalAddress ip, callee_fp; LocalAddress fp = VM_Magic.getCallerFramePointer(currfp); do { callee_fp = fp; ip = VM_Magic.getReturnAddress(fp); fp = VM_Magic.getCallerFramePointer(fp); } while (!MM_Interface.addressInVM(VM_Magic.localAddressAsAddress(ip)) && fp.NE(VM_Magic.addressAsLocalAddress(STACKFRAME_SENTINEL_FP))); return callee_fp; } else { return unwindNativeStackFrame(currfp); } } /** * Unwind stack frame for an <invisible method>. * See also: VM_ExceptionDeliverer.unwindStackFrame() * * !!TODO: Could be a reflective method invoker frame. * Does it clobber any non-volatiles? * If so, how do we restore them? * (I don't think our current implementations of reflective method * invokers save/restore any nonvolatiles, so we're probably ok. * --dave 6/29/01 */ @NoSubArchCompile private static void unwindInvisibleStackFrame(VM_Registers registers) { registers.unwindStackFrame(); } /** * Number of allocations left before a GC is forced. Only used if VM.StressGCAllocationInterval is not 0. */ static int allocationCountDownToGC = VM.StressGCAllocationInterval; /** * Number of c-to-java jni calls left before a GC is forced. Only used if VM.StressGCAllocationInterval is not 0. */ static int jniCountDownToGC = VM.StressGCAllocationInterval; /** * Check to see if we are stress testing garbage collector and if another JNI call should * trigger a gc then do so. */ @Inline @NoSubArchCompile public static void checkJNICountDownToGC() { // Temporarily disabled as it will causes nightly to take too long to run // There should be a mechanism to optionally enable this countdown in VM_Configuration if (false && canForceGC()) { if (jniCountDownToGC-- <= 0) { jniCountDownToGC = VM.StressGCAllocationInterval; System.gc(); } } } /** * Check to see if we are stress testing garbage collector and if another allocation should * trigger a gc then do so. */ @Inline @NoSubArchCompile private static void checkAllocationCountDownToGC() { if (canForceGC()) { if (allocationCountDownToGC-- <= 0) { allocationCountDownToGC = VM.StressGCAllocationInterval; System.gc(); } } } /** * Return true if we are stress testing garbage collector and the system is in state where we * can force a garbage collection. */ @Inline @NoSubArchCompile private static boolean canForceGC() { return VM.ForceFrequentGC && VM_Scheduler.safeToForceGCs(); } }