访问使用ASM操作的类时,如何在不加载任何类的情况下知道该类的祖先类?

huangapple go评论72阅读模式
英文:

When visiting a class using ASM, how to know the ancestor class of that class without loading any class?

问题

以下是翻译好的部分:

我想要做的是我有一个 ClassVisitor 适配器在进入 `visit` 方法时我想要知道类 `AncestorClass` 是否是那个类的祖先类我尝试使用反射(`Class.forname(...)`)来实现就像这样

MyTransformer.class:
```java
public class MyTransformer implements ClassFileTransformer {

    public byte[] transform(ClassLoader loader, String className,
                            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
                            byte[] classfileBuffer) throws IllegalClassFormatException {

        if (className == null){
            return classfileBuffer;
        }
        ClassReader cr = new ClassReader(classfileBuffer);
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
        CollectionCVAdapter mca = new MyCVAdapter(cw, className);
        cr.accept(mca, 0);
        return cw.toByteArray();
    }
}

MyCVAdapter.class:

class MyCVAdapter extends ClassVisitor {

    private String className;
    private boolean isDescendantClass;

    CollectionCVAdapter(ClassVisitor classVisitor, String className){
        super(Opcodes.ASM5, classVisitor);
        this.className = className;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        // 检查此类是否是 AncestorClass.class 的子类
        try{
            Class superClass = Class.forName(superName.replace("/", "."));
            do {
                if (superClass == AncestorClass.class) {
                    this.isDescendantClass= true;
                    break;
                }
                superClass = superClass.getSuperclass();
            } while (superClass != null);
        }catch (Exception e){
            e.printStackTrace();
        }
        super.visit(version, access, name, signature, superName, interfaces);
    }
    ...
}

但问题是,我还想对被仪器化类的父类进行处理。我发现一旦 Class.forName() 加载了父类,父类就不会进入 transform 方法。这意味着无法再对父类进行仪器化。那么是否有其他方法来判断一个类是否是 AncestorClass 的子类呢?


<details>
<summary>英文:</summary>

What I want to do is: I have a ClassVisitor adapter, when entering the `visit` method, I want to know if the class `AncestorClass` is the ancestor class of that class? I have tried to use reflection (`Class.forname(...)`) like this:

MyTransformer.class:
```java
public class MyTransformer implements ClassFileTransformer {

    public byte[] transform(ClassLoader loader, String className,
                            Class&lt;?&gt; classBeingRedefined, ProtectionDomain protectionDomain,
                            byte[] classfileBuffer) throws IllegalClassFormatException {

        if (className == null){
            return classfileBuffer;
        }
        ClassReader cr = new ClassReader(classfileBuffer);
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
        CollectionCVAdapter mca = new MyCVAdapter(cw, className);
        cr.accept(mca, 0);
        return cw.toByteArray();
    }
}

MyCVAdapter.class:

class MyCVAdapter extends ClassVisitor {

    private String className;
    private boolean isDescendantClass;

    CollectionCVAdapter(ClassVisitor classVisitor, String className){
        super(Opcodes.ASM5, classVisitor);
        this.className = className;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        // check if this class is a descendant class of AncestorClass.class
        try{
            Class superClass = Class.forName(superName.replace(&quot;/&quot;, &quot;.&quot;));
            do {
                if (superClass == AncestorClass.class) {
                    this.isDescendantClass= true;
                    break;
                }
                superClass = superClass.getSuperclass();
            } while (superClass != null);
        }catch (Exception e){
            e.printStackTrace();
        }
        super.visit(version, access, name, signature, superName, interfaces);
    }
    ...
}

but the problem is that I also want to instrument the parent class of the instrumented class. I found that once Class.forName() loads the parent class, the parent class will not get into the transform method. Which means the parent class can not be instrumented any more. So is there any alternatives to find out whether the class is a descendant class of AncestorClass?

答案1

得分: 1

感谢 @JohannesKuhn 和 @Holger,我终于找到了静态分析继承关系的方法。

    @Override
    public void visit(int version, int access, String name, String signature, String superSlashName, String[] interfaces) {
        String originalSuperName = superSlashName;
        // 检查这个类是否是 AncestorClass.class 的子类
        try{
            while (superSlashName != null){
                if (superSlashName.equals(AncestorClassName)){
                    this.isDescendantClass= true;
                    break;
                }else{
                    InputStream is = this.loader.getResourceAsStream(superSlashName + ".class");
                    byte[] superBytes = FileUtil.loadByteCode(is);
                    ClassReader parentCr = new ClassReader(superBytes);
                    superSlashName = parentCr.getSuperName();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        super.visit(version, access, name, signature, originalSuperName, interfaces);
    }
英文:

Thanks to @JohannesKuhn and @Holger, I finally found the way to analyze the inheritance relationship statically.

    @Override
    public void visit(int version, int access, String name, String signature, String superSlashName, String[] interfaces) {
        String originalSuperName = superSlashName;
        // check if this class is a subclass of AncestorClass.class
        try{
            while (superSlashName != null){
                if (superSlashName.equals(AncestorClassName)){
                    this.isDescendantClass= true;
                    break;
                }else{
                    InputStream is = this.loader.getResourceAsStream(superSlashName + &quot;.class&quot;);
                    byte[] superBytes = FileUtil.loadByteCode(is);
                    ClassReader parentCr = new ClassReader(superBytes);
                    superSlashName = parentCr.getSuperName();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        super.visit(version, access, name, signature, originalSuperName, interfaces);
    }

huangapple
  • 本文由 发表于 2020年7月26日 22:11:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/63101275.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定