簡體   English   中英

如何以編程方式查找對方法的所有引用?

[英]How to find all references to a method programmatically?

因此,這不是使用 Eclipse IDE 查找方法參考的方法。

我需要通過代碼(Java)找到它們。

背景:我們有數百個項目,都基於一個共同的項目。 多年來,通用項目有一些非常糟糕的代碼已被替換。 錯誤代碼已被標記為已棄用。

現在,我想刪除所有已棄用的代碼。 但是,我想創建一些代碼來遷移我們擁有的所有其他項目。

方法:現在,對於某些事情,我可以在項目代碼中搜索一些關鍵字(即方法名稱)並替換為其他內容。 但是,有些方法名稱會發生沖突,並且進行簡單的搜索/替換將不起作用。

我的想法是,如果我有一個不推薦使用的方法: com.foo.bar.SomeClass.someMethod(String)

然后以某種方式找到使用 someMethod(String) 的每個地方,然后我可以用 newMethod(String) 替換那個

我看過一些:

基本上,我想創建一個遷移程序,使遷移到新的通用代碼變得簡單。 我計划使用相同的遷移代碼來幫助將項目從 JDK8 遷移到 OpenJDK11。

關於如何實現這一點的想法?

您當然可以將 ASM 與 ClassVisitor / MethodVisitor 一起使用來查找對該方法的所有引用。

您只需在ClassVisitor中覆蓋visitMethod以返回您自己的MethodVisitor ,並在MethodVisitor中覆蓋visitMethodInsn

在 visitMethodInsn 中,您檢查目標是否是不推薦使用的方法之一,並記錄它的使用位置。 這里有一個例子:

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;

import static org.objectweb.asm.Opcodes.*;

public class MethodScanner extends ClassVisitor {

    private final String fileName;

    private MethodScanner(String fileName) {
        super(ASM7);
        this.fileName = fileName;
    }

    public static void main(String[] args) throws IOException {
        scanJar(new File("path/to/some.jar"));

    }

    public static void scanJar(File jarFile) throws IOException {
        var jf = new JarFile(jarFile);

        jf.stream().filter(je -> getFileExtension(je.getName()).equals(".class"))
        .forEach(je -> processEntry(jf, je));
    }

    private static String getFileExtension(String name) {
        int idx = name.lastIndexOf(".");
        if (idx == -1)
            return "";
        return name.substring(idx);
    }

    private static void processEntry(JarFile jf, JarEntry je) {
        try {
            byte[] bytes = jf.getInputStream(je).readAllBytes();
            MethodScanner ms = new MethodScanner(jf.getName() + "!" + je.getName());
            ClassReader cr = new ClassReader(bytes);
            cr.accept(ms, 0);
        } catch (IOException ioe) {
            throw new UncheckedIOException(ioe);
        }
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
            String[] exceptions) {
        return new MethodScannerVisitor(super.visitMethod(access, name, descriptor, signature, exceptions), fileName, name + descriptor);
    }

    private static class MethodScannerVisitor extends MethodVisitor {

        private final String fileName;
        private final String methodName;

        private MethodScannerVisitor(MethodVisitor parent, String fileName, String methodName) {
            super(ASM7, parent);
            this.fileName = fileName;
            this.methodName = methodName;
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
            if (owner.equals("java/lang/Math") && name.equals("random") && descriptor.equals("()D")) {
                System.out.println("reference to Math.random() from " + fileName + " in " + methodName);
            }
        }

    }
}

做一些搜索,我有一些基本代碼,但仍然沒有 100%

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;


public class ProjectMigration {

  public static void main (String[] args) {
    ProjectMigration migration = new ProjectMigration();
    try {
      migration.migrate();
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
      e.printStackTrace();
    }
  }

  private List<DeprecatedClass> getDeprecatedMethods () {
    List<DeprecatedClass> deprecatedClasses = new ArrayList<>();
    deprecatedClasses.add(
        new DeprecatedClass("com.e2open.selenium.api_common.abstractHelpers.SystemInformation",
            "isInternetExplorer8", "boolean", ""));
    deprecatedClasses.add(
        new DeprecatedClass("com.e2open.selenium.api_common.AbstractPage", "radioButtonSelect",
            "boolean", "java.lang.String", "java.lang.String"));

    return deprecatedClasses;
  }

  public void migrate ()
      throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    List<DeprecatedClass> deprecatedClasses = getDeprecatedMethods();

    for (DeprecatedClass clazz : deprecatedClasses) {
      System.out.println("CLASS: " + clazz.getClazz());
      Class c = Class.forName(clazz.getClazz());
      analyze(c.newInstance(), clazz);
      System.out.println();
    }
  }

  private void analyze (Object object, DeprecatedClass deprecatedClass) {

    ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
      @Override
      public MethodVisitor visitMethod (
          int access, String name, String desc, String signature, String[] exceptions) {

        if (isDeprecatedMethod(deprecatedClass, name, desc)) {
          logMethod(name, desc);
          return new MethodVisitor(Opcodes.ASM5) {
            @Override
            public void visitMethodInsn (
                int opcode, String owner, String name, String desc, boolean arg4) {
              if (owner.contains("e2open")) {
                processMethod(owner, name, desc);
              }
              super.visitMethodInsn(opcode, owner, name, desc, arg4);
            }
          };
        }
        return null;
      }
    };
    try {
      ClassReader classReader = new ClassReader(object.getClass().getCanonicalName());
      classReader.accept(cv, 0);
    } catch (IOException e) {
      System.err.println("Something went wrong !! " + e.getMessage());
    }
  }

  protected void processMethod (String owner, String name, String desc) {
    System.out.println(
        "classUsed[" + owner.replace("/", ".") + "]; methodUsed[" + name + "]; methodParameters["
            + desc + "]");
  }

  protected void logMethod (String name, String desc) {
    String returnType = getReturnType(desc);
    String paramters = getParamters(desc);
    System.out.println(String.format("METHOD: %s %s(%s)", returnType, name, paramters));
  }



  private String getParamters (String desc) {
    String parms = desc.split("\\)")[0];
    if (parms.equals("(")) {
      return "";
    }
    parms = parms.substring(2, parms.length());
    String[] parmameters = parms.split(";");
    StringBuilder paramterString = new StringBuilder();
    for (String p : parmameters) {
      paramterString.append(p.replaceAll("/", ".")).append(", ");
    }
    return paramterString.toString().substring(0, paramterString.length() - 2);
  }

  private String getReturnType (String desc) {
    String returnType = desc.split("\\)")[1];
    if (returnType.equals("V")) {
      return "void";
    }
    if (returnType.length() == 1) {
      return returnType;
    }
    return returnType.substring(1, returnType.length() - 1).replace("/", ".");
  }

  private boolean isDeprecatedMethod (
      DeprecatedClass deprecatedClass, String method, String parmeters) {
    if ((method.equals(deprecatedClass.getMethod()))
        && (parmeters.equals(deprecatedClass.getParameters()))) {
      return true;
    }
    return false;
  }


  private class DeprecatedClass {
    private String clazz;
    private String method;
    private List<String> parameters;
    private String returnType;

    public DeprecatedClass() {}

    /**
     * No paramters and no return type
     *
     * @param clazzName The class to check. Example: com.e2open.selenium.api_common.AbstractPage
     * @param methodName The method name to find references for. Example: clearAllInputFields
     */
    public DeprecatedClass(String clazzName, String methodName) {
      this.clazz = clazzName;
      this.method = methodName;
    }

    /**
     * Use if there is a return type and/or parameters
     *
     * @param clazzName The class to check. Example: com.e2open.selenium.api_common.AbstractPage
     * @param methodName he method name to find references for. Example: findFieldByAttribute
     * @param returnType The returned type. Example: org.openqa.selenium.By
     * @param parameterValues List of paramters. Example: "java.lang.String", "java.lang.String",
     *        "java.lang.String"
     */
    public DeprecatedClass(String clazzName, String methodName, String returnType,
        String... parameterValues) {
      this.clazz = clazzName;
      this.method = methodName;
      setReturnType(returnType);
      if (parameterValues != null) {
        for (String parm : parameterValues) {
          if (StringUtils.isNoneBlank(parm)) {
            addParameter(parm);
          }
        }
      }
    }

    /**
     * @return the clazz
     */
    public String getClazz () {
      return clazz;
    }

    /**
     * @param clazz the clazz to set
     */
    public void setClazz (String clazz) {
      this.clazz = clazz;
    }

    /**
     * @return the method
     */
    public String getMethod () {
      return method;
    }

    /**
     * @param method the method to set
     */
    public void setMethod (String method) {
      this.method = method;
    }


    /**
     * @param returnType the returnType to set
     */
    public void setReturnType (String returnType) {
      if (returnType != null) {
        if ("boolean".equals(returnType)) {
          this.returnType = "Z";
        } else {
          this.returnType = "L" + returnType.replaceAll("\\.", "/") + ";";
        }
      }
    }

    /**
     * @return the parameters
     */
    public String getParameters () {
      StringBuilder parms = new StringBuilder();
      if (this.parameters == null) {
        parms.append("()");
      } else {
        parms.append("(").append(String.join("", this.parameters)).append(")");
      }

      if (this.returnType == null) {
        parms.append("V");
      } else {
        parms.append(this.returnType);
      }
      return parms.toString();
    }

    /**
     * @param parameters the parameters to set
     */
    public void addParameter (String parameter) {
      if (this.parameters == null) {
        this.parameters = new ArrayList<>();
      }

      this.parameters.add("L" + parameter.replaceAll("\\.", "/") + ";");
    }


  }

}

我得到的結果是:

CLASS: com.e2open.selenium.api_common.abstractHelpers.SystemInformation
METHOD: Z isInternetExplorer8()
classUsed[com.e2open.selenium.api_common.abstractHelpers.SystemInformation]; methodUsed[getSystemData]; methodParameters[()Lcom/e2open/selenium/api_common/abstractHelpers/SystemInformation$SystemData;]
classUsed[com.e2open.selenium.api_common.browser.BrowserType]; methodUsed[equals]; methodParameters[(Ljava/lang/Object;)Z]

CLASS: com.e2open.selenium.api_common.AbstractPage
METHOD: Z radioButtonSelect(java.lang.String, Ljava.lang.String)
classUsed[com.e2open.selenium.api_common.JLog]; methodUsed[write]; methodParameters[(Ljava/lang/String;)V]
classUsed[com.e2open.selenium.api_common.AbstractPage]; methodUsed[radioButtonGet]; methodParameters[(Ljava/lang/String;Ljava/lang/String;)Lorg/openqa/selenium/WebElement;]

然而,結果並不正確。

如果我采用其中一種方法:com.e2open.selenium.api_common.abstractHelpers.SystemInformation.isInternetExplorer8()

並使用 Eclipse 查找 References,我得到isInternetExplorer8 Eclipse References Image

對於其他方法進行搜索:com.e2open.selenium.api_common.AbstractPage.radioButtonSelect(String, String) radioButtonSelect Z32F7222026696F307878 Image89194DEE89194DEE

我不確定從這里更改什么或從哪里更改 go。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM