View Javadoc

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      protected final List labels = new ArrayList();
56      protected final List nodes = new ArrayList();
57      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          super(access, name, desc, signature, exceptions);
66          this.className = className;
67          this.cv = cv;
68          this.mv = mv;
69      }
70  
71      public void visitMethodInsn(int opcode, String owner, String name, String desc) {
72          MethodInsnNode mnode = new MethodInsnNode(opcode, owner, name, desc);
73          if (opcode == INVOKESPECIAL || "<init>".equals(name)) {
74              methods.add(mnode);
75          }
76          if (needsFrameGuard(opcode, owner, name, desc) /* && transformer.inScope(owner, name)*/) {
77              Label label = new Label();
78              super.visitLabel(label);
79              labels.add(label);
80              nodes.add(mnode);
81          }
82          instructions.add(mnode);
83      }
84  
85      public void visitEnd() {
86          if (instructions.size() == 0 || labels.size() == 0) {
87              accept(mv);
88              return;
89          }
90  
91          this.stackRecorderVar = maxLocals;
92          try {
93              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             analyzer = new Analyzer(new SimpleVerifier() {
105                 protected Class getClass(Type t) {
106                     try {
107                         if (t.getSort() == Type.ARRAY) {
108                             return Class.forName(t.getDescriptor().replace('/', '.'), true, Thread.currentThread().getContextClassLoader());
109                         }
110                         return Class.forName(t.getClassName(), true, Thread.currentThread().getContextClassLoader());
111                     } catch (ClassNotFoundException e) {
112                         throw new RuntimeException(e.toString());
113                     }
114                 }
115             });
116             analyzer.analyze(className, this);
117             accept(new ContinuationMethodAdapter(this));
118 
119         } catch (AnalyzerException ex) {
120             // TODO log the error or fail?
121             ex.printStackTrace();
122             accept(mv);
123 
124         }
125     }
126 
127     void moveNew() throws AnalyzerException {
128         DataflowInterpreter i = new DataflowInterpreter();
129         Analyzer a = new Analyzer(i);
130         a.analyze(className, this);
131 
132         HashMap movable = new HashMap();
133 
134         Frame[] frames = a.getFrames();
135         for (int j = 0; j < methods.size(); j++) {
136             MethodInsnNode mnode = (MethodInsnNode) methods.get(j);
137             // require to move NEW instruction
138             int n = a.getIndex(mnode);
139             Frame f = frames[n];
140             Type[] args = Type.getArgumentTypes(mnode.desc);
141 
142             DataflowValue v = (DataflowValue) f.getStack(f.getStackSize() - args.length - 1);
143             Set insns = v.insns;
144             for (Iterator it = insns.iterator(); it.hasNext();) {
145                 AbstractInsnNode ins = (AbstractInsnNode) it.next();
146                 if (ins.getOpcode() == NEW) {
147                     movable.put(ins, mnode);
148                 } else {
149                     // other known patterns
150                     int n1 = a.getIndex(ins);
151                     if (ins.getOpcode() == DUP) { // <init> with params
152                         AbstractInsnNode ins1 = (AbstractInsnNode) instructions.get(n1 - 1);
153                         if (ins1.getOpcode() == NEW) {
154                             movable.put(ins1, mnode);
155                         }
156                     } else if (ins.getOpcode() == SWAP) {  // in exception handler
157                         AbstractInsnNode ins1 = (AbstractInsnNode) instructions.get(n1 - 1);
158                         AbstractInsnNode ins2 = (AbstractInsnNode) instructions.get(n1 - 2);
159                         if (ins1.getOpcode() == DUP_X1 && ins2.getOpcode() == NEW) {
160                             movable.put(ins2, mnode);
161                         }
162                     }
163                 }
164             }
165         }
166 
167         int updateMaxStack = 0;
168         for (Iterator it = movable.entrySet().iterator(); it.hasNext();) {
169             Map.Entry e = (Map.Entry) it.next();
170             AbstractInsnNode node1 = (AbstractInsnNode) e.getKey();
171             int n1 = instructions.indexOf(node1);
172             AbstractInsnNode node2 = (AbstractInsnNode) instructions.get(n1 + 1);
173             AbstractInsnNode node3 = (AbstractInsnNode) instructions.get(n1 + 2);
174             int producer = node2.getOpcode();
175 
176             instructions.remove(node1);  // NEW
177             boolean requireDup = false;
178             if (producer == DUP) {
179                 instructions.remove(node2);  // DUP
180                 requireDup = true;
181             } else if (producer == DUP_X1) {
182                 instructions.remove(node2);  // DUP_X1
183                 instructions.remove(node3);  // SWAP
184                 requireDup = true;
185             }
186 
187             MethodInsnNode mnode = (MethodInsnNode) e.getValue();
188             int nm = instructions.indexOf(mnode);
189 
190             int varOffset = stackRecorderVar + 1;
191             Type[] args = Type.getArgumentTypes(mnode.desc);
192 
193             // optimizations for some common cases
194             if (args.length == 0) {
195                 instructions.add(nm++, node1);  // NEW
196                 if (requireDup) {
197                     instructions.add(nm++, new InsnNode(DUP));
198                 }
199                 continue;
200             }
201 
202             if (args.length == 1 && args[0].getSize() == 1) {
203                 instructions.add(nm++, node1);  // NEW
204                 if (requireDup) {
205                     instructions.add(nm++, new InsnNode(DUP));
206                     instructions.add(nm++, new InsnNode(DUP2_X1));
207                     instructions.add(nm++, new InsnNode(POP2));
208                     updateMaxStack = updateMaxStack < 2 ? 2 : updateMaxStack; // a two extra slots for temp values
209                 } else {
210                     instructions.add(nm++, new InsnNode(SWAP));
211                 }
212                 continue;
213             }
214 
215             // TODO this one untested!
216             if ((args.length == 1 && args[0].getSize() == 2) ||
217                 (args.length == 2 && args[0].getSize() == 1 && args[1].getSize() == 1)) {
218                 instructions.add(nm++, node1);  // NEW
219                 if (requireDup) {
220                     instructions.add(nm++, new InsnNode(DUP));
221                     instructions.add(nm++, new InsnNode(DUP2_X2));
222                     instructions.add(nm++, new InsnNode(POP2));
223                     updateMaxStack = updateMaxStack < 2 ? 2 : updateMaxStack; // a two extra slots for temp values
224                 } else {
225                     instructions.add(nm++, new InsnNode(DUP_X2));
226                     instructions.add(nm++, new InsnNode(POP));
227                     updateMaxStack = updateMaxStack < 1 ? 1 : updateMaxStack; // an extra slot for temp value
228                 }
229                 continue;
230             }
231 
232             // generic code using temporary locals
233             // save stack
234             for (int j = args.length - 1; j >= 0; j--) {
235                 Type type = args[j];
236                 instructions.add(nm++, new VarInsnNode(type.getOpcode(ISTORE), varOffset));
237                 varOffset += type.getSize();
238             }
239             if (varOffset > maxLocals) {
240                 maxLocals = varOffset;
241             }
242 
243             instructions.add(nm++, node1);  // NEW
244             if (requireDup) {
245                 instructions.add(nm++, new InsnNode(DUP));
246             }
247 
248             // restore stack
249             for (int j = 0; j < args.length; j++) {
250                 Type type = args[j];
251                 varOffset -= type.getSize();
252                 instructions.add(nm++, new VarInsnNode(type.getOpcode(ILOAD), varOffset));
253                 // clean up store to avoid memory leak?
254                 if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
255                     updateMaxStack = updateMaxStack < 1 ? 1 : updateMaxStack; // an extra slot for ACONST_NULL
256                     instructions.add(nm++, new InsnNode(ACONST_NULL));
257                     instructions.add(nm++, new VarInsnNode(type.getOpcode(ISTORE), varOffset));
258                 }
259             }
260         }
261 
262         maxStack += updateMaxStack;
263     }
264 
265     // TODO
266     boolean needsFrameGuard(int opcode, String owner, String name, String desc) {
267         if (opcode == Opcodes.INVOKEINTERFACE ||
268             (opcode == Opcodes.INVOKESPECIAL && !"<init>".equals(name)) ||
269             opcode == Opcodes.INVOKESTATIC ||
270             opcode == Opcodes.INVOKEVIRTUAL) {
271             return true;
272         }
273         return false;
274     }
275 
276 }