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