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.List;
20
21 import org.apache.commons.javaflow.bytecode.StackRecorder;
22
23 import org.objectweb.asm.Label;
24 import org.objectweb.asm.MethodAdapter;
25 import org.objectweb.asm.Opcodes;
26 import org.objectweb.asm.Type;
27 import org.objectweb.asm.tree.MethodInsnNode;
28 import org.objectweb.asm.tree.analysis.Analyzer;
29 import org.objectweb.asm.tree.analysis.BasicValue;
30 import org.objectweb.asm.tree.analysis.Frame;
31
32
33
34
35
36
37
38 public final class ContinuationMethodAdapter extends MethodAdapter implements Opcodes {
39 private static final String STACK_RECORDER = Type.getInternalName(StackRecorder.class);
40 private static final String POP_METHOD = "pop";
41 private static final String PUSH_METHOD = "push";
42
43 private final Analyzer analyzer;
44 private Label startLabel = new Label();
45 private final List labels;
46 private final List nodes;
47 private final int stackRecorderVar;
48 private final boolean isStatic;
49 private final String methodDesc;
50
51 private int currentIndex = 0;
52 private Frame currentFrame = null;
53
54
55 public ContinuationMethodAdapter(ContinuationMethodAnalyzer a) {
56 super(a.mv);
57 this.analyzer = a.analyzer;
58 this.labels = a.labels;
59 this.nodes = a.nodes;
60 this.stackRecorderVar = a.stackRecorderVar;
61 this.isStatic = (a.access & ACC_STATIC) > 0;
62 this.methodDesc = a.desc;
63 }
64
65 public void visitCode() {
66 mv.visitCode();
67
68 int fsize = labels.size();
69 Label[] restoreLabels = new Label[fsize];
70 for (int i = 0; i < restoreLabels.length; i++) {
71 restoreLabels[i] = new Label();
72 }
73
74
75 Label l0 = new Label();
76
77 mv.visitMethodInsn(INVOKESTATIC, STACK_RECORDER, "get", "()L" + STACK_RECORDER + ";");
78 mv.visitInsn(DUP);
79 mv.visitVarInsn(ASTORE, stackRecorderVar);
80 mv.visitLabel(startLabel);
81
82 mv.visitJumpInsn(IFNULL, l0);
83 mv.visitVarInsn(ALOAD, stackRecorderVar);
84 mv.visitFieldInsn(GETFIELD, STACK_RECORDER, "isRestoring", "Z");
85 mv.visitJumpInsn(IFEQ, l0);
86
87 mv.visitVarInsn(ALOAD, stackRecorderVar);
88 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, POP_METHOD + "Int", "()I");
89 mv.visitTableSwitchInsn(0, fsize - 1, l0, restoreLabels);
90
91
92 for (int i = 0; i < fsize; i++) {
93 Label frameLabel = (Label) labels.get(i);
94 mv.visitLabel(restoreLabels[i]);
95
96 MethodInsnNode mnode = (MethodInsnNode) nodes.get(i);
97
98 Frame frame = analyzer.getFrames()[analyzer.getIndex(mnode)];
99
100
101 int lsize = frame.getLocals();
102 for (int j = lsize - 1; j >= 0; j--) {
103 BasicValue value = (BasicValue) frame.getLocal(j);
104 if (value == null) {
105 mv.visitInsn(ACONST_NULL);
106 mv.visitVarInsn(ASTORE, j);
107 } else if (value == BasicValue.UNINITIALIZED_VALUE) {
108
109 } else if (value == BasicValue.RETURNADDRESS_VALUE) {
110
111 } else {
112 mv.visitVarInsn(ALOAD, stackRecorderVar);
113 Type type = value.getType();
114 if (value.isReference()) {
115 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, POP_METHOD + "Object", "()Ljava/lang/Object;");
116 Type t = value.getType();
117 String desc = t.getDescriptor();
118 if (desc.charAt(0) == '[') {
119 mv.visitTypeInsn(CHECKCAST, desc);
120 } else {
121 mv.visitTypeInsn(CHECKCAST, t.getInternalName());
122 }
123 mv.visitVarInsn(ASTORE, j);
124
125 } else {
126 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, getPopMethod(type), "()" + type.getDescriptor());
127 mv.visitVarInsn(type.getOpcode(ISTORE), j);
128 }
129 }
130 }
131
132
133 int argSize = Type.getArgumentTypes(mnode.desc).length;
134 int ownerSize = mnode.getOpcode() == INVOKESTATIC ? 0 : 1;
135 int initSize = mnode.name.equals("<init>") ? 2 : 0;
136 int ssize = frame.getStackSize();
137 for (int j = 0; j < ssize - argSize - ownerSize - initSize; j++) {
138 BasicValue value = (BasicValue) frame.getStack(j);
139 if (value == null) {
140 mv.visitInsn(ACONST_NULL);
141 } else if (value == BasicValue.UNINITIALIZED_VALUE) {
142
143 } else if (value == BasicValue.RETURNADDRESS_VALUE) {
144
145 } else if (value.isReference()) {
146 mv.visitVarInsn(ALOAD, stackRecorderVar);
147 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, POP_METHOD + "Object", "()Ljava/lang/Object;");
148 mv.visitTypeInsn(CHECKCAST, value.getType().getInternalName());
149 } else {
150 Type type = value.getType();
151 mv.visitVarInsn(ALOAD, stackRecorderVar);
152 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, getPopMethod(type), "()" + type.getDescriptor());
153 }
154 }
155
156 if (mnode.getOpcode() != INVOKESTATIC) {
157 mv.visitVarInsn(ALOAD, stackRecorderVar);
158 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, POP_METHOD + "Reference", "()Ljava/lang/Object;");
159 mv.visitTypeInsn(CHECKCAST, ((BasicValue) frame.getStack(ssize - argSize - 1)).getType().getInternalName());
160 }
161
162
163 Type[] paramTypes = Type.getArgumentTypes(mnode.desc);
164 for (int j = 0; j < paramTypes.length; j++) {
165 pushDefault(paramTypes[j]);
166 }
167
168
169 mv.visitJumpInsn(GOTO, frameLabel);
170 }
171
172
173 mv.visitLabel(l0);
174 }
175
176 public void visitLabel(Label label) {
177 if (currentIndex < labels.size() && label == labels.get(currentIndex)) {
178 int i = analyzer.getIndex(nodes.get(currentIndex));
179 currentFrame = analyzer.getFrames()[i];
180 }
181 mv.visitLabel(label);
182 }
183
184 public void visitMethodInsn(int opcode, String owner, String name, String desc) {
185 mv.visitMethodInsn(opcode, owner, name, desc);
186
187 if (currentFrame != null) {
188 Label fl = new Label();
189
190 mv.visitVarInsn(ALOAD, stackRecorderVar);
191 mv.visitJumpInsn(IFNULL, fl);
192 mv.visitVarInsn(ALOAD, stackRecorderVar);
193 mv.visitFieldInsn(GETFIELD, STACK_RECORDER, "isCapturing", "Z");
194 mv.visitJumpInsn(IFEQ, fl);
195
196
197 Type returnType = Type.getReturnType(desc);
198 boolean hasReturn = returnType != Type.VOID_TYPE;
199 if (hasReturn) {
200 mv.visitInsn(returnType.getSize() == 1 ? POP : POP2);
201 }
202
203 Type[] params = Type.getArgumentTypes(desc);
204 int argSize = params.length;
205 int ownerSize = opcode == INVOKESTATIC ? 0 : 1;
206 int ssize = currentFrame.getStackSize() - argSize - ownerSize;
207 for (int i = 0; i < ssize; i++) {
208 BasicValue value = (BasicValue) currentFrame.getStack(i);
209 if (value == null) {
210 mv.visitInsn(POP);
211 } else if (value == BasicValue.UNINITIALIZED_VALUE) {
212
213 } else if (value.isReference()) {
214 mv.visitVarInsn(ALOAD, stackRecorderVar);
215 mv.visitInsn(SWAP);
216 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, PUSH_METHOD + "Object", "(Ljava/lang/Object;)V");
217 } else {
218 Type type = value.getType();
219 mv.visitVarInsn(ALOAD, stackRecorderVar);
220 if (type.getSize() > 1) {
221 mv.visitInsn(DUP);
222 mv.visitInsn(DUP2_X2);
223 mv.visitInsn(POP2);
224 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, getPushMethod(type), "(" + type.getDescriptor() + ")V");
225 mv.visitInsn(POP);
226 } else {
227 mv.visitInsn(SWAP);
228 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, getPushMethod(type), "(" + type.getDescriptor() + ")V");
229 }
230 }
231 }
232
233 if (!isStatic) {
234 mv.visitVarInsn(ALOAD, stackRecorderVar);
235 mv.visitVarInsn(ALOAD, 0);
236 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, PUSH_METHOD + "Reference", "(Ljava/lang/Object;)V");
237 }
238
239
240 int fsize = currentFrame.getLocals();
241 for (int j = 0; j < fsize; j++) {
242 BasicValue value = (BasicValue) currentFrame.getLocal(j);
243 if (value == null) {
244
245 } else if (value == BasicValue.UNINITIALIZED_VALUE) {
246
247 } else if (value.isReference()) {
248 mv.visitVarInsn(ALOAD, stackRecorderVar);
249 mv.visitVarInsn(ALOAD, j);
250 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, PUSH_METHOD + "Object", "(Ljava/lang/Object;)V");
251 } else {
252 mv.visitVarInsn(ALOAD, stackRecorderVar);
253 Type type = value.getType();
254 mv.visitVarInsn(type.getOpcode(ILOAD), j);
255 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, getPushMethod(type), "(" + type.getDescriptor() + ")V");
256 }
257 }
258
259 mv.visitVarInsn(ALOAD, stackRecorderVar);
260 mv.visitIntInsn(BIPUSH, currentIndex);
261 mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, "pushInt", "(I)V");
262
263 Type methodReturnType = Type.getReturnType(methodDesc);
264 pushDefault(methodReturnType);
265 mv.visitInsn(methodReturnType.getOpcode(IRETURN));
266 mv.visitLabel(fl);
267
268 currentIndex++;
269 currentFrame = null;
270 }
271 }
272
273
274 public void visitMaxs(int maxStack, int maxLocals) {
275 Label endLabel = new Label();
276 mv.visitLabel(endLabel);
277
278 mv.visitLocalVariable("__stackRecorder", "L" + STACK_RECORDER + ";", null, startLabel, endLabel, stackRecorderVar);
279
280 mv.visitMaxs(0, 0);
281 }
282
283 void pushDefault(Type type) {
284 switch (type.getSort()) {
285 case Type.VOID:
286 break;
287 case Type.DOUBLE:
288 mv.visitInsn(DCONST_0);
289 break;
290 case Type.LONG:
291 mv.visitInsn(LCONST_0);
292 break;
293 case Type.FLOAT:
294 mv.visitInsn(FCONST_0);
295 break;
296 case Type.OBJECT:
297 case Type.ARRAY:
298 mv.visitInsn(ACONST_NULL);
299 break;
300 default:
301 mv.visitInsn(ICONST_0);
302 break;
303 }
304 }
305
306 private static String[] SUFFIXES = {
307 "Object",
308 "Int",
309 "Int",
310 "Int",
311 "Int",
312 "Int",
313 "Float",
314 "Long",
315 "Double",
316 "Object",
317 "Object",
318 };
319
320
321 String getPopMethod(Type type) {
322 return POP_METHOD + SUFFIXES[type.getSort()];
323 }
324
325 String getPushMethod(Type type) {
326 return PUSH_METHOD + SUFFIXES[type.getSort()];
327 }
328
329 }
330