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.classFile.code.instructions.ReferenceInstruction;
010    import edu.rice.cs.cunit.classFile.constantPool.MethodPoolInfo;
011    import edu.rice.cs.cunit.classFile.constantPool.visitors.CheckMethodVisitor;
012    
013    /**
014     * Instrumentation strategy to replace static calls to certain marker methods with inlined bytecode.
015     * 1) "invokestatic SyncPointBuffer.monitorEnter" -> "monitorenter".
016     * 2) "invokestatic SyncPointBuffer.monitorExit" -> "monitorexit".
017     * 3) "invokestatic SyncPointBuffer.isOldThread"
018     *    -> "invokestatic java/lang/Thread.currentThread; getfield java/lang/Thread.$$$oldThread$$$"
019     * 4) "invokestatic SyncPointBuffer.setOldThread"
020     *    -> "iconst_1; invokestatic java/lang/Thread.currentThread; putfield java/lang/Thread.$$$oldThread$$$"
021     * @author Mathias Ricken
022     */
023    public class MarkerInlineStrategy implements IInstrumentationStrategy {
024        /**
025         * monitorenter instruction.
026         */
027        private final GenericInstruction _monitorEnter = new GenericInstruction(new byte[]{Opcode.MONITORENTER});
028    
029        /**
030         * monitorexit instruction.
031         */
032        private final GenericInstruction _monitorExit = new GenericInstruction(new byte[]{Opcode.MONITOREXIT});
033    
034        /**
035         * iconst_1 instruction.
036         */
037        private final GenericInstruction _iconst_1 = new GenericInstruction(new byte[]{Opcode.ICONST_1});
038    
039        /**
040         * getfield java/lang/Thread.$$$oldThread$$$ instruction.
041         */
042        final private ReferenceInstruction _getOldThreadFieldInstr = new ReferenceInstruction(Opcode.GETFIELD, (short)0);
043    
044        /**
045         * putfield java/lang/Thread.$$$oldThread$$$ instruction.
046         */
047        final private ReferenceInstruction _putOldThreadFieldInstr = new ReferenceInstruction(Opcode.PUTFIELD, (short)0);
048    
049        /**
050         * invokestatic java/lang/Thread.currentThread()Ljava/lang/Thread; instruction
051         */
052        final private ReferenceInstruction _invokeCurrentThreadInstr
053            = new ReferenceInstruction(Opcode.INVOKESTATIC, (short)0);
054    
055        /**
056         * True if names still have to be added.
057         */
058        private boolean _addNames;
059    
060        /**
061         * Instrument the class.
062         *
063         * @param cf class file info
064         */
065        public void instrument(ClassFile cf) {
066            _addNames = true;
067            for(MethodInfo mi : cf.getMethods()) {
068                if ((mi.getAccessFlags() & (ClassFile.ACC_NATIVE | ClassFile.ACC_ABSTRACT)) == 0) {
069                    // not a native method, should have a body
070                    boolean changed = false;
071                    int stackIncrease = 0;
072                    InstructionList il = new InstructionList(mi.getCodeAttributeInfo().getCode());
073    
074                    // instrument monitorenter
075                    do {
076                        if (il.getOpcode() == Opcode.INVOKESTATIC) {
077                            ReferenceInstruction ri = (ReferenceInstruction)il.getInstr();
078                            MethodPoolInfo ref = cf.getConstantPoolItem(ri.getReference())
079                                    .execute(CheckMethodVisitor.singleton(), null);
080                            if (ref.getClassInfo().getName().toString().equals("edu/rice/cs/cunit/SyncPointBuffer")) {
081                                if ((ref.getNameAndType().getName().toString().equals("monitorEnter")) &&
082                                    (ref.getNameAndType().getDescriptor().toString().equals("(Ljava/lang/Object;)V"))) {
083                                    // insert the instruction while keeping jump targets the same
084                                    il.insertInstr(_monitorEnter, mi.getCodeAttributeInfo());
085                                    boolean ret = il.advanceIndex();
086                                    assert ret; // we inserted an instruction, so we should be able to advance
087                                    il.deleteInstr(mi.getCodeAttributeInfo());
088                                    ret = il.rewindIndex();
089                                    assert ret; // we inserted an instruction, so we should be able to rewind
090                                    changed = true;
091                                }
092                                else if ((ref.getNameAndType().getName().toString().equals("monitorExit")) &&
093                                         (ref.getNameAndType().getDescriptor().toString()
094                                              .equals("(Ljava/lang/Object;)V"))) {
095                                    // insert the instruction while keeping jump targets the same
096                                    il.insertInstr(_monitorExit, mi.getCodeAttributeInfo());
097                                    boolean ret = il.advanceIndex();
098                                    assert ret; // we inserted an instruction, so we should be able to advance
099                                    il.deleteInstr(mi.getCodeAttributeInfo());
100                                    ret = il.rewindIndex();
101                                    assert ret; // we inserted an instruction, so we should be able to rewind
102                                    changed = true;
103                                }
104                                else if ((ref.getNameAndType().getName().toString().equals("isOldThread")) &&
105                                         (ref.getNameAndType().getDescriptor().toString().equals("()Z"))) {
106                                    addNamesIfNecessary(cf);
107                                    // insert the instructions while keeping jump targets the same
108                                    il.insertInstr(_getOldThreadFieldInstr, mi.getCodeAttributeInfo());
109                                    il.insertInstr(_invokeCurrentThreadInstr, mi.getCodeAttributeInfo());
110                                    boolean ret = il.advanceIndex(2);
111                                    assert ret; // we inserted two instructions, so we should be able to advance
112                                    il.deleteInstr(mi.getCodeAttributeInfo());
113                                    ret = il.rewindIndex();
114                                    assert ret; // we inserted instructions, so we should be able to rewind
115                                    changed = true;
116                                    stackIncrease = Math.max(stackIncrease, 1);
117                                }
118                                else if ((ref.getNameAndType().getName().toString().equals("setOldThread")) &&
119                                         (ref.getNameAndType().getDescriptor().toString().equals("()V"))) {
120                                    addNamesIfNecessary(cf);
121                                    // insert the instruction while keeping jump targets the same
122                                    il.insertInstr(_putOldThreadFieldInstr, mi.getCodeAttributeInfo());
123                                    il.insertInstr(_iconst_1, mi.getCodeAttributeInfo());
124                                    il.insertInstr(_invokeCurrentThreadInstr, mi.getCodeAttributeInfo());
125                                    boolean ret = il.advanceIndex(3);
126                                    assert ret; // we inserted three instructions, so we should be able to advance
127                                    il.deleteInstr(mi.getCodeAttributeInfo());
128                                    ret = il.rewindIndex();
129                                    assert ret; // we inserted instructions, so we should be able to rewind
130                                    changed = true;
131                                    stackIncrease = Math.max(stackIncrease, 2);
132                                }
133                            }
134                        }
135                    } while(il.advanceIndex());
136                    if (changed) {
137                        // write code back
138                        mi.getCodeAttributeInfo().setCode(il.getCode());
139    
140                        // make sure we have four more slots on the stack
141                        CodeAttributeInfo.CodeProperties cProps = mi.getCodeAttributeInfo().getProperties();
142                        cProps.maxStack += stackIncrease;
143                        mi.getCodeAttributeInfo().setProperties(cProps.maxStack, cProps.maxLocals);
144                    }
145                }
146            }
147        }
148    
149        /**
150         * Add names to the constant pool if necessary.
151         * @param cf class file
152         */
153        private void addNamesIfNecessary(ClassFile cf) {
154            // see if we need to add the names and constant
155            if (_addNames) {
156                _addNames = false;
157                int oldThreadFieldIndex = cf.addField("java/lang/Thread", "$$$oldThread$$$", "Z", false, (short)0);
158                _getOldThreadFieldInstr.setReference(oldThreadFieldIndex);
159                _putOldThreadFieldInstr.setReference(oldThreadFieldIndex);
160    
161                // add the names for the call to Thread.currentThread()
162                int currentThreadMethodIndex = cf.addMethodToConstantPool("java/lang/Thread",
163                                                                          "currentThread",
164                                                                          "()Ljava/lang/Thread;");
165                _invokeCurrentThreadInstr.setReference(currentThreadMethodIndex);
166            }
167        }
168    
169        /**
170         * Instrumentation of all classes is done.
171         */
172        public void done() {
173            // nothing to do
174        }
175    }