001 package edu.rice.cs.cunit.instrumentors; 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.util.Types; 010 011 import java.util.ArrayList; 012 013 /** 014 * Instrumentor that turns a synchronized methods into an unsynchronized method with a synchronized block. 015 * <p/> 016 * NOTE: This instrumentation strategy has to be run before SynchronizedBlockStrategy. 017 * 018 * @author Mathias Ricken 019 */ 020 public class SynchronizedMethodToBlockStrategy implements IInstrumentationStrategy { 021 /** 022 * Methods to add. 023 */ 024 protected ArrayList<MethodInfo> _newMethods = new ArrayList<MethodInfo>(); 025 026 /** 027 * Constructor. 028 */ 029 public SynchronizedMethodToBlockStrategy() { 030 // nothing to do 031 } 032 033 /** 034 * Instrument the class. 035 * 036 * @param cf class file info 037 */ 038 public void instrument(ClassFile cf) { 039 //Debug.out.println("Instrumenting synchronized methods"); 040 041 // process methods 042 instrumentSynchronizedMethods(cf); 043 } 044 045 /** 046 * Instrument synchronized methods in this class. Inside a non-native synchronized method, a synchronized 047 * block around the entire code is added, and the method is changed to unsynchronized. 048 * 049 * @param cf class file info 050 */ 051 protected void instrumentSynchronizedMethods(ClassFile cf) { 052 // process all methods in this class 053 byte[] loadCode = new byte[]{Opcode.ALOAD_0}; 054 byte[] loadClassCode = new byte[]{Opcode.LDC_W, 0, 0}; 055 for(MethodInfo mi : cf.getMethods()) { 056 if (0 != (mi.getAccessFlags() & ClassFile.ACC_SYNCHRONIZED)) { 057 // synchronized... 058 if (0 == (mi.getAccessFlags() & ClassFile.ACC_NATIVE)) { 059 // synchronized non-native... 060 if (0 != (mi.getAccessFlags() & ClassFile.ACC_STATIC)) { 061 // synchronized static non-native... 062 //Debug.out.println("Instrumenting synchronized static method..."); 063 064 // instruction to load the class object 065 Types.bytesFromShort(cf.getConstantPool().indexOf(cf.getThisClass()), 066 loadClassCode, 067 1); 068 069 // instrument static method 070 instrumentSynchronizedMethod(cf,mi,loadClassCode, 2); 071 } 072 else { 073 // synchronized non-static non-native... 074 //Debug.out.println("Instrumenting synchronized method..."); 075 076 // instrument static method 077 instrumentSynchronizedMethod(cf,mi,loadCode, 2); 078 } 079 080 // erase the synchronized flag 081 short newFlags = (short)(mi.getAccessFlags() & ~(ClassFile.ACC_SYNCHRONIZED)); 082 mi.setAccessFlags(newFlags); 083 } 084 else { 085 // synchronized native... 086 // can't do it 087 } 088 } 089 } 090 } 091 092 /** 093 * Instrument a synchronized method's code blocks. This turns any method <code> synchronized T method(T t...) { abc(); } </code> 094 * into <code> T method(T t...) { synchronized (?) { abc(); } } </code> 095 * <p/> 096 * 097 * @param cf class file 098 * @param mi method info 099 * @param loadLockCode instructions to load the object that gets locked 100 * @param reqdStackSize required state size 101 */ 102 protected void instrumentSynchronizedMethod(ClassFile cf, 103 MethodInfo mi, 104 byte[] loadLockCode, 105 int reqdStackSize) { 106 CodeAttributeInfo codeAttr = mi.getCodeAttributeInfo(); 107 InstructionList il = new InstructionList(codeAttr.getCode()); 108 GenericInstruction monitorExitInstr = new GenericInstruction(new byte[] {Opcode.MONITOREXIT}); 109 boolean result; 110 111 // insert monitorenter 112 if (0 < loadLockCode.length) { 113 InstructionList loadil = new InstructionList(loadLockCode); 114 do { 115 il.insertBeforeInstr(loadil.getInstr(), codeAttr); 116 // since we just inserted an instruction, we must be able to advance the PC 117 result = il.advanceIndex(); 118 assert result; 119 } while(loadil.advanceIndex()); 120 } 121 il.insertBeforeInstr(new GenericInstruction(new byte[] {Opcode.MONITORENTER}), codeAttr); 122 // since we just inserted an instruction, we must be able to advance the PC 123 result = il.advanceIndex(); 124 assert result; 125 126 int exceptionEndPC = il.getPCFromIndex(il.getIndex()); 127 128 do { 129 if (Opcode.isReturn(il.getOpcode())) { 130 // insert monitorexit 131 if (0 < loadLockCode.length) { 132 InstructionList loadil = new InstructionList(loadLockCode); 133 do { 134 il.insertInstr(loadil.getInstr(), codeAttr); 135 // since we just inserted an instruction, we must be able to advance the PC 136 result = il.advanceIndex(); 137 assert result; 138 } while(loadil.advanceIndex()); 139 } 140 il.insertInstr(monitorExitInstr, codeAttr); 141 // since we just inserted an instruction, we must be able to advance the PC 142 result = il.advanceIndex(); 143 assert result; 144 } 145 exceptionEndPC = il.getPCFromIndex(il.getIndex()); 146 } while(il.advanceIndex()); 147 148 // insert exception handler 149 int exceptionHandlerPC = il.getPCFromIndex(il.getIndex()); 150 if (0 < loadLockCode.length) { 151 InstructionList loadil = new InstructionList(loadLockCode); 152 do { 153 il.insertInstr(loadil.getInstr(), codeAttr); 154 // since we just inserted an instruction, we must be able to advance the PC 155 result = il.advanceIndex(); 156 assert result; 157 } while(loadil.advanceIndex()); 158 } 159 il.insertInstr(monitorExitInstr, codeAttr); 160 // since we just inserted an instruction, we must be able to advance the PC 161 result = il.advanceIndex(); 162 assert result; 163 164 // rethrow 165 il.insertInstr(new GenericInstruction(new byte[] {Opcode.ATHROW}), codeAttr); 166 // since we just inserted an instruction, we must be able to advance the PC 167 result = il.advanceIndex(); 168 assert result; 169 170 // update exceptions list 171 CodeAttributeInfo.ExceptionTableEntry[] excTable = 172 new CodeAttributeInfo.ExceptionTableEntry[codeAttr.getExceptionTableEntries().length+1]; 173 System.arraycopy(codeAttr.getExceptionTableEntries(), 0, excTable, 0, codeAttr.getExceptionTableEntries().length); 174 excTable[excTable.length-1] = new CodeAttributeInfo.ExceptionTableEntry((short)0, 175 exceptionEndPC, 176 exceptionHandlerPC, 177 (short)0); 178 codeAttr.setExceptionTableEntries(excTable); 179 180 codeAttr.setCode(il.getCode()); 181 182 // make sure state size is at least reqdStackSize 183 CodeAttributeInfo.CodeProperties cp = mi.getCodeAttributeInfo().getProperties(); 184 cp.maxStack = (short)Math.max(reqdStackSize, cp.maxStack); 185 mi.getCodeAttributeInfo().setProperties(cp.maxStack, cp.maxLocals); 186 } 187 188 /** 189 * Instrumentation of all classes is done. 190 */ 191 public void done() { 192 // nothing to do 193 } 194 }