private Bytecode createAbstractMethodCode(ClassFile file, MethodInformation method)
     throws NotFoundException {
   if ((delegateField != null) && (!Modifier.isPrivate(delegateField.getModifiers()))) {
     // Call the corresponding method directly on the delegate
     Bytecode b = new Bytecode(file.getConstPool());
     int localVariables = MethodUtils.calculateMaxLocals(method.getMethod());
     b.setMaxLocals(localVariables);
     // load the delegate field
     b.addAload(0);
     b.addGetfield(
         file.getName(),
         delegateField.getName(),
         DescriptorUtils.classToStringRepresentation(delegateField.getType()));
     // load the parameters
     BytecodeUtils.loadParameters(b, method.getDescriptor());
     // invoke the delegate method
     b.addInvokeinterface(
         delegateField.getType().getName(),
         method.getName(),
         method.getDescriptor(),
         localVariables);
     // return the value if applicable
     BytecodeUtils.addReturnInstruction(b, method.getReturnType());
     return b;
   } else {
     if (!Modifier.isPrivate(method.getMethod().getModifiers())) {
       // if it is a parameter injection point we need to initalize the
       // injection point then handle the method with the method handler
       return createAbstractMethodHandler(file, method);
     } else {
       // if the delegate is private we need to use the method handler
       return createInterceptorBody(file, method);
     }
   }
 }
 /**
  * calls _initMH on the method handler and then stores the result in the methodHandler field as
  * then new methodHandler
  */
 private Bytecode createMethodHandlerInitializerBody(ClassFile proxyClassType) {
   Bytecode b = new Bytecode(proxyClassType.getConstPool(), 1, 2);
   b.add(Opcode.ALOAD_0);
   StaticMethodInformation methodInfo =
       new StaticMethodInformation(
           "_initMH", new Class[] {Object.class}, void.class, proxyClassType.getName());
   invokeMethodHandler(proxyClassType, b, methodInfo, false, DEFAULT_METHOD_RESOLVER);
   b.addCheckcast("javassist/util/proxy/MethodHandler");
   b.addPutfield(
       proxyClassType.getName(),
       "methodHandler",
       DescriptorUtils.classToStringRepresentation(MethodHandler.class));
   b.add(Opcode.RETURN);
   log.trace("Created MH initializer body for decorator proxy:  " + getBeanType());
   return b;
 }
Esempio n. 3
0
  public BaseClassData(ClassFile file, ClassLoader loader, boolean replaceable) {
    className = file.getName();
    this.replaceable = replaceable;
    internalName = Descriptor.toJvmName(file.getName());
    this.loader = loader;
    superClassName = file.getSuperclass();
    boolean finalMethod = false;
    Set<MethodData> meths = new HashSet<MethodData>();
    for (Object o : file.getMethods()) {
      String methodClassName = className;
      MethodInfo m = (MethodInfo) o;
      MemberType type = MemberType.NORMAL;
      if ((m.getDescriptor().equals(Constants.ADDED_METHOD_DESCRIPTOR)
              && m.getName().equals(Constants.ADDED_METHOD_NAME))
          || (m.getDescriptor().equals(Constants.ADDED_STATIC_METHOD_DESCRIPTOR)
              && m.getName().equals(Constants.ADDED_STATIC_METHOD_NAME))
          || (m.getDescriptor().equals(Constants.ADDED_CONSTRUCTOR_DESCRIPTOR))) {
        type = MemberType.ADDED_SYSTEM;
      } else if (m.getAttribute(Constants.FINAL_METHOD_ATTRIBUTE) != null) {
        finalMethod = true;
      }

      MethodData md =
          new MethodData(
              m.getName(),
              m.getDescriptor(),
              methodClassName,
              type,
              m.getAccessFlags(),
              finalMethod);
      meths.add(md);
    }
    this.methods = Collections.unmodifiableSet(meths);
    Set<FieldData> fieldData = new HashSet<FieldData>();
    for (Object o : file.getFields()) {
      FieldInfo m = (FieldInfo) o;
      MemberType mt = MemberType.NORMAL;
      fieldData.add(new FieldData(m, mt, className, m.getAccessFlags()));
    }
    this.fields = Collections.unmodifiableSet(fieldData);
  }
 /**
  * Client proxies are equal to other client proxies for the same bean.
  *
  * <p>The corresponding java code: <code>
  * return other instanceof MyProxyClassType.class
  * </code>
  */
 @Override
 protected MethodInfo generateEqualsMethod(ClassFile proxyClassType) {
   MethodInfo method =
       new MethodInfo(proxyClassType.getConstPool(), "equals", "(Ljava/lang/Object;)Z");
   method.setAccessFlags(AccessFlag.PUBLIC);
   Bytecode b = new Bytecode(proxyClassType.getConstPool());
   b.addAload(1);
   b.addInstanceof(proxyClassType.getName());
   b.add(Opcode.IRETURN);
   b.setMaxLocals(2);
   b.setMaxStack(1);
   method.setCodeAttribute(b.toCodeAttribute());
   return method;
 }
 @Override
 public void handleChanges(Collection<WatchServiceFileSystemWatcher.FileChangeEvent> changes) {
   try {
     List<AddedClass> addedClasses = new ArrayList<>();
     List<ClassDefinition> changedClasses = new ArrayList<>();
     for (WatchServiceFileSystemWatcher.FileChangeEvent change : changes) {
       if (change.getType() == WatchServiceFileSystemWatcher.FileChangeEvent.Type.ADDED) {
         try (FileInputStream in = new FileInputStream(change.getFile())) {
           byte[] bytes = FileReader.readFileBytes(in);
           ClassFile file = new ClassFile(new DataInputStream(new ByteArrayInputStream(bytes)));
           addedClasses.add(new AddedClass(file.getName(), bytes, classLoader));
         }
       } else if (change.getType()
           == WatchServiceFileSystemWatcher.FileChangeEvent.Type.MODIFIED) {
         String hash = hashes.get(change.getFile().getCanonicalPath());
         if (hash == null) {
           // class is not loaded yet
           continue;
         }
         try (FileInputStream in = new FileInputStream(change.getFile())) {
           byte[] bytes = FileReader.readFileBytes(in);
           if (!hash.equals(MD5.md5(bytes))) {
             ClassFile file =
                 new ClassFile(new DataInputStream(new ByteArrayInputStream(bytes)));
             changedClasses.add(
                 new ClassDefinition(classLoader.loadClass(file.getName()), bytes));
           }
         }
       }
     }
     Agent.redefine(
         changedClasses.toArray(new ClassDefinition[changedClasses.size()]),
         addedClasses.toArray(new AddedClass[addedClasses.size()]));
   } catch (Exception e) {
     e.printStackTrace();
   }
 }
 private void addHandlerInitializerMethod(ClassFile proxyClassType) throws Exception {
   StaticMethodInformation methodInfo =
       new StaticMethodInformation(
           "_initMH",
           new Class[] {Object.class},
           void.class,
           proxyClassType.getName(),
           Modifier.PRIVATE);
   proxyClassType.addMethod(
       MethodUtils.makeMethod(
           methodInfo,
           new Class[] {},
           createMethodHandlerInitializerBody(proxyClassType),
           proxyClassType.getConstPool()));
 }
 protected void fillSupers(ClassFile clazz) {
   String name = clazz.getName();
   List<String> list = supers.get(name);
   if (list == null) {
     list = new ArrayList<>();
     supers.put(name, list);
   }
   if (clazz.isInterface()) {
     Collections.addAll(list, clazz.getInterfaces());
   } else {
     String superclass = clazz.getSuperclass();
     if (superclass != null) {
       list.add(superclass);
     }
   }
 }
 /** Client proxies use the following hashCode: <code>MyProxyName.class.hashCode()</code> */
 @Override
 protected MethodInfo generateHashCodeMethod(ClassFile proxyClassType) {
   MethodInfo method = new MethodInfo(proxyClassType.getConstPool(), "hashCode", "()I");
   method.setAccessFlags(AccessFlag.PUBLIC);
   Bytecode b = new Bytecode(proxyClassType.getConstPool());
   // MyProxyName.class.hashCode()
   int classLocation = proxyClassType.getConstPool().addClassInfo(proxyClassType.getName());
   b.addLdc(classLocation);
   // now we have the class object on top of the stack
   b.addInvokevirtual("java.lang.Object", "hashCode", "()I");
   // now we have the hashCode
   b.add(Opcode.IRETURN);
   b.setMaxLocals(1);
   b.setMaxStack(1);
   method.setCodeAttribute(b.toCodeAttribute());
   return method;
 }
  /**
   * add a bogus constructor call to a bytecode sequence so a constructor can pass bytecode
   * validation
   *
   * @param bytecode
   */
  public static boolean addBogusConstructorCall(ClassFile file, Bytecode code) {
    MethodInfo constructorToCall = null;
    for (Object meth : file.getMethods()) {
      MethodInfo m = (MethodInfo) meth;
      if (m.getName().equals("<init>")) {
        constructorToCall = m;
        break;
      }
    }
    if (constructorToCall == null) {
      return false;
    }
    // push this onto the stack
    code.add(Bytecode.ALOAD_0);

    String[] params =
        DescriptorUtils.descriptorStringToParameterArray(constructorToCall.getDescriptor());
    for (String p : params) {
      // int char short boolean byte
      if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) {
        // push integer 0
        code.add(Opcode.ICONST_0);
      }
      // long
      else if (p.equals("J")) {
        code.add(Opcode.LCONST_0);
      }
      // double
      else if (p.equals("D")) {
        code.add(Opcode.DCONST_0);
      }
      // float
      else if (p.equals("F")) {
        code.add(Opcode.FCONST_0);
      }
      // arrays and reference types
      else {
        code.add(Opcode.ACONST_NULL);
      }
    }
    // all our args should be pushed onto the stack, call the constructor
    code.addInvokespecial(file.getName(), "<init>", constructorToCall.getDescriptor());
    code.add(Opcode.RETURN);
    return true;
  }
Esempio n. 10
0
  /**
   * When creates the delegate initializer code when the delegate is injected into a method.
   *
   * <p>super initializer method is called first, and then _initMH is called
   *
   * @param file
   * @param intializerMethodInfo
   * @param delegateParameterPosition
   * @return
   */
  private Bytecode createDelegateInitializerCode(
      ClassFile file, MethodInformation intializerMethodInfo, int delegateParameterPosition) {
    Bytecode b = new Bytecode(file.getConstPool());
    // we need to push all the pareters on the stack to call the corresponding
    // superclass arguments
    b.addAload(0); // load this
    int localVariables = 1;
    int actualDelegateParamterPosition = 0;
    for (int i = 0; i < intializerMethodInfo.getMethod().getParameterTypes().length; ++i) {
      if (i == delegateParameterPosition) {
        // figure out the actual position of the delegate in the local
        // variables
        actualDelegateParamterPosition = localVariables;
      }
      Class<?> type = intializerMethodInfo.getMethod().getParameterTypes()[i];
      BytecodeUtils.addLoadInstruction(
          b, DescriptorUtils.classToStringRepresentation(type), localVariables);
      if (type == long.class || type == double.class) {
        localVariables = localVariables + 2;
      } else {
        localVariables++;
      }
    }
    b.addInvokespecial(
        file.getSuperclass(), intializerMethodInfo.getName(), intializerMethodInfo.getDescriptor());
    // if this method returns a value it is now sitting on top of the stack
    // we will leave it there are return it later

    // now we need to call _initMH
    b.addAload(0); // load this
    b.addAload(actualDelegateParamterPosition); // load the delegate
    b.addInvokevirtual(file.getName(), "_initMH", "(Ljava/lang/Object;)V");
    // return the object from the top of the stack that we got from calling
    // the superclass method earlier
    BytecodeUtils.addReturnInstruction(b, intializerMethodInfo.getReturnType());
    b.setMaxLocals(localVariables);
    return b;
  }
Esempio n. 11
0
    private JPackage assignPackage(File e) {

      JPackage retVal;

      final File[] classes = e.listFiles(CLASSFILE_FILTER);
      for (int i = 0; i < classes.length; i++) {

        ClassFile classFile =
            FileUtils.doWithFile(
                classes[i],
                new FileInputStreamOperation<ClassFile>() {

                  @Override
                  public ClassFile execute(String path, InputStream fileInputStream) {

                    try {
                      return JavassistClassFileHelper.constructClassFile(path, fileInputStream);
                    } catch (IOException e) {
                      throw new FileAccessException(
                          "Cannot access class file: " + e.getMessage(), e);
                    }
                  }
                });

        try {
          Package pkg = Class.forName(classFile.getName()).getPackage();
          retVal = JPackage.getJPackage(pkg, ClasspathResolver.this);
        } catch (ClassNotFoundException ex) {
          throw new FileAccessException("Cannot access class file: " + ex.getMessage(), ex);
        }

        if (retVal != null) {
          return retVal;
        }
      }
      return null;
    }
  @Override
  public boolean transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      ClassFile file)
      throws IllegalClassFormatException, BadBytecode {

    /**
     * Hack up the proxy factory so it stores the proxy ClassFile. We need this to regenerate
     * proxies.
     */
    if (file.getName().equals("org.jboss.weld.bean.proxy.ProxyFactory")) {
      for (final MethodInfo method : (List<MethodInfo>) file.getMethods()) {
        if (method.getName().equals("createProxyClass")) {
          final MethodInvokationManipulator methodInvokationManipulator =
              new MethodInvokationManipulator();
          methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic(
              ClassLoader.class.getName(),
              WeldProxyClassLoadingDelegate.class.getName(),
              "loadClass",
              "(Ljava/lang/String;)Ljava/lang/Class;",
              "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;",
              loader);
          methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic(
              "org.jboss.weld.util.bytecode.ClassFileUtils",
              WeldProxyClassLoadingDelegate.class.getName(),
              "toClass",
              "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;",
              "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;",
              loader);
          HashSet<MethodInfo> modifiedMethods = new HashSet<MethodInfo>();
          methodInvokationManipulator.transformClass(file, loader, true, modifiedMethods);
          for (MethodInfo m : modifiedMethods) {
            m.rebuildStackMap(ClassPool.getDefault());
          }
          return true;
        } else if (method.getName().equals("<init>")) {

          Integer beanArgument = null;
          int count = 0;
          for (final String paramType :
              DescriptorUtils.descriptorStringToParameterArray(method.getDescriptor())) {
            if (paramType.equals("javax/enterprise/inject/spi/Bean")) {
              beanArgument = count;
              break;
            } else if (paramType.equals("D") || paramType.equals("J")) {
              count += 2;
            } else {
              count++;
            }
          }
          if (beanArgument == null) {
            log.error(
                "Constructor org.jboss.weld.bean.proxy.ProxyFactory.<init>"
                    + method.getDescriptor()
                    + " does not have a bean parameter, proxies produced by this factory will not be reloadable");
            continue;
          }

          // similar to other tracked instances
          // but we need a strong ref
          Bytecode code = new Bytecode(file.getConstPool());
          code.addAload(0);
          code.addAload(beanArgument);
          code.addInvokestatic(
              WeldClassChangeAware.class.getName(),
              "addProxyFactory",
              "(Lorg/jboss/weld/bean/proxy/ProxyFactory;)V");
          CodeIterator it = method.getCodeAttribute().iterator();
          it.skipConstructor();
          it.insert(code.get());
        }
      }
    }
    return false;
  }
Esempio n. 13
0
  @Override
  public byte[] transform(
      final ClassLoader loader,
      final String className,
      final Class<?> classBeingRedefined,
      final ProtectionDomain protectionDomain,
      final byte[] classfileBuffer)
      throws IllegalClassFormatException {

    if (integrationClassTriggers.containsKey(className)) {
      integrationClassloader.put(loader, new Object());
      // we need to load the class in another thread
      // otherwise it will not go through the javaagent
      ThreadLoader.loadAsync(
          integrationClassTriggers.get(className).getClassChangeAwareName(), loader, true);
    }

    boolean changed = false;
    if (UnmodifiedFileIndex.isClassUnmodified(className)) {
      // TODO: enable this
      return null;
    }

    final ClassFile file;
    try {
      file = new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer)));
      for (final FakereplaceTransformer transformer : transformers) {
        if (transformer.transform(loader, className, classBeingRedefined, protectionDomain, file)) {
          changed = true;
        }
      }

      if (!changed) {
        UnmodifiedFileIndex.markClassUnmodified(className);
        return null;
      } else {
        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        file.write(new DataOutputStream(bs));
        // dump the class for debugging purposes
        if (DefaultEnvironment.getEnvironment().getDumpDirectory() != null
            && classBeingRedefined != null) {
          FileOutputStream s =
              new FileOutputStream(
                  DefaultEnvironment.getEnvironment().getDumpDirectory()
                      + '/'
                      + file.getName()
                      + ".class");
          DataOutputStream dos = new DataOutputStream(s);
          file.write(dos);
          s.close();
        }
        return bs.toByteArray();
      }
    } catch (IOException e) {
      e.printStackTrace();
      throw new IllegalClassFormatException(e.getMessage());
    } catch (Throwable e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
  }
  /**
   * Calls methodHandler.invoke with a null method parameter in order to get the underlying
   * instance. The invocation is then forwarded to this instance with generated bytecode.
   */
  protected Bytecode createForwardingMethodBody(ClassFile file, MethodInformation methodInfo)
      throws NotFoundException {
    Method method = methodInfo.getMethod();
    // we can only use bytecode based invocation for some methods
    // at the moment we restrict it solely to public methods with public
    // return and parameter types
    boolean bytecodeInvocationAllowed =
        Modifier.isPublic(method.getModifiers())
            && Modifier.isPublic(method.getReturnType().getModifiers());
    for (Class<?> paramType : method.getParameterTypes()) {
      if (!Modifier.isPublic(paramType.getModifiers())) {
        bytecodeInvocationAllowed = false;
        break;
      }
    }
    if (!bytecodeInvocationAllowed) {
      return createInterceptorBody(file, methodInfo);
    }
    Bytecode b = new Bytecode(file.getConstPool());
    int localCount = MethodUtils.calculateMaxLocals(method) + 1;

    // create a new interceptor invocation context whenever we invoke a method on a client proxy
    // we use a try-catch block in order to make sure that endInterceptorContext() is invoked
    // regardless whether
    // the method has succeeded or not
    int start = b.currentPc();
    b.addInvokestatic(
        "org.jboss.weld.bean.proxy.InterceptionDecorationContext",
        "startInterceptorContext",
        "()V");

    b.add(Opcode.ALOAD_0);
    b.addGetfield(
        file.getName(),
        "methodHandler",
        DescriptorUtils.classToStringRepresentation(MethodHandler.class));
    // pass null arguments to methodHandler.invoke
    b.add(Opcode.ALOAD_0);
    b.add(Opcode.ACONST_NULL);
    b.add(Opcode.ACONST_NULL);
    b.add(Opcode.ACONST_NULL);

    // now we have all our arguments on the stack
    // lets invoke the method
    b.addInvokeinterface(
        MethodHandler.class.getName(),
        "invoke",
        "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
        5);

    b.addCheckcast(methodInfo.getDeclaringClass());

    // now we should have the target bean instance on top of the stack
    // we need to dup it so we still have it to compare to the return value
    b.add(Opcode.DUP);

    // lets create the method invocation
    String methodDescriptor = methodInfo.getDescriptor();
    BytecodeUtils.loadParameters(b, methodDescriptor);
    if (method.getDeclaringClass().isInterface()) {
      b.addInvokeinterface(
          methodInfo.getDeclaringClass(),
          methodInfo.getName(),
          methodDescriptor,
          method.getParameterTypes().length + 1);
    } else {
      b.addInvokevirtual(methodInfo.getDeclaringClass(), methodInfo.getName(), methodDescriptor);
    }

    // end the interceptor context, everything was fine
    b.addInvokestatic(
        "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "endInterceptorContext", "()V");

    // jump over the catch block
    b.addOpcode(Opcode.GOTO);
    JumpMarker gotoEnd = JumpUtils.addJumpInstruction(b);

    // create catch block
    b.addExceptionHandler(start, b.currentPc(), b.currentPc(), 0);
    b.addInvokestatic(
        "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "endInterceptorContext", "()V");
    b.add(Opcode.ATHROW);

    // update the correct address to jump over the catch block
    gotoEnd.mark();

    // if this method returns a primitive we just return
    if (method.getReturnType().isPrimitive()) {
      BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType());
    } else {
      // otherwise we have to check that the proxy is not returning 'this;
      // now we need to check if the proxy has return 'this' and if so return
      // an
      // instance of the proxy.
      // currently we have result, beanInstance on the stack.
      b.add(Opcode.DUP_X1);
      // now we have result, beanInstance, result
      // we need to compare result and beanInstance

      // first we need to build up the inner conditional that just returns
      // the
      // result
      b.add(Opcode.IF_ACMPEQ);
      JumpMarker returnInstruction = JumpUtils.addJumpInstruction(b);
      BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType());
      returnInstruction.mark();

      // now add the case where the proxy returns 'this';
      b.add(Opcode.ALOAD_0);
      b.addCheckcast(methodInfo.getMethod().getReturnType().getName());
      BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType());
    }
    if (b.getMaxLocals() < localCount) {
      b.setMaxLocals(localCount);
    }
    return b;
  }
  public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass) {
    Set<Integer> methodCallLocations = new HashSet<Integer>();
    Integer newCallLocation = null;
    Integer methodReflectionLocation = null;
    Integer fakeCallRequiredLocation = null;
    // first we need to scan the constant pool looking for
    // CONSTANT_method_info_ref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
      // we have a method call
      if (pool.getTag(i) == ConstPool.CONST_Methodref) {
        String className = pool.getMethodrefClassName(i);
        String methodName = pool.getMethodrefName(i);

        if (className.equals(Method.class.getName())) {
          if (methodName.equals("invoke")) {
            // store the location in the const pool of the method ref
            methodCallLocations.add(i);
            // we have found a method call

            // if we have not already stored a reference to our new
            // method in the const pool
            if (newCallLocation == null) {
              methodReflectionLocation =
                  pool.addClassInfo("org.fakereplace.reflection.MethodReflection");
              int nt = pool.addNameAndTypeInfo("fakeCallRequired", "(Ljava/lang/reflect/Method;)Z");
              fakeCallRequiredLocation = pool.addMethodrefInfo(methodReflectionLocation, nt);
              newCallLocation = pool.addNameAndTypeInfo(METHOD_NAME, REPLACED_METHOD_DESCRIPTOR);
            }
          }
        }
      }
    }

    // this means we found an instance of the call, now we have to iterate
    // through the methods and replace instances of the call
    if (newCallLocation != null) {
      List<MethodInfo> methods = file.getMethods();
      for (MethodInfo m : methods) {
        try {
          // ignore abstract methods
          if (m.getCodeAttribute() == null) {
            continue;
          }
          CodeIterator it = m.getCodeAttribute().iterator();
          while (it.hasNext()) {
            // loop through the bytecode
            int index = it.next();
            int op = it.byteAt(index);
            // if the bytecode is a method invocation
            if (op == CodeIterator.INVOKEVIRTUAL) {
              int val = it.s16bitAt(index + 1);
              // if the method call is one of the methods we are
              // replacing
              if (methodCallLocations.contains(val)) {
                Bytecode b = new Bytecode(file.getConstPool());
                // our stack looks like Method, instance,params
                // we need Method, instance, params , Method
                b.add(Opcode.DUP_X2);
                b.add(Opcode.POP);
                b.add(Opcode.DUP_X2);
                b.add(Opcode.POP);
                b.add(Opcode.DUP_X2);
                b.addInvokestatic(
                    methodReflectionLocation, "fakeCallRequired", "(Ljava/lang/reflect/Method;)Z");
                b.add(Opcode.IFEQ);
                JumpMarker performRealCall = JumpUtils.addJumpInstruction(b);
                // now perform the fake call
                b.addInvokestatic(methodReflectionLocation, "invoke", REPLACED_METHOD_DESCRIPTOR);
                b.add(Opcode.GOTO);
                JumpMarker finish = JumpUtils.addJumpInstruction(b);
                performRealCall.mark();
                b.addInvokevirtual(Method.class.getName(), METHOD_NAME, METHOD_DESCRIPTOR);
                finish.mark();
                it.writeByte(CodeIterator.NOP, index);
                it.writeByte(CodeIterator.NOP, index + 1);
                it.writeByte(CodeIterator.NOP, index + 2);
                it.insert(b.get());
              }
            }
          }
          m.getCodeAttribute().computeMaxStack();
        } catch (Exception e) {
          log.error("Bad byte code transforming " + file.getName());
          e.printStackTrace();
        }
      }
      return true;
    } else {
      return false;
    }
  }
  protected void checkClassFile(ClassFile file) throws Exception {
    Map<Integer, Triple> calls = new HashMap<>();

    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
      // we have a method call
      BytecodeUtils.Ref ref = BytecodeUtils.getRef(pool, i);
      String className = ref.getClassName(pool, i);
      if (className != null) {
        String methodName = ref.getName(pool, i);
        String methodDesc = ref.getDesc(pool, i);
        fillCalls(i, className, methodName, methodDesc, calls);
      }
    }

    if (calls.isEmpty() && annotations.isEmpty()) {
      return;
    }

    String className = file.getName();

    AnnotationsAttribute faa =
        (AnnotationsAttribute) file.getAttribute(AnnotationsAttribute.visibleTag);
    checkAnnotations(className, TYPE_USAGE.getMethodName(), faa, -1);

    List<MethodInfo> methods = file.getMethods();
    for (MethodInfo m : methods) {
      try {
        // ignore abstract methods
        if (m.getCodeAttribute() == null) {
          continue;
        }

        AnnotationsAttribute maa =
            (AnnotationsAttribute) m.getAttribute(AnnotationsAttribute.visibleTag);
        boolean annotationsChecked = false;
        int firstLine = -1;

        CodeIterator it = m.getCodeAttribute().iterator();
        while (it.hasNext()) {
          // loop through the bytecode
          final int index = it.next();
          final int line = m.getLineNumber(index);

          if (annotationsChecked == false) {
            annotationsChecked = true;
            firstLine = line;
            checkAnnotations(
                className, m.getName(), maa, line - 2); // -2 to get the line above the method
          }

          int op = it.byteAt(index);
          // if the bytecode is a method invocation
          if (op == CodeIterator.INVOKEVIRTUAL
              || op == CodeIterator.INVOKESTATIC
              || op == CodeIterator.INVOKEINTERFACE
              || op == CodeIterator.INVOKESPECIAL) {
            int val = it.s16bitAt(index + 1);
            Triple triple = calls.get(val);
            if (triple != null) {
              Map<Tuple, Set<CodeLine>> map = report.get(triple.className);
              Set<CodeLine> set = map.get(triple.tuple);
              CodeLine cl = new CodeLine(className, m.getName(), line);
              set.add(cl.modify()); // check for .jsp, etc
            }
          }
        }

        if (BaseMethodExclusion.isBridge(m) == false) {
          SignatureAttribute.MethodSignature signature =
              SignatureAttribute.toMethodSignature(m.getDescriptor());
          handleMethodSignature(className, m.getName(), firstLine - 1, signature.getReturnType());
          handleMethodSignature(
              className, m.getName(), firstLine - 1, signature.getParameterTypes());
          handleMethodSignature(
              className, m.getName(), firstLine - 1, signature.getExceptionTypes());
        }

        ParameterAnnotationsAttribute paa =
            (ParameterAnnotationsAttribute)
                m.getAttribute(ParameterAnnotationsAttribute.visibleTag);
        if (paa != null) {
          Annotation[][] paas = paa.getAnnotations();
          if (paas != null) {
            for (Annotation[] params : paas) {
              for (Annotation a : params) {
                for (Map.Entry<String, Boolean> entry : annotations.entrySet()) {
                  if (entry.getKey().equals(a.getTypeName())) {
                    checkAnnotation(
                        className, m.getName(), firstLine - 1, entry.getValue(), entry.getKey(), a);
                  }
                }
              }
            }
          }
        }

        m.getCodeAttribute().computeMaxStack();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }