001    package edu.rice.cs.cunit.instrumentors.delay;
002    
003    import edu.rice.cs.cunit.instrumentors.util.AInsertAtOpcodeStrategy;
004    import edu.rice.cs.cunit.classFile.ClassFile;
005    import edu.rice.cs.cunit.classFile.ClassFileTools;
006    import edu.rice.cs.cunit.classFile.MethodInfo;
007    import edu.rice.cs.cunit.classFile.constantPool.MethodPoolInfo;
008    import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor;
009    import edu.rice.cs.cunit.classFile.code.InstructionList;
010    import edu.rice.cs.cunit.classFile.code.Opcode;
011    import edu.rice.cs.cunit.classFile.code.instructions.ReferenceInstruction;
012    import edu.rice.cs.cunit.classFile.attributes.CodeAttributeInfo;
013    import edu.rice.cs.cunit.util.IPredicate;
014    
015    /**
016     * Instrumentation strategy that adds a delay before calls to Object.wait.
017     *
018     * @author Mathias Ricken
019     */
020    public class CallOnObjectWaitStrategy extends AInsertAtOpcodeStrategy {
021        /**
022         * Name of the class containing the method to be called.
023         */
024        protected String _className;
025    
026        /**
027         * Name of the method to be called.
028         */
029        protected String _methodName;
030            
031        /**
032         * Name of the class containing the pre-enabled flag.
033         */
034        protected String _preVarClassName;
035    
036        /**
037         * Name of the pre-enabled flag.
038         */
039        protected String _preVarName;
040            
041        /**
042         * Name of the class containing the post-enabled flag.
043         */
044        protected String _postVarClassName;
045    
046        /**
047         * Name of the post-enabled flag.
048         */
049        protected String _postVarName;
050    
051        /**
052         * Create a new strategy.
053         * @param className name of the class containing the method to be called
054         * @param methodName name of the method to be called
055         * @param preVarClassName name of the class containing the pre-enabled flag
056         * @param preVarName name of the pre-enabled flag
057         * @param postVarClassName name of the class containing the post-enabled flag
058         * @param postVarName name of the post-enabled flag
059         */
060        public CallOnObjectWaitStrategy(final String className, String methodName,
061                                        String preVarClassName, String preVarName,
062                                        String postVarClassName, String postVarName) {
063            super(new IPredicate<ClassFile>() {
064                public Boolean apply(ClassFile param) {
065                    // do not instrument the standard Java API
066                    return ClassFileTools.classNameMatches(param.getThisClassName(),
067                                                           "***", // any class, but none of the following
068                                                           "!java.***",
069                                                           "!javax.***",
070                                                           "!com.sun.***",
071                                                           "!edu.rice.cs.cunit.SyncPointBuffer",
072                                                           "!"+className.replace('/','.'),
073                                                           "!sun.***",
074                                                           "!sunw.***");
075                }
076            }, new IPredicate.Binary<ClassFile, MethodInfo>() {
077                public Boolean apply(ClassFile cf, MethodInfo param) {
078                    // instrument all methods
079                    return true;
080                }
081            }, new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() {
082                public Boolean apply(ClassFile cf, MethodInfo mi, InstructionList il) {
083                    if (il.getOpcode() != Opcode.INVOKEVIRTUAL) {
084                        return false;
085                    }
086                    int methodIndex = ((ReferenceInstruction)il.getInstr()).getReference();
087                    MethodPoolInfo mpi = cf.getConstantPoolItem(methodIndex).execute(CheckMethodVisitor.singleton(), null);
088                    return (mpi.getClassInfo().getName().toString().equals("java/lang/Object")) &&
089                           (mpi.getNameAndType().getName().toString().equals("wait")) &&
090                           ((mpi.getNameAndType().getDescriptor().toString().equals("()V")) ||
091                            (mpi.getNameAndType().getDescriptor().toString().equals("(J)V")) ||
092                            (mpi.getNameAndType().getDescriptor().toString().equals("(JI)V")));
093                }
094            }, OPCODE_NEVER);
095            _className = className;
096            _methodName = methodName;
097            _preVarClassName = preVarClassName;
098            _preVarName = preVarName; 
099            _postVarClassName = postVarClassName;
100            _postVarName = postVarName; 
101        }
102    
103        /**
104         * Add instructions to call SyncPointBuffer.randomDelay before calls to Object.wait.
105         * @param cf class file
106         * @param mi method information
107         * @param il instruction list
108         */
109        public void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
110            boolean result;
111    
112            ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0);
113            int getFlagIndex = 0;
114    
115            getFlagIndex = cf.addField(_preVarClassName, _preVarName, "Z", false, (short)0);
116            getFlagInstr.setReference(getFlagIndex);
117            
118            ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
119            int sleepCallIndex = 0;
120    
121            sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V");
122            sleepCallInstr.setReference(sleepCallIndex);
123    
124            // bytecode looks like this:
125            // invokevirtual (java/lang/Object.wait) <-- we are here
126            // ...
127    
128            // insert instructions
129            il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo());
130            result = il.advanceIndex();
131            assert result == true;
132            il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo());
133            result = il.advanceIndex();
134            assert result == true;
135    
136            // bytecode looks like this:
137            // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_PRE_WAIT)
138            // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.delayObjectWait)
139            // invokevirtual (java/lang/Object.wait) <-- we are here
140        }
141    
142        /**
143         * Add instructions to call SyncPointBuffer.randomDelay after calls to Object.wait.
144         * @param cf class file
145         * @param mi method information
146         * @param il instruction list
147         */
148        public void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il) {
149            boolean result;
150    
151            ReferenceInstruction getFlagInstr = new ReferenceInstruction(Opcode.GETSTATIC, (short)0);
152            int getFlagIndex = 0;
153    
154            getFlagIndex = cf.addField(_postVarClassName, _postVarName, "Z", false, (short)0);
155            getFlagInstr.setReference(getFlagIndex);
156            
157            ReferenceInstruction sleepCallInstr = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
158            int sleepCallIndex = 0;
159    
160            sleepCallIndex = cf.addMethodToConstantPool(_className, _methodName, "(Z)V");
161            sleepCallInstr.setReference(sleepCallIndex);
162    
163            // bytecode looks like this:
164            // invokevirtual (java/lang/Object.wait) <-- we are here
165            // ...
166    
167            // advance past the instruction
168            result = il.advanceIndex();
169            assert result == true;
170    
171            // insert instructions
172            il.insertBeforeInstr(getFlagInstr, mi.getCodeAttributeInfo());
173            result = il.advanceIndex();
174            assert result == true;
175            
176            il.insertBeforeInstr(sleepCallInstr, mi.getCodeAttributeInfo());
177    
178            // do not advance past this new instruction, because the postcondition of this method is that we
179            // are exactly before the instruction that followed the original instruction
180    
181            // bytecode looks like this:
182            // ...
183            // invokevirtual (java/lang/Object.wait)
184            // getstatic (edu/rice/cs/cunit/SyncPointBuffer.RANDOM_DELAY_THREAD_POST_WAIT)
185            // invokestatic (edu/rice/cs/cunit/SyncPointBuffer.randomDelay) <-- we are here
186        }
187        
188        /**
189         * Insert instructions at the end of the method.
190         * NOTE: The method is expected to move exactly to the last instruction that was inserted.
191         * If it moves to a place before the instruction, an infinite loop may occur.
192         * If it moves to a place after the instruction, future inserts may not occur.
193         * @param cf class file
194         * @param mi method information
195         * @param il instruction list
196         * @param insertBefore true if code was inserted before an opcode
197         * @param insertAfter true if code was inserted after an opcode
198         * @return true if code was inserted
199         */
200        public boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
201                                         boolean insertBefore, boolean insertAfter) {
202            return false;
203        }
204    
205        /**
206         * Modify the stack size and number of local variables so that the added instructions can execute. Called only once
207         * per method.
208         *
209         * @param cf           class file
210         * @param mi           method information
211         * @param il           instruction list
212         * @param insertBefore true if code was inserted before an opcode
213         * @param insertAfter  true if code was inserted after an opcode
214         * @param insertEndOfMethod true if code was inserted at the end of the method
215         */
216        public void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il, boolean insertBefore,
217                                         boolean insertAfter, boolean insertEndOfMethod) {
218            // call to a static method with one parameters, one stack space required
219            // make sure we have at least one slot on the stack
220            CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
221            cProps.maxStack = Math.max(1, cProps.maxStack);
222            mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
223        }
224    }