Package com.google.test.metric.asm

Source Code of com.google.test.metric.asm.MethodVisitorBuilder$GetFieldRunnable

/*
* Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.test.metric.asm;

import static com.google.test.metric.asm.SignatureParser.parse;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import com.google.test.metric.ClassInfo;
import com.google.test.metric.ClassRepository;
import com.google.test.metric.FieldInfo;
import com.google.test.metric.FieldNotFoundException;
import com.google.test.metric.JavaType;
import com.google.test.metric.LocalVariableInfo;
import com.google.test.metric.MethodInfo;
import com.google.test.metric.ParameterInfo;
import com.google.test.metric.Type;
import com.google.test.metric.Variable;
import com.google.test.metric.Visibility;
import com.google.test.metric.method.BlockDecomposer;
import com.google.test.metric.method.Constant;
import com.google.test.metric.method.op.stack.ArrayLoad;
import com.google.test.metric.method.op.stack.ArrayStore;
import com.google.test.metric.method.op.stack.Convert;
import com.google.test.metric.method.op.stack.Duplicate;
import com.google.test.metric.method.op.stack.Duplicate2;
import com.google.test.metric.method.op.stack.GetField;
import com.google.test.metric.method.op.stack.Increment;
import com.google.test.metric.method.op.stack.Invoke;
import com.google.test.metric.method.op.stack.Load;
import com.google.test.metric.method.op.stack.MonitorEnter;
import com.google.test.metric.method.op.stack.MonitorExit;
import com.google.test.metric.method.op.stack.MultiANewArrayIns;
import com.google.test.metric.method.op.stack.Pop;
import com.google.test.metric.method.op.stack.RetSub;
import com.google.test.metric.method.op.stack.Return;
import com.google.test.metric.method.op.stack.Store;
import com.google.test.metric.method.op.stack.Swap;
import com.google.test.metric.method.op.stack.Throw;
import com.google.test.metric.method.op.stack.Transform;

public class MethodVisitorBuilder implements MethodVisitor {

  private final ClassInfo classInfo;
  private final String name;
  private final String desc;
  private final Visibility visibility;
  private final Map<Integer, Variable> slots = new HashMap<Integer, Variable>();
  private final BlockDecomposer block = new BlockDecomposer();
  private final List<Runnable> recorder = new ArrayList<Runnable>();
  private final ClassRepository repository;

  private final List<Integer> cyclomaticComplexity = new ArrayList<Integer>();
  private Variable methodThis;
  private int lineNumber;
  private final Map<Label, Integer> lineNumbers = new HashMap<Label, Integer>();
  private int startingLineNumber;
  private final List<ParameterInfo> parameters = new ArrayList<ParameterInfo>();
  private final List<LocalVariableInfo> localVariables = new ArrayList<LocalVariableInfo>();
  private final boolean isFinal;
  private final JavaNamer namer = new JavaNamer();

  public MethodVisitorBuilder(ClassRepository repository, ClassInfo classInfo,
      String name, String desc, String signature, String[] exceptions,
      boolean isStatic, boolean isFinal, Visibility visibility) {
    this.repository = repository;
    this.classInfo = classInfo;
    this.name = name;
    this.desc = desc;
    this.isFinal = isFinal;
    this.visibility = visibility;
    int slot = 0;
    if (!isStatic) {
      Type thisType = JavaType.fromJava(classInfo.getName());
      methodThis = new LocalVariableInfo("this", thisType);
      slots.put(slot++, methodThis);
      localVariables.add((LocalVariableInfo) methodThis);
    }
    for (Type type : parse(desc).getParameters()) {
      ParameterInfo parameterInfo = new ParameterInfo("param_" + slot, type);
      parameters.add(parameterInfo);
      slots.put(slot++, parameterInfo);
      if (JavaType.isDoubleSlot(type)) {
        slot++;
      }
    }
  }

  public void visitJumpInsn(final int opcode, final Label label) {
    if (opcode == Opcodes.GOTO) {
      recorder.add(new Runnable() {
        public void run() {
          block.addOp(new Transform(lineNumber, "GOTO", null, null, null));
          block.unconditionalGoto(label);
        }
      });
    } else if (opcode == Opcodes.JSR) {
      recorder.add(new Runnable() {
        public void run() {
          block.jumpSubroutine(label, lineNumber);
        }
      });
    } else {
      recorder.add(new Runnable() {
        public void run() {
          cyclomaticComplexity.add(lineNumber);
          switch (opcode) {
            case Opcodes.IFEQ :
              if1("IFEQ");
              break;
            case Opcodes.IFNE :
              if1("IFNE");
              break;
            case Opcodes.IFLT :
              if1("IFLT");
              break;
            case Opcodes.IFGE :
              if1("IFGE");
              break;
            case Opcodes.IFGT :
              if1("IFGT");
              break;
            case Opcodes.IFLE :
              if1("IFLE");
              break;
            case Opcodes.IFNONNULL :
              if1("IFNONNULL");
              break;
            case Opcodes.IFNULL :
              if1("IFNULL");
              break;
            case Opcodes.IF_ACMPEQ :
              if2("IF_ACMPEQ");
              break;
            case Opcodes.IF_ACMPNE :
              if2("IF_ACMPNE");
              break;
            case Opcodes.IF_ICMPEQ :
              if2("IF_ICMPEQ");
              break;
            case Opcodes.IF_ICMPGE :
              if2("IF_ICMPGE");
              break;
            case Opcodes.IF_ICMPGT :
              if2("IF_ICMPGT");
              break;
            case Opcodes.IF_ICMPLE :
              if2("IF_ICMPLE");
              break;
            case Opcodes.IF_ICMPLT :
              if2("IF_ICMPLT");
              break;
            case Opcodes.IF_ICMPNE :
              if2("IF_ICMPNE");
              break;
            default :
              throw new UnsupportedOperationException("" + opcode);
          }
          block.conditionalGoto(label);
        }

        private void if1(String name) {
          block.addOp(new Transform(lineNumber, name, JavaType.INT, null, null));
        }

        private void if2(String name) {
          block.addOp(new Transform(lineNumber, name, JavaType.INT, JavaType.INT, null));
        }
      });
    }
  }

  public void visitTryCatchBlock(final Label start, final Label end,
      final Label handler, final String type) {
    recorder.add(new Runnable() {
      public void run() {
        if (type != null) {
          cyclomaticComplexity.add(getLineNumberForLable(handler));
        }
        block.tryCatchBlock(start, end, handler, type);
      }

    });
  }

  private Integer getLineNumberForLable(final Label label) {
    Integer line = lineNumbers.get(label);
    return line == null ? -1 : line;
  }

  public void visitTableSwitchInsn(int min, int max, final Label dflt,
      final Label[] labels) {
    recorder.add(new Runnable() {
      public void run() {
        for (Label label : labels) {
          if (label != dflt) {
            cyclomaticComplexity.add(getLineNumberForLable(label));
          }
        }
        block.addOp(new Pop(lineNumber, 1));
        block.tableSwitch(dflt, labels);
      }
    });
  }

  public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
      final Label[] labels) {
    recorder.add(new Runnable() {
      public void run() {
        for (Label label : labels) {
          if (label != null) {
            cyclomaticComplexity.add(getLineNumberForLable(label));
          }
        }
        block.addOp(new Pop(lineNumber, 1));
        block.tableSwitch(dflt, labels);
      }
    });
  }

  public void visitLocalVariable(String name, String desc, String signature,
      Label start, Label end, int slotNum) {
    Type type = JavaType.fromDesc(desc);
    Variable variable = slots.get(slotNum);
    if (variable == null) {
      LocalVariableInfo localVar = new LocalVariableInfo(name, type);
      slots.put(slotNum, localVar);
      localVariables.add(localVar);
    } else {
      variable.setName(name);
    }
  }

  public void visitLineNumber(final int line, final Label start) {
    lineNumbers.put(start, line);
    recorder.add(new Runnable() { // $6
      public void run() {
        if (lineNumber == 0) {
          startingLineNumber = line;
        }
        lineNumber = line;
      }
    });
  }

  public void visitEnd() {
    for (Runnable runnable : recorder) {
      runnable.run();
    }
    block.decomposeIntoBlocks();
    try {
      String javaName = namer.nameMethod(classInfo.getName(), name, desc);
      boolean isConstructor = name.equals("<init>") || name.equals("<clinit>");
      MethodInfo methodInfo = new MethodInfo(classInfo, javaName, startingLineNumber,
          methodThis, parameters, localVariables, visibility,
          block.getOperations(), isFinal, isConstructor, cyclomaticComplexity);
      classInfo.addMethod(methodInfo);
    } catch (IllegalStateException e) {
      throw new IllegalStateException("Error in " + classInfo + "." + name
          + desc, e);
    }
  }

  public void visitTypeInsn(final int opcode, final String desc) {
    if (desc.length() == 1) {
      throw new IllegalStateException(
          "WARNING! I don't expect primitive types:" + desc);
    }
    final Type type = desc.contains(";") ? JavaType.fromDesc(desc) : JavaType
        .fromJava(desc);
    recorder.add(new Runnable() {
      public void run() {
        switch (opcode) {
          case Opcodes.NEW :
            Constant constant = new Constant("new", type);
            block.addOp(new Load(lineNumber, constant));
            break;
          case Opcodes.NEWARRAY :
          case Opcodes.ANEWARRAY :
            block.addOp(new Transform(lineNumber, "newarray", JavaType.INT, null,
                type.toArray()));
            break;
          case Opcodes.INSTANCEOF :
            block.addOp(new Transform(lineNumber, "instanceof", JavaType.OBJECT,
                null, JavaType.INT));
            break;
          case Opcodes.CHECKCAST :
            block
                .addOp(new Transform(lineNumber, "checkcast", type, null, type));
            break;
          default :
            throw new UnsupportedOperationException("" + opcode);
        }
      }
    });
  }

  public void visitVarInsn(final int opcode, final int var) {
    switch (opcode) {
      case Opcodes.ILOAD :
        load(var, JavaType.INT);
        break;
      case Opcodes.LLOAD :
        load(var, JavaType.LONG);
        break;
      case Opcodes.FLOAD :
        load(var, JavaType.FLOAT);
        break;
      case Opcodes.DLOAD :
        load(var, JavaType.DOUBLE);
        break;
      case Opcodes.ALOAD :
        load(var, JavaType.OBJECT);
        break;

      case Opcodes.ISTORE :
        store(var, JavaType.INT);
        break;
      case Opcodes.LSTORE :
        store(var, JavaType.LONG);
        break;
      case Opcodes.FSTORE :
        store(var, JavaType.FLOAT);
        break;
      case Opcodes.DSTORE :
        store(var, JavaType.DOUBLE);
        break;
      case Opcodes.ASTORE :
        store(var, JavaType.OBJECT);
        break;

      case Opcodes.RET :
        recorder.add(new Runnable() {
          public void run() {
            block.addOp(new RetSub(lineNumber));
          }
        });
        break;
      default :
        throw new UnsupportedOperationException("opcode: " + opcode);
    }
  }

  private void store(final int var, final Type type) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new Store(lineNumber, variable(var, type)));
      }
    });
  }

  private void load(final int var, final Type type) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new Load(lineNumber, variable(var, type)));
      }
    });
  }

  private Variable variable(int varIndex, Type type) {
    Variable variable = slots.get(varIndex);
    if (variable == null) {
      LocalVariableInfo localVar = new LocalVariableInfo("local_" + varIndex, type);
      slots.put(varIndex, localVar);
      localVariables.add(localVar);
      variable = localVar;
    }
    Type varType = variable.getType();
    if (!varType.equals(type) && (type.isPrimitive() || varType.isPrimitive())) {
      // Apparently the compiler reuses local variables and it is possible
      // that the types change. So if types change we have to drop
      // the variable and try again.
      slots.put(varIndex, null);
      return variable(varIndex, type);
    }
    return variable;
  }

  public void visitLabel(final Label label) {
    recorder.add(new Runnable() { // $11
      public void run() {
        block.label(label);
      }
    });
  }

  public void visitLdcInsn(final Object cst) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new Load(lineNumber, new Constant(cst, JavaType.fromClass(cst
            .getClass()))));
      }
    });
  }

  public void visitInsn(final int opcode) {
    switch (opcode) {
      case Opcodes.ACONST_NULL :
        recorder.add(new Runnable() {
          public void run() {
            block.addOp(new Load(lineNumber, new Constant(null, JavaType.OBJECT)));
          }
        });
        break;
      case Opcodes.ICONST_M1 :
      case Opcodes.ICONST_0 :
      case Opcodes.ICONST_1 :
      case Opcodes.ICONST_2 :
      case Opcodes.ICONST_3 :
      case Opcodes.ICONST_4 :
      case Opcodes.ICONST_5 :
        loadConstant(opcode - Opcodes.ICONST_M1 - 1, JavaType.INT);
        break;
      case Opcodes.LCONST_0 :
      case Opcodes.LCONST_1 :
        loadConstant(opcode - Opcodes.LCONST_0, JavaType.LONG);
        break;
      case Opcodes.FCONST_0 :
      case Opcodes.FCONST_1 :
      case Opcodes.FCONST_2 :
        loadConstant(opcode - Opcodes.FCONST_0, JavaType.FLOAT);
        break;
      case Opcodes.DCONST_0 :
      case Opcodes.DCONST_1 :
        loadConstant(opcode - Opcodes.DCONST_0, JavaType.DOUBLE);
        break;
      case Opcodes.IALOAD :
        recordArrayLoad(JavaType.INT);
        break;
      case Opcodes.LALOAD :
        recordArrayLoad(JavaType.LONG);
        break;
      case Opcodes.FALOAD :
        recordArrayLoad(JavaType.FLOAT);
        break;
      case Opcodes.DALOAD :
        recordArrayLoad(JavaType.DOUBLE);
        break;
      case Opcodes.AALOAD :
        recordArrayLoad(JavaType.OBJECT);
        break;
      case Opcodes.BALOAD :
        recordArrayLoad(JavaType.BYTE);
        break;
      case Opcodes.CALOAD :
        recordArrayLoad(JavaType.CHAR);
        break;
      case Opcodes.SALOAD :
        recordArrayLoad(JavaType.SHORT);
        break;

      case Opcodes.IASTORE :
        recordArrayStore(JavaType.INT);
        break;
      case Opcodes.LASTORE :
        recordArrayStore(JavaType.LONG);
        break;
      case Opcodes.FASTORE :
        recordArrayStore(JavaType.FLOAT);
        break;
      case Opcodes.DASTORE :
        recordArrayStore(JavaType.DOUBLE);
        break;
      case Opcodes.AASTORE :
        recordArrayStore(JavaType.OBJECT);
        break;
      case Opcodes.BASTORE :
        recordArrayStore(JavaType.BYTE);
        break;
      case Opcodes.CASTORE :
        recordArrayStore(JavaType.CHAR);
        break;
      case Opcodes.SASTORE :
        recordArrayStore(JavaType.SHORT);
        break;
      case Opcodes.POP :
      case Opcodes.POP2 :
        recorder.add(new Runnable() {
          public void run() {
            block.addOp(new Pop(lineNumber, opcode - Opcodes.POP + 1));
          }
        });
        break;
      case Opcodes.DUP :
      case Opcodes.DUP_X1 :
      case Opcodes.DUP_X2 :
        recorder.add(new Runnable() {
          public void run() {
            int offset = opcode - Opcodes.DUP;
            block.addOp(new Duplicate(lineNumber, offset));
          }
        });
        break;
      case Opcodes.DUP2 :
      case Opcodes.DUP2_X1 :
      case Opcodes.DUP2_X2 :
        recorder.add(new Runnable() {
          public void run() {
            block.addOp(new Duplicate2(lineNumber, opcode - Opcodes.DUP2));
          }
        });
        break;
      case Opcodes.SWAP :
        recorder.add(new Runnable() {
          public void run() {
            block.addOp(new Swap(lineNumber));
          }
        });
        break;
      case Opcodes.IRETURN :
        _return(JavaType.INT);
        break;
      case Opcodes.FRETURN :
        _return(JavaType.FLOAT);
        break;
      case Opcodes.ARETURN :
        _return(JavaType.OBJECT);
        break;
      case Opcodes.LRETURN :
        _return(JavaType.LONG);
        break;
      case Opcodes.DRETURN :
        _return(JavaType.DOUBLE);
        break;
      case Opcodes.ATHROW :
        recorder.add(new Runnable() {
          public void run() {
            block.addOp(new Throw(lineNumber));
          }
        });
        break;
      case Opcodes.RETURN :
        _return(JavaType.VOID);
        break;
      case Opcodes.LCMP :
        operation("cmp", JavaType.LONG, JavaType.LONG, JavaType.INT);
        break;
      case Opcodes.FCMPL :
        operation("cmpl", JavaType.FLOAT, JavaType.FLOAT, JavaType.INT);
        break;
      case Opcodes.FCMPG :
        operation("cmpg", JavaType.FLOAT, JavaType.FLOAT, JavaType.INT);
        break;
      case Opcodes.DCMPL :
        operation("cmpl", JavaType.DOUBLE, JavaType.DOUBLE, JavaType.INT);
        break;
      case Opcodes.DCMPG :
        operation("cmpg", JavaType.DOUBLE, JavaType.DOUBLE, JavaType.INT);
        break;
      case Opcodes.LSHL :
        operation("shl", JavaType.LONG, JavaType.INT, JavaType.LONG);
        break;
      case Opcodes.LSHR :
        operation("shr", JavaType.LONG, JavaType.INT, JavaType.LONG);
        break;
      case Opcodes.LUSHR :
        operation("ushr", JavaType.LONG, JavaType.INT, JavaType.LONG);
        break;
      case Opcodes.LADD :
        operation("add", JavaType.LONG, JavaType.LONG, JavaType.LONG);
        break;
      case Opcodes.LSUB :
        operation("sub", JavaType.LONG, JavaType.LONG, JavaType.LONG);
        break;
      case Opcodes.LDIV :
        operation("div", JavaType.LONG, JavaType.LONG, JavaType.LONG);
        break;
      case Opcodes.LREM :
        operation("rem", JavaType.LONG, JavaType.LONG, JavaType.LONG);
        break;
      case Opcodes.LAND :
        operation("and", JavaType.LONG, JavaType.LONG, JavaType.LONG);
        break;
      case Opcodes.LOR :
        operation("or", JavaType.LONG, JavaType.LONG, JavaType.LONG);
        break;
      case Opcodes.LXOR :
        operation("xor", JavaType.LONG, JavaType.LONG, JavaType.LONG);
        break;
      case Opcodes.LMUL :
        operation("mul", JavaType.LONG, JavaType.LONG, JavaType.LONG);
        break;
      case Opcodes.FADD :
        operation("add", JavaType.FLOAT, JavaType.FLOAT, JavaType.FLOAT);
        break;
      case Opcodes.FSUB :
        operation("sub", JavaType.FLOAT, JavaType.FLOAT, JavaType.FLOAT);
        break;
      case Opcodes.FMUL :
        operation("mul", JavaType.FLOAT, JavaType.FLOAT, JavaType.FLOAT);
        break;
      case Opcodes.FREM :
        operation("rem", JavaType.FLOAT, JavaType.FLOAT, JavaType.FLOAT);
        break;
      case Opcodes.FDIV :
        operation("div", JavaType.FLOAT, JavaType.FLOAT, JavaType.FLOAT);
        break;
      case Opcodes.ISHL :
        operation("shl", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.ISHR :
        operation("shr", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.IUSHR :
        operation("ushr", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.IADD :
        operation("add", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.ISUB :
        operation("sub", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.IMUL :
        operation("mul", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.IDIV :
        operation("div", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.IREM :
        operation("rem", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.IAND :
        operation("and", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.IOR :
        operation("or", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.IXOR :
        operation("xor", JavaType.INT, JavaType.INT, JavaType.INT);
        break;
      case Opcodes.DSUB :
        operation("sub", JavaType.DOUBLE, JavaType.DOUBLE, JavaType.DOUBLE);
        break;
      case Opcodes.DADD :
        operation("add", JavaType.DOUBLE, JavaType.DOUBLE, JavaType.DOUBLE);
        break;
      case Opcodes.DMUL :
        operation("mul", JavaType.DOUBLE, JavaType.DOUBLE, JavaType.DOUBLE);
        break;
      case Opcodes.DDIV :
        operation("div", JavaType.DOUBLE, JavaType.DOUBLE, JavaType.DOUBLE);
        break;
      case Opcodes.DREM :
        operation("rem", JavaType.DOUBLE, JavaType.DOUBLE, JavaType.DOUBLE);
        break;
      case Opcodes.L2I :
        convert(JavaType.LONG, JavaType.INT);
        break;
      case Opcodes.L2F :
        convert(JavaType.LONG, JavaType.FLOAT);
        break;
      case Opcodes.L2D :
        convert(JavaType.LONG, JavaType.DOUBLE);
        break;
      case Opcodes.LNEG :
        operation("neg", JavaType.LONG, null, JavaType.LONG);
        break;
      case Opcodes.F2I :
        convert(JavaType.FLOAT, JavaType.INT);
        break;
      case Opcodes.F2L :
        convert(JavaType.FLOAT, JavaType.LONG);
        break;
      case Opcodes.FNEG :
        operation("neg", JavaType.FLOAT, null, JavaType.FLOAT);
        break;
      case Opcodes.F2D :
        convert(JavaType.FLOAT, JavaType.DOUBLE);
        break;
      case Opcodes.D2I :
        convert(JavaType.DOUBLE, JavaType.INT);
        break;
      case Opcodes.D2L :
        convert(JavaType.DOUBLE, JavaType.LONG);
        break;
      case Opcodes.D2F :
        convert(JavaType.DOUBLE, JavaType.FLOAT);
        break;
      case Opcodes.DNEG :
        operation("neg", JavaType.DOUBLE, null, JavaType.DOUBLE);
        break;
      case Opcodes.I2L :
        convert(JavaType.INT, JavaType.LONG);
        break;
      case Opcodes.I2F :
        convert(JavaType.INT, JavaType.FLOAT);
        break;
      case Opcodes.I2D :
        convert(JavaType.INT, JavaType.DOUBLE);
        break;
      case Opcodes.I2B :
        convert(JavaType.INT, JavaType.BYTE);
        break;
      case Opcodes.I2C :
        convert(JavaType.INT, JavaType.CHAR);
        break;
      case Opcodes.I2S :
        convert(JavaType.INT, JavaType.SHORT);
        break;
      case Opcodes.INEG :
        operation("neg", JavaType.INT, null, JavaType.INT);
        break;
      case Opcodes.ARRAYLENGTH :
        operation("arraylength", JavaType.OBJECT.toArray(), null, JavaType.INT);
        break;
      case Opcodes.MONITORENTER :
        recorder.add(new Runnable() {
          public void run() {
            block.addOp(new MonitorEnter(lineNumber));
          }
        });
        break;
      case Opcodes.MONITOREXIT :
        recorder.add(new Runnable() {
          public void run() {
            block.addOp(new MonitorExit(lineNumber));
          }
        });
        break;
      case Opcodes.NOP:
        recorder.add(new Runnable(){
          public void run() {
            block.addOp(new Transform(lineNumber, "NOP", null, null, null));
          }
        });
    }
  }

  private void operation(final String operation, final Type op1,
      final Type op2, final Type result) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new Transform(lineNumber, operation, op1, op2, result));
      }
    });
  }

  private void convert(final Type from, final Type to) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new Convert(lineNumber, from, to));
      }
    });
  }

  private void _return(final Type type) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new Return(lineNumber, type));
      }
    });
  }

  private void recordArrayLoad(final Type type) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new ArrayLoad(lineNumber, type));
      }
    });
  }

  private void recordArrayStore(final Type type) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new ArrayStore(lineNumber, type));
      }
    });
  }

  private void loadConstant(final int constant, final Type type) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new Load(lineNumber, new Constant(constant, type)));
      }
    });
  }

  public void visitFieldInsn(final int opcode, String owner,
      final String name, final String desc) {
    owner = namer.nameClass(owner);
    switch (opcode) {
      case Opcodes.PUTSTATIC :
          recorder.add(new PutFieldRunnable(repository, owner, name, desc, true));
          break;
      case Opcodes.PUTFIELD :
        recorder.add(new PutFieldRunnable(repository, owner, name, desc, false));
        break;
      case Opcodes.GETSTATIC :
          recorder.add(new GetFieldRunnable(repository, owner, name, desc, true));
          break;
      case Opcodes.GETFIELD :
        recorder.add(new GetFieldRunnable(repository, owner, name, desc, false));
        break;
    }
  }

  public void visitMethodInsn(final int opcode, final String clazz,
      final String name, final String desc) {
    SignatureParser signature = parse(desc);
    final List<Type> params = signature.getParameters();
    final Type returnType = signature.getReturnType();
    recorder.add(new Runnable() {
      public void run() {
        String className = namer.nameClass(clazz);
        block.addOp(new Invoke(lineNumber, className, namer.nameMethod(className, name, desc),
            params, opcode == Opcodes.INVOKESTATIC, returnType));
      }
    });
  }

  public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
    return null;
  }

  public AnnotationVisitor visitAnnotationDefault() {
    return null;
  }

  public void visitAttribute(Attribute arg0) {
  }

  public void visitCode() {
  }

  public void visitFrame(int arg0, int arg1, Object[] arg2, int arg3,
      Object[] arg4) {
  }

  public void visitIincInsn(final int var, final int increment) {
    recorder.add(new Runnable() {
      public void run() {
        Variable variable = variable(var, JavaType.INT);
        block.addOp(new Increment(lineNumber, increment, variable));
      }
    });
  }

  public void visitIntInsn(int opcode, int operand) {
    switch (opcode) {
      case Opcodes.NEWARRAY :
        newArray(operand, toType(operand));
        break;
      case Opcodes.BIPUSH :
        loadConstant(operand, JavaType.INT);
        break;
      case Opcodes.SIPUSH :
        loadConstant(operand, JavaType.INT);
        break;
      default :
        throw new UnsupportedOperationException("Unexpected opcode: " + opcode);
    }
  }

  private Type toType(int operand) {
    switch (operand) {
      case Opcodes.T_BOOLEAN :
        return JavaType.BOOLEAN;
      case Opcodes.T_BYTE :
        return JavaType.BYTE;
      case Opcodes.T_CHAR :
        return JavaType.CHAR;
      case Opcodes.T_DOUBLE :
        return JavaType.DOUBLE;
      case Opcodes.T_FLOAT :
        return JavaType.FLOAT;
      case Opcodes.T_INT :
        return JavaType.INT;
      case Opcodes.T_LONG :
        return JavaType.LONG;
      case Opcodes.T_SHORT :
        return JavaType.SHORT;
      default :
        throw new IllegalArgumentException();
    }
  }

  private void newArray(final int operand, final Type type) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new Transform(lineNumber, "newarray", JavaType.INT, null, type
            .toArray()));
      }
    });
  }

  public void visitMaxs(int maxStack, int maxLocals) {
  }

  public void visitMultiANewArrayInsn(final String clazz, final int dims) {
    recorder.add(new Runnable() {
      public void run() {
        block.addOp(new MultiANewArrayIns(lineNumber, JavaType.fromDesc(clazz),
            dims));
      }
    });
  }

  public AnnotationVisitor visitParameterAnnotation(int arg0, String arg1,
      boolean arg2) {
    return null;
  }

  @Override
  public String toString() {
    return classInfo + "." + name + desc + "\n" + block;
  }

  private class PutFieldRunnable implements Runnable {
    private final String fieldOwner;
    private final String fieldName;
    private final String fieldDesc;
    private final boolean isStatic;
    private final ClassRepository repository;

    public PutFieldRunnable(ClassRepository repository, String owner, String name, String desc,
        boolean isStatic) {
      this.repository = repository;
      this.fieldOwner = owner;
      this.fieldName = name;
      this.fieldDesc = desc;
      this.isStatic = isStatic;
    }

    public void run() {
      FieldInfo field = null;
      ClassInfo ownerClass = repository.getClass(fieldOwner);
      try {
        field = ownerClass.getField(fieldName);
      } catch (FieldNotFoundException e) {
        field =
            new FieldInfo(ownerClass, "FAKE:" + fieldName, JavaType
                .fromDesc(fieldDesc), false, isStatic, false);
      }
      block.addOp(new com.google.test.metric.method.op.stack.PutField(
          lineNumber, field));
    }
  }

  private class GetFieldRunnable implements Runnable {
    private final String fieldOwner;
    private final String fieldName;
    private final String fieldDesc;
    private final boolean isStatic;
    private final ClassRepository repository;

    public GetFieldRunnable(ClassRepository repository, String owner, String name, String desc,
        boolean isStatic) {
      this.repository = repository;
      this.fieldOwner = owner;
      this.fieldName = name;
      this.fieldDesc = desc;
      this.isStatic = isStatic;
    }

    public void run() {
      FieldInfo field = null;
      ClassInfo ownerClass = repository.getClass(fieldOwner);
      try {
        field = ownerClass.getField(fieldName);
      } catch (FieldNotFoundException e) {
        field = new FieldInfo(ownerClass, "FAKE:" + fieldName, JavaType
                .fromDesc(fieldDesc), false, isStatic, false);
      }
      block.addOp(new GetField(lineNumber, field));
    }

  }
}
TOP

Related Classes of com.google.test.metric.asm.MethodVisitorBuilder$GetFieldRunnable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.