001    package edu.rice.cs.cunit.instrumentors.util;
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.util.IPredicate;
008    import edu.rice.cs.cunit.instrumentors.IInstrumentationStrategy;
009    
010    /**
011     * Abstract instrumentation strategy that insert code before an opcode.
012     *
013     * @author Mathias Ricken
014     */
015    public abstract class AInsertAtOpcodeStrategy implements IInstrumentationStrategy {
016        /**
017         * Predicate that decides if this class should be instrumented.
018         */
019        protected IPredicate<ClassFile> _classPredicate;
020    
021        /**
022         * Predicate that decides if this method should be instrumented.
023         */
024        protected IPredicate.Binary<ClassFile, MethodInfo> _methodPredicate;
025    
026        /**
027         * Predicate that decides if code should be inserted BEFORE this opcode.
028         */
029        protected IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> _beforeOpcodePredicate;
030    
031        /**
032         * Predicate that decides if code should be inserted AFTER this opcode.
033         */
034        protected IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> _afterOpcodePredicate;
035    
036        /**
037         * A predicate that always returns false, i.e. never causes code to be inserted.
038         */
039        protected static final IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> OPCODE_NEVER =
040            new IPredicate.Ternary<ClassFile, MethodInfo, InstructionList>() {
041                public Boolean apply(ClassFile classFile, MethodInfo methodInfo, InstructionList instructionList) {
042                    return false; // never instrument
043                }
044            };
045    
046        /**
047         * Create a new strategy with the specified predicates.
048         * @param classPredicate predicate that decides if this class should be instrumented
049         * @param methodPredicate predicate that decides if this method should be instrumented
050         * @param beforeOpcodePredicate predicate that decides if this opcode should be instrumented
051         * @param afterOpcodePredicate predicate that decides if this opcode should be instrumented
052         */
053        protected AInsertAtOpcodeStrategy(IPredicate<ClassFile> classPredicate,
054                                              IPredicate.Binary<ClassFile, MethodInfo> methodPredicate,
055                                              IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> beforeOpcodePredicate,
056                                              IPredicate.Ternary<ClassFile, MethodInfo, InstructionList> afterOpcodePredicate) {
057            _classPredicate = classPredicate;
058            _methodPredicate = methodPredicate;
059            _beforeOpcodePredicate = beforeOpcodePredicate;
060            _afterOpcodePredicate = afterOpcodePredicate;
061        }
062    
063        /**
064         * Instrument the class.
065         *
066         * @param cf class file info
067         */
068        public void instrument(final ClassFile cf) {
069            // check if this is a target class
070            if (((cf.getClassAccessFlags() & ClassFile.ACC_INTERFACE) == 0) && // not an interface
071                (_classPredicate.apply(cf))) { // and class predicate is true
072                for(MethodInfo mi: cf.getMethods()) {
073                    // check if this is a target method
074                    if (((mi.getAccessFlags() & ClassFile.ACC_ABSTRACT) == 0) && // not abstract
075                        ((mi.getAccessFlags() & ClassFile.ACC_NATIVE) == 0) && // not native
076                        (_methodPredicate.apply(cf, mi))) { // and method predicate is true
077                        CodeAttributeInfo codeAttr = mi.getCodeAttributeInfo();
078                        InstructionList il = new InstructionList(codeAttr.getCode());
079    
080                        boolean insertBefore = false;
081                        boolean insertAfter = false;
082                        do {
083                            // check if code should be inserted before the opcode
084                            if (_beforeOpcodePredicate.apply(cf, mi, il)) {
085                                insertBefore = true;
086                                insertInstructionsBefore(cf, mi, il);
087                            }
088                            // check if code should be inserted after the opcode
089                            if (_afterOpcodePredicate.apply(cf, mi, il)) {
090                                insertAfter = true;
091                                insertInstructionsAfter(cf, mi, il);
092                            }
093                        } while(il.advanceIndex());
094    
095                        boolean insertEndOfMethod = insertEndOfMethod(cf, mi, il, insertBefore, insertAfter);
096                        
097                        if (insertBefore || insertAfter || insertEndOfMethod) {
098                            codeAttr.setCode(il.getCode());
099                            modifyStackAndLocals(cf, mi, il, insertBefore, insertAfter, insertEndOfMethod);
100                        }
101                    }
102                }
103            }
104        }
105    
106        /**
107         * Insert instructions before.
108         * NOTE: The method is expected to move back exactly to the instruction it was on.
109         * If it moves to a place before the instruction, an infinite loop may occur.
110         * If it moves to a place after the instruction, future inserts may not occur.
111         * @param cf class file
112         * @param mi method information
113         * @param il instruction list
114         */
115        public abstract void insertInstructionsBefore(final ClassFile cf, final MethodInfo mi, final InstructionList il);
116    
117        /**
118         * Insert instructions after and advance past the instruction.
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         */
126        public abstract void insertInstructionsAfter(final ClassFile cf, final MethodInfo mi, final InstructionList il);
127    
128        /**
129         * Insert instructions at the end of the method.
130         * NOTE: The method is expected to move exactly to the last instruction that was inserted.
131         * If it moves to a place before the instruction, an infinite loop may occur.
132         * If it moves to a place after the instruction, future inserts may not occur.
133         * @param cf class file
134         * @param mi method information
135         * @param il instruction list
136         * @param insertBefore true if code was inserted before an opcode
137         * @param insertAfter true if code was inserted after an opcode
138         * @return true if code was inserted
139         */
140        public abstract boolean insertEndOfMethod(final ClassFile cf, final MethodInfo mi, final InstructionList il,
141                                                  boolean insertBefore, boolean insertAfter);
142    
143        /**
144         * Modify the stack size and number of local variables so that the added instructions can execute.
145         * Called only once per method.
146         * @param cf class file
147         * @param mi method information
148         * @param il instruction list
149         * @param insertBefore true if code was inserted before an opcode
150         * @param insertAfter true if code was inserted after an opcode
151         * @param insertEndOfMethod true if code was inserted at the end of the method
152         */
153        public abstract void modifyStackAndLocals(final ClassFile cf, final MethodInfo mi, final InstructionList il,
154                                                  boolean insertBefore, boolean insertAfter, boolean insertEndOfMethod);
155    
156        /**
157         * Instrumentation of all classes is done.
158         */
159        public void done() {
160            // nothing to do
161        }
162    }