001    package edu.rice.cs.cunit.instrumentors.delay;
002    
003    import edu.rice.cs.cunit.classFile.ClassFile;
004    import edu.rice.cs.cunit.classFile.MethodInfo;
005    import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
006    import edu.rice.cs.cunit.classFile.code.InstructionList;
007    import edu.rice.cs.cunit.classFile.code.Opcode;
008    import edu.rice.cs.cunit.classFile.code.instructions.GenericInstruction;
009    import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
010    import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
011    import edu.rice.cs.cunit.instrumentors.util.AInsertAtOpcodeStrategy;
012    import edu.rice.cs.cunit.util.IPredicate;
013    
014    /**
015     * Instrumentation strategy that adds code to java.lang.Thread.exit to call SyncPointBuffer.delayThreadExit.
016     *
017     * @author Mathias Ricken
018     */
019    public class CallOnThreadExitStrategy extends AInsertAtOpcodeStrategy {
020        /**
021         * Name of the class containing the method to be called.
022         */
023        protected String _className;
024    
025        /**
026         * Name of the method to be called.
027         */
028        protected String _methodName;
029            
030        /**
031         * Name of the class containing the enabled flag.
032         */
033        protected String _varClassName;
034    
035        /**
036         * Name of the enabled flag.
037         */
038        protected String _varName;
039    
040        /**
041         * Create a new strategy.
042         * @param className name of the class containing the method to be called
043         * @param methodName name of the method to be called
044         * @param varClassName name of the class containing the enabled flag
045         * @param varName name of the enabled flag
046         */
047        public CallOnThreadExitStrategy(String className, String methodName, String varClassName, String varName) {
048            super(new IPredicate<ClassFile>() {
049                public Boolean apply(ClassFile param) {
050                    return param.getThisClassName().equals("java.lang.Thread");
051                }
052            }, new IPredicate.Binary<ClassFile, MethodInfo>() {
053                public Boolean apply(ClassFile cf, MethodInfo param) {
054                    return param.getName().toString().equals("exit");
055                }
056            }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() {
057                public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) {
058                    return Opcode.isReturn(il.getOpcode()) /*|| (il.getOpcode()==Opcode.ATHROW)*/;
059                }
060            }, OPCODE_NEVER);
061            _className = className;
062            _methodName = methodName;
063            _varClassName = varClassName;
064            _varName = varName; 
065        }
066    
067        /**
068         * Add instructions to call SyncPointBuffer.delayThreadExit.
069         * @param cf class file
070         * @param mi method information
071         * @param il instruction list
072         */
073        public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
074            boolean result;
075            
076            ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0);
077            int getFlagIndex = 0;
078    
079            getFlagIndex = cf.addField(_varClassName, _varName, "Z", false, (short)0);
080            getFlagInstr.setReference(getFlagIndex);
081    
082            ReferenceInstruction exitCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
083            int exitCallIndex = 0;
084    
085            exitCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V");
086            exitCallInstr.setReference(exitCallIndex);
087    
088            // bytecode looks like this:
089            // ?ret <-- we are here
090            // ...
091    
092            // insert instructions
093            il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo());
094            result = il.advanceIndex();
095            assert result == true;
096            il.insertBeforeInstr(exitCallInstr, mi.getCodeAttributeInfo());
097            result = il.advanceIndex();
098            assert result == true;
099    
100            // bytecode looks like this:
101            // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_EXIT)
102            // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.delayThreadExit)
103            // ?ret <-- we are here
104        }
105    
106        /**
107         * Insert instructions after.
108         *
109         * @param cf class file
110         * @param mi method information
111         * @param il instruction list
112         */
113        public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
114            // never going to be called
115        }
116        
117        /**
118         * Insert instructions at the end of the method.
119         * NOTE: The method is expected to move exactly to the last instruction that was inserted.
120         * If it moves to a place before the instruction, an infinite loop may occur.
121         * If it moves to a place after the instruction, future inserts may not occur.
122         * @param cf class file
123         * @param mi method information
124         * @param il instruction list
125         * @param insertBefore true if code was inserted before an opcode
126         * @param insertAfter true if code was inserted after an opcode
127         * @return true if code was inserted
128         */
129        public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
130                                         boolean insertBefore, boolean insertAfter) {
131            // insert exception handler
132            int exceptionEndPC = il.getPCFromIndex(il.getIndex()-1);
133            int exceptionHandlerPC = il.getPCFromIndex(il.getIndex());
134            insertInstructionsBefore(cf,mi,il);
135    
136            // rethrow
137            CodeAttributeInfo codeAttr = mi.getCodeAttributeInfo();
138            il.insertInstr(new GenericInstruction(new byte[] {Opcode.ATHROW}), codeAttr);
139            // since we just inserted an instruction, we must be able to advance the PC
140            boolean result = il.advanceIndex();
141            assert result;
142    
143            // update exceptions list
144            CodeAttributeInfo.ExceptionTableEntry[] excTable =
145                new CodeAttributeInfo.ExceptionTableEntry[codeAttr.getExceptionTableEntries().length+1];
146            System.arraycopy(codeAttr.getExceptionTableEntries(), 0, excTable, 0, codeAttr.getExceptionTableEntries().length);
147            excTable[excTable.length-1] = new CodeAttributeInfo.ExceptionTableEntry((short)0,
148                                                                                    exceptionEndPC,
149                                                                                    exceptionHandlerPC,
150                                                                                    (short)0);
151            codeAttr.setExceptionTableEntries(excTable);
152            
153            return true;
154        }
155    
156        /**
157         * Modify the stack size and number of local variables so that the added instructions can execute. Called only once
158         * per method.
159         *
160         * @param cf           class file
161         * @param mi           method information
162         * @param il           instruction list
163         * @param insertBefore true if code was inserted before an opcode
164         * @param insertAfter  true if code was inserted after an opcode
165         * @param insertEndOfMethod true if code was inserted at the end of the method
166         */
167        public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore,
168                                         boolean insertAfter, boolean insertEndOfMethod) {
169            // call to a static method with one parameters, one stack space required
170            // make sure we have at least one slot on the stack
171            CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
172            cProps.maxStack = Math.max(1, cProps.maxStack);
173            mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
174        }
175    
176        /**
177         * Instrumentation of all classes is done.
178         */
179        public void done() {
180            // nothing to do
181        }
182    }