001 package edu.rice.cs.cunit.instrumentors.record;
002
003 import edu.rice.cs.cunit.SyncPointBuffer;
004 import edu.rice.cs.cunit.instrumentors.record.CompactRecordBufferCodeStrategy;
005 import edu.rice.cs.cunit.classFile.ClassFile;
006 import edu.rice.cs.cunit.classFile.MethodInfo;
007 import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
008 import edu.rice.cs.cunit.classFile.code.InstructionList;
009 import edu.rice.cs.cunit.classFile.code.Opcode;
010 import edu.rice.cs.cunit.classFile.code.instructions.GenericInstruction;
011 import edu.rice.cs.cunit.util.IPredicate;
012
013 /**
014 * Instrumentation strategy that adds code to java.lang.Thread.exit to call SyncPointBuffer.compactAdd.
015 *
016 * @author Mathias Ricken
017 */
018 public class CompactRecordThreadExitStrategy extends CompactRecordBufferCodeStrategy {
019 /**
020 * Create a new strategy.
021 */
022 public CompactRecordThreadExitStrategy() {
023 super(new IPredicate<ClassFile>() {
024 public Boolean apply(ClassFile param) {
025 return param.getThisClassName().equals("java.lang.Thread");
026 }
027 }, new IPredicate.Binary<ClassFile, MethodInfo>() {
028 public Boolean apply(ClassFile cf, MethodInfo param) {
029 return param.getName().toString().equals("exit");
030 }
031 }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() {
032 public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) {
033 return Opcode.isReturn(il.getOpcode()) /*|| (il.getOpcode()==Opcode.ATHROW)*/;
034 }
035 }, SyncPointBuffer.SP.THREADEXIT.intValue());
036 }
037
038 /**
039 * Insert instructions at the end of the method.
040 * NOTE: The method is expected to move exactly to the last instruction that was inserted.
041 * If it moves to a place before the instruction, an infinite loop may occur.
042 * If it moves to a place after the instruction, future inserts may not occur.
043 * @param cf class file
044 * @param mi method information
045 * @param il instruction list
046 * @param insertBefore true if code was inserted before an opcode
047 * @param insertAfter true if code was inserted after an opcode
048 * @return true if code was inserted
049 */
050 public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
051 boolean insertBefore, boolean insertAfter) {
052 // insert exception handler
053 int exceptionEndPC = il.getPCFromIndex(il.getIndex()-1);
054 int exceptionHandlerPC = il.getPCFromIndex(il.getIndex());
055 insertInstructionsBefore(cf,mi,il);
056
057 // rethrow
058 CodeAttributeInfo codeAttr = mi.getCodeAttributeInfo();
059 il.insertInstr(new GenericInstruction(new byte[] {Opcode.ATHROW}), codeAttr);
060 // since we just inserted an instruction, we must be able to advance the PC
061 boolean result = il.advanceIndex();
062 assert result;
063
064 // update exceptions list
065 CodeAttributeInfo.ExceptionTableEntry[] excTable =
066 new CodeAttributeInfo.ExceptionTableEntry[codeAttr.getExceptionTableEntries().length+1];
067 System.arraycopy(codeAttr.getExceptionTableEntries(), 0, excTable, 0, codeAttr.getExceptionTableEntries().length);
068 excTable[excTable.length-1] = new CodeAttributeInfo.ExceptionTableEntry((short)0,
069 exceptionEndPC,
070 exceptionHandlerPC,
071 (short)0);
072 codeAttr.setExceptionTableEntries(excTable);
073
074 return true;
075 }
076 }