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 }