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.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   * ContinuationMethodAdapter
35   * 
36   * @author Evgueni Koulechov
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          // verify if restoring
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          // switch cases
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             // locals
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                     // TODO ??
109                 } else if (value == BasicValue.RETURNADDRESS_VALUE) {
110                     // TODO ??
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             // stack
133             int argSize = Type.getArgumentTypes(mnode.desc).length;
134             int ownerSize = mnode.getOpcode() == INVOKESTATIC ? 0 : 1;  // TODO
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                     // TODO ??
143                 } else if (value == BasicValue.RETURNADDRESS_VALUE) {
144                     // TODO ??
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             // Create null types for the parameters of the method invocation
163             Type[] paramTypes = Type.getArgumentTypes(mnode.desc);
164             for (int j = 0; j < paramTypes.length; j++) {
165                 pushDefault(paramTypes[j]);
166             }
167 
168             // continue to the next method
169             mv.visitJumpInsn(GOTO, frameLabel);
170         }
171 
172         // end of start block
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             // save stack
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;  // TODO
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                     // TODO ??
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);  // dummy stack entry
222                         mv.visitInsn(DUP2_X2);  // swap2 for long/double
223                         mv.visitInsn(POP2);
224                         mv.visitMethodInsn(INVOKEVIRTUAL, STACK_RECORDER, getPushMethod(type), "(" + type.getDescriptor() + ")V");
225                         mv.visitInsn(POP);  // remove dummy stack entry
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             // save locals
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                     // no need to save null
245                 } else if (value == BasicValue.UNINITIALIZED_VALUE) {
246                     // no need to save uninitialized objects
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);  // TODO optimize to iconst_0...
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",  // 0 void
308         "Int",     // 1 boolean
309         "Int",     // 2 char
310         "Int",     // 3 byte
311         "Int",     // 4 short
312         "Int",     // 5 int
313         "Float",   // 6 float
314         "Long",    // 7 long
315         "Double",  // 8 double
316         "Object",  // 9 array
317         "Object",  // 10 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