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.code.InstructionList;
006    import edu.rice.cs.cunit.classFile.code.Opcode;
007    import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
008    import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
009    import edu.rice.cs.cunit.classFile.constantPool.MethodPoolInfo;
010    import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor;
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.start to call a static void method without parameters.
016     *
017     * @author Mathias Ricken
018     */
019    public class CallOnThreadStartStrategy 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 CallOnThreadStartStrategy(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("start");
055                }
056            }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() {
057                public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) {
058                    if (il.getOpcode() != Opcode.INVOKESPECIAL) {
059                        return false;
060                    }
061                    int methodIndex = ((ReferenceInstruction)il.getInstr()).getReference();
062                    MethodPoolInfo mpi = cf.getConstantPoolItem(methodIndex).execute(CheckMethodVisitor.singleton(), null);
063                    return (mpi.getClassInfo().getName().toString().equals("java/lang/Thread")) &&
064                           (mpi.getNameAndType().getName().toString().equals("start0")) &&
065                           (mpi.getNameAndType().getDescriptor().toString().equals("()V"));
066                }
067            }, OPCODE_NEVER);
068            _className = className;
069            _methodName = methodName;
070            _varClassName = varClassName;
071            _varName = varName;  
072        }
073    
074        /**
075         * Add instructions to call SyncPointBuffer.delayThreadStart.
076         * @param cf class file
077         * @param mi method information
078         * @param il instruction list
079         */
080        public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
081            boolean result;
082    
083            ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0);
084            int getFlagIndex = 0;
085    
086            getFlagIndex = cf.addField(_varClassName, _varName, "Z", false, (short)0);
087            getFlagInstr.setReference(getFlagIndex);
088    
089            ReferenceInstruction startCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
090            int startCallIndex = 0;
091    
092            startCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V");
093            startCallInstr.setReference(startCallIndex);
094    
095            // bytecode looks like this:
096            // invokespecial (java/lang/Thread/start0) <-- we are here
097            // ...
098            
099            // insert instructions
100            il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo());
101            result = il.advanceIndex();
102            assert result == true;
103            il.insertBeforeInstr(startCallInstr, mi.getCodeAttributeInfo());
104            result = il.advanceIndex();
105            assert result == true;
106    
107            // bytecode looks like this:
108            // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_START)
109            // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.delayThreadStart)
110            // invokespecial (java/lang/Thread/start0) <-- we are here
111        }
112    
113        /**
114         * Insert instructions after.
115         *
116         * @param cf class file
117         * @param mi method information
118         * @param il instruction list
119         */
120        public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
121            // never going to be called
122        }
123        
124        /**
125         * Insert instructions at the end of the method.
126         * NOTE: The method is expected to move exactly to the last instruction that was inserted.
127         * If it moves to a place before the instruction, an infinite loop may occur.
128         * If it moves to a place after the instruction, future inserts may not occur.
129         * @param cf class file
130         * @param mi method information
131         * @param il instruction list
132         * @param insertBefore true if code was inserted before an opcode
133         * @param insertAfter true if code was inserted after an opcode
134         * @return true if code was inserted
135         */
136        public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
137                                         boolean insertBefore, boolean insertAfter) {
138            return false;
139        }
140    
141        /**
142         * Modify the stack size and number of local variables so that the added instructions can execute. Called only once
143         * per method.
144         *
145         * @param cf           class file
146         * @param mi           method information
147         * @param il           instruction list
148         * @param insertBefore true if code was inserted before an opcode
149         * @param insertAfter  true if code was inserted after an opcode
150         * @param insertEndOfMethod true if code was inserted at the end of the method
151         */
152        public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore,
153                                         boolean insertAfter, boolean insertEndOfMethod) {
154            // call to a static method with one parameters, one stack space required
155            // make sure we have at least one slot on the stack
156            CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
157            cProps.maxStack = Math.max(1, cProps.maxStack);
158            mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
159        }
160    }