1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
47
48
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)
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
96
97
98
99
100
101
102
103
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
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
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
150 int n1 = a.getIndex(ins);
151 if (ins.getOpcode() == DUP) {
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) {
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);
177 boolean requireDup = false;
178 if (producer == DUP) {
179 instructions.remove(node2);
180 requireDup = true;
181 } else if (producer == DUP_X1) {
182 instructions.remove(node2);
183 instructions.remove(node3);
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
194 if (args.length == 0) {
195 instructions.add(nm++, node1);
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);
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;
209 } else {
210 instructions.add(nm++, new InsnNode(SWAP));
211 }
212 continue;
213 }
214
215
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);
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;
224 } else {
225 instructions.add(nm++, new InsnNode(DUP_X2));
226 instructions.add(nm++, new InsnNode(POP));
227 updateMaxStack = updateMaxStack < 1 ? 1 : updateMaxStack;
228 }
229 continue;
230 }
231
232
233
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);
244 if (requireDup) {
245 instructions.add(nm++, new InsnNode(DUP));
246 }
247
248
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
254 if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
255 updateMaxStack = updateMaxStack < 1 ? 1 : updateMaxStack;
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
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 }