Coverage Report - org.apache.commons.javaflow.bytecode.transformation.asm.ContinuationMethodAnalyzer
 
Classes in this File Line Coverage Branch Coverage Complexity
ContinuationMethodAnalyzer
0%
0/130
0%
0/84
6.5
ContinuationMethodAnalyzer$1
0%
0/6
0%
0/2
6.5
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.javaflow.bytecode.transformation.asm;
 18  
 
 19  
 import java.util.ArrayList;
 20  
 import java.util.HashMap;
 21  
 import java.util.Iterator;
 22  
 import java.util.List;
 23  
 import java.util.Map;
 24  
 import java.util.Set;
 25  
 
 26  
 
 27  
 import org.objectweb.asm.ClassVisitor;
 28  
 import org.objectweb.asm.Label;
 29  
 import org.objectweb.asm.MethodVisitor;
 30  
 import org.objectweb.asm.Opcodes;
 31  
 import org.objectweb.asm.Type;
 32  
 import org.objectweb.asm.tree.AbstractInsnNode;
 33  
 import org.objectweb.asm.tree.InsnNode;
 34  
 import org.objectweb.asm.tree.MethodInsnNode;
 35  
 import org.objectweb.asm.tree.MethodNode;
 36  
 import org.objectweb.asm.tree.VarInsnNode;
 37  
 import org.objectweb.asm.tree.analysis.Analyzer;
 38  
 import org.objectweb.asm.tree.analysis.AnalyzerException;
 39  
 import org.objectweb.asm.tree.analysis.DataflowInterpreter;
 40  
 import org.objectweb.asm.tree.analysis.DataflowValue;
 41  
 import org.objectweb.asm.tree.analysis.Frame;
 42  
 import org.objectweb.asm.tree.analysis.SimpleVerifier;
 43  
 
 44  
 
 45  
 /**
 46  
  * ContinuationMethodAdapter
 47  
  *
 48  
  * @author Evgueni Koulechov
 49  
  */
 50  
 public class ContinuationMethodAnalyzer extends MethodNode implements Opcodes {
 51  
     protected final String className;
 52  
     protected final ClassVisitor cv;
 53  
     protected final MethodVisitor mv;
 54  
 
 55  0
     protected final List labels = new ArrayList();
 56  0
     protected final List nodes = new ArrayList();
 57  0
     protected final List methods = new ArrayList();
 58  
 
 59  
     protected Analyzer analyzer;
 60  
     public int stackRecorderVar;
 61  
 
 62  
 
 63  
     public ContinuationMethodAnalyzer(String className, ClassVisitor cv,
 64  
                                       MethodVisitor mv, int access, String name, String desc, String signature, String[] exceptions) {
 65  0
         super(access, name, desc, signature, exceptions);
 66  0
         this.className = className;
 67  0
         this.cv = cv;
 68  0
         this.mv = mv;
 69  0
     }
 70  
 
 71  
     public void visitMethodInsn(int opcode, String owner, String name, String desc) {
 72  0
         MethodInsnNode mnode = new MethodInsnNode(opcode, owner, name, desc);
 73  0
         if (opcode == INVOKESPECIAL || "<init>".equals(name)) {
 74  0
             methods.add(mnode);
 75  
         }
 76  0
         if (needsFrameGuard(opcode, owner, name, desc) /* && transformer.inScope(owner, name)*/) {
 77  0
             Label label = new Label();
 78  0
             super.visitLabel(label);
 79  0
             labels.add(label);
 80  0
             nodes.add(mnode);
 81  
         }
 82  0
         instructions.add(mnode);
 83  0
     }
 84  
 
 85  
     public void visitEnd() {
 86  0
         if (instructions.size() == 0 || labels.size() == 0) {
 87  0
             accept(mv);
 88  0
             return;
 89  
         }
 90  
 
 91  0
         this.stackRecorderVar = maxLocals;
 92  
         try {
 93  0
             moveNew();
 94  
 
 95  
 //          TraceMethodVisitor mv = new TraceMethodVisitor();
 96  
 //          System.err.println(name + desc);
 97  
 //          for (int j = 0; j < instructions.size(); ++j) {
 98  
 //              ((AbstractInsnNode) instructions.get(j)).accept(mv);
 99  
 //              System.err.print("   " + mv.text.get(j)); // mv.text.get(j));
 100  
 //          }
 101  
 //          System.err.println();
 102  
 
 103  
             // analyzer = new Analyzer(new BasicVerifier());
 104  0
             analyzer = new Analyzer(new SimpleVerifier() {
 105  0
                 protected Class getClass(Type t) {
 106  
                     try {
 107  0
                         if (t.getSort() == Type.ARRAY) {
 108  0
                             return Class.forName(t.getDescriptor().replace('/', '.'), true, Thread.currentThread().getContextClassLoader());
 109  
                         }
 110  0
                         return Class.forName(t.getClassName(), true, Thread.currentThread().getContextClassLoader());
 111  0
                     } catch (ClassNotFoundException e) {
 112  0
                         throw new RuntimeException(e.toString());
 113  
                     }
 114  
                 }
 115  
             });
 116  0
             analyzer.analyze(className, this);
 117  0
             accept(new ContinuationMethodAdapter(this));
 118  
 
 119  0
         } catch (AnalyzerException ex) {
 120  
             // TODO log the error or fail?
 121  0
             ex.printStackTrace();
 122  0
             accept(mv);
 123  
 
 124  0
         }
 125  0
     }
 126  
 
 127  
     void moveNew() throws AnalyzerException {
 128  0
         DataflowInterpreter i = new DataflowInterpreter();
 129  0
         Analyzer a = new Analyzer(i);
 130  0
         a.analyze(className, this);
 131  
 
 132  0
         HashMap movable = new HashMap();
 133  
 
 134  0
         Frame[] frames = a.getFrames();
 135  0
         for (int j = 0; j < methods.size(); j++) {
 136  0
             MethodInsnNode mnode = (MethodInsnNode) methods.get(j);
 137  
             // require to move NEW instruction
 138  0
             int n = a.getIndex(mnode);
 139  0
             Frame f = frames[n];
 140  0
             Type[] args = Type.getArgumentTypes(mnode.desc);
 141  
 
 142  0
             DataflowValue v = (DataflowValue) f.getStack(f.getStackSize() - args.length - 1);
 143  0
             Set insns = v.insns;
 144  0
             for (Iterator it = insns.iterator(); it.hasNext();) {
 145  0
                 AbstractInsnNode ins = (AbstractInsnNode) it.next();
 146  0
                 if (ins.getOpcode() == NEW) {
 147  0
                     movable.put(ins, mnode);
 148  0
                 } else {
 149  
                     // other known patterns
 150  0
                     int n1 = a.getIndex(ins);
 151  0
                     if (ins.getOpcode() == DUP) { // <init> with params
 152  0
                         AbstractInsnNode ins1 = (AbstractInsnNode) instructions.get(n1 - 1);
 153  0
                         if (ins1.getOpcode() == NEW) {
 154  0
                             movable.put(ins1, mnode);
 155  
                         }
 156  0
                     } else if (ins.getOpcode() == SWAP) {  // in exception handler
 157  0
                         AbstractInsnNode ins1 = (AbstractInsnNode) instructions.get(n1 - 1);
 158  0
                         AbstractInsnNode ins2 = (AbstractInsnNode) instructions.get(n1 - 2);
 159  0
                         if (ins1.getOpcode() == DUP_X1 && ins2.getOpcode() == NEW) {
 160  0
                             movable.put(ins2, mnode);
 161  
                         }
 162  
                     }
 163  
                 }
 164  0
             }
 165  
         }
 166  
 
 167  0
         int updateMaxStack = 0;
 168  0
         for (Iterator it = movable.entrySet().iterator(); it.hasNext();) {
 169  0
             Map.Entry e = (Map.Entry) it.next();
 170  0
             AbstractInsnNode node1 = (AbstractInsnNode) e.getKey();
 171  0
             int n1 = instructions.indexOf(node1);
 172  0
             AbstractInsnNode node2 = (AbstractInsnNode) instructions.get(n1 + 1);
 173  0
             AbstractInsnNode node3 = (AbstractInsnNode) instructions.get(n1 + 2);
 174  0
             int producer = node2.getOpcode();
 175  
 
 176  0
             instructions.remove(node1);  // NEW
 177  0
             boolean requireDup = false;
 178  0
             if (producer == DUP) {
 179  0
                 instructions.remove(node2);  // DUP
 180  0
                 requireDup = true;
 181  0
             } else if (producer == DUP_X1) {
 182  0
                 instructions.remove(node2);  // DUP_X1
 183  0
                 instructions.remove(node3);  // SWAP
 184  0
                 requireDup = true;
 185  
             }
 186  
 
 187  0
             MethodInsnNode mnode = (MethodInsnNode) e.getValue();
 188  0
             int nm = instructions.indexOf(mnode);
 189  
 
 190  0
             int varOffset = stackRecorderVar + 1;
 191  0
             Type[] args = Type.getArgumentTypes(mnode.desc);
 192  
 
 193  
             // optimizations for some common cases
 194  0
             if (args.length == 0) {
 195  0
                 instructions.add(nm++, node1);  // NEW
 196  0
                 if (requireDup) {
 197  0
                     instructions.add(nm++, new InsnNode(DUP));
 198  0
                 }
 199  
                 continue;
 200  
             }
 201  
 
 202  0
             if (args.length == 1 && args[0].getSize() == 1) {
 203  0
                 instructions.add(nm++, node1);  // NEW
 204  0
                 if (requireDup) {
 205  0
                     instructions.add(nm++, new InsnNode(DUP));
 206  0
                     instructions.add(nm++, new InsnNode(DUP2_X1));
 207  0
                     instructions.add(nm++, new InsnNode(POP2));
 208  0
                     updateMaxStack = updateMaxStack < 2 ? 2 : updateMaxStack; // a two extra slots for temp values
 209  0
                 } else {
 210  0
                     instructions.add(nm++, new InsnNode(SWAP));
 211  
                 }
 212  0
                 continue;
 213  
             }
 214  
 
 215  
             // TODO this one untested!
 216  0
             if ((args.length == 1 && args[0].getSize() == 2) ||
 217  
                 (args.length == 2 && args[0].getSize() == 1 && args[1].getSize() == 1)) {
 218  0
                 instructions.add(nm++, node1);  // NEW
 219  0
                 if (requireDup) {
 220  0
                     instructions.add(nm++, new InsnNode(DUP));
 221  0
                     instructions.add(nm++, new InsnNode(DUP2_X2));
 222  0
                     instructions.add(nm++, new InsnNode(POP2));
 223  0
                     updateMaxStack = updateMaxStack < 2 ? 2 : updateMaxStack; // a two extra slots for temp values
 224  0
                 } else {
 225  0
                     instructions.add(nm++, new InsnNode(DUP_X2));
 226  0
                     instructions.add(nm++, new InsnNode(POP));
 227  0
                     updateMaxStack = updateMaxStack < 1 ? 1 : updateMaxStack; // an extra slot for temp value
 228  
                 }
 229  0
                 continue;
 230  
             }
 231  
 
 232  
             // generic code using temporary locals
 233  
             // save stack
 234  0
             for (int j = args.length - 1; j >= 0; j--) {
 235  0
                 Type type = args[j];
 236  0
                 instructions.add(nm++, new VarInsnNode(type.getOpcode(ISTORE), varOffset));
 237  0
                 varOffset += type.getSize();
 238  
             }
 239  0
             if (varOffset > maxLocals) {
 240  0
                 maxLocals = varOffset;
 241  
             }
 242  
 
 243  0
             instructions.add(nm++, node1);  // NEW
 244  0
             if (requireDup) {
 245  0
                 instructions.add(nm++, new InsnNode(DUP));
 246  
             }
 247  
 
 248  
             // restore stack
 249  0
             for (int j = 0; j < args.length; j++) {
 250  0
                 Type type = args[j];
 251  0
                 varOffset -= type.getSize();
 252  0
                 instructions.add(nm++, new VarInsnNode(type.getOpcode(ILOAD), varOffset));
 253  
                 // clean up store to avoid memory leak?
 254  0
                 if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
 255  0
                     updateMaxStack = updateMaxStack < 1 ? 1 : updateMaxStack; // an extra slot for ACONST_NULL
 256  0
                     instructions.add(nm++, new InsnNode(ACONST_NULL));
 257  0
                     instructions.add(nm++, new VarInsnNode(type.getOpcode(ISTORE), varOffset));
 258  
                 }
 259  
             }
 260  0
         }
 261  
 
 262  0
         maxStack += updateMaxStack;
 263  0
     }
 264  
 
 265  
     // TODO
 266  
     boolean needsFrameGuard(int opcode, String owner, String name, String desc) {
 267  0
         if (opcode == Opcodes.INVOKEINTERFACE ||
 268  
             (opcode == Opcodes.INVOKESPECIAL && !"<init>".equals(name)) ||
 269  
             opcode == Opcodes.INVOKESTATIC ||
 270  
             opcode == Opcodes.INVOKEVIRTUAL) {
 271  0
             return true;
 272  
         }
 273  0
         return false;
 274  
     }
 275  
 
 276  
 }