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    }