本文属于Java ASM系列三:Tree API当中的一篇。

在本章内容当中,最核心的内容就是下面两行代码。这两行代码包含了asm-analysis.jar当中AnalyzerFrameInterpreterValue最重要的四个类:

   ┌── Analyzer
   │        ┌── Value                                   ┌── Interpreter
   │        │                                           │
Analyzer<SourceValue> analyzer = new Analyzer<>(new SourceInterpreter());
Frame<BasicValue>[] frames = analyzer.analyze(owner, mn);
   │        │
   │        └── Value
   └── Frame

在本文当中,我们将介绍SourceInterpreterSourceValue类:

┌───┬───────────────────┬─────────────┬───────┐
│ 0 │    Interpreter    │    Value    │ Range │
├───┼───────────────────┼─────────────┼───────┤
│ 1 │ BasicInterpreter  │ BasicValue  │   7   │
├───┼───────────────────┼─────────────┼───────┤
│ 2 │   BasicVerifier   │ BasicValue  │   7   │
├───┼───────────────────┼─────────────┼───────┤
│ 3 │  SimpleVerifier   │ BasicValue  │   N   │
├───┼───────────────────┼─────────────┼───────┤
│ 4 │ SourceInterpreter │ SourceValue │   N   │
└───┴───────────────────┴─────────────┴───────┘

在上面这个表当中,我们关注以下三点:

  • 第一点,类的继承关系。SourceInterpreter类继承自Interpreter抽象类。
  • 第二点,类的合作关系。SourceInterpreterSourceValue类是一起使用的。
  • 第三点,类的表达能力。SourceInterpreter类能够使用的SourceValue对象有很多个。

1. SourceValue

1.1. class info

第一个部分,SourceValue类实现了Value接口。

public class SourceValue implements Value {
}

1.2. fields

第二个部分,SourceValue类定义的字段有哪些。

public class SourceValue implements Value {
    public int size;

    public Set<AbstractInsnNode> insns;
}

1.3. constructors

第三个部分,SourceValue类定义的构造方法有哪些。这三个构造方法,有不同的应用场景:

  • SourceValue(int)构造方法,在指令(Instruction)开始执行之前,设置local variable的初始值,例如this、方法接收的参数,这些不需要指令(Instruction)参数。
  • SourceValue(int, AbstractInsnNode)构造方法,在指令(Instruction)开始执行之后,记录一条指令(AbstractInsnNode)和对应的local variable、operand stack上的值(SourceValue)之间的关系。
  • SourceValue(int, Set&lt;AbstractInsnNode&gt;)构造方法,在SourceValue值进行合并(merge)的时候,记录多条指令(Set&lt;AbstractInsnNode&gt;)和对应的local variable、operand stack上的值(SourceValue)之间的关系。
public class SourceValue implements Value {
    public SourceValue(int size) {
        this(size, new SmallSet<AbstractInsnNode>());
    }

    public SourceValue(int size, AbstractInsnNode insnNode) {
        this.size = size;
        this.insns = new SmallSet<>(insnNode);
    }

    public SourceValue(int size, Set<AbstractInsnNode> insnSet) {
        this.size = size;
        this.insns = insnSet;
    }
}

1.4. methods

第四个部分,SourceValue类定义的方法有哪些。

public class SourceValue implements Value {
    @Override
    public int getSize() {
        return size;
    }
}

2. SourceInterpreter

SourceInterpreter的主要作用是记录指令(instruction)与Frame当中值(SourceValue)的关联关系。

2.1. class info

第一个部分,SourceInterpreter类实现了Interpreter抽象类。

public class SourceInterpreter extends Interpreter<SourceValue> implements Opcodes {
}

2.2. fields

第二个部分,SourceInterpreter类定义的字段有哪些。

public class SourceInterpreter extends Interpreter<SourceValue> implements Opcodes {
    // 没有定义字段
}

2.3. constructors

第三个部分,SourceInterpreter类定义的构造方法有哪些。

public class SourceInterpreter extends Interpreter<SourceValue> implements Opcodes {
    public SourceInterpreter() {
        super(ASM9);
        if (getClass() != SourceInterpreter.class) {
            throw new IllegalStateException();
        }
    }

    protected SourceInterpreter(int api) {
        super(api);
    }
}

2.4. methods

第四个部分,SourceInterpreter类定义的方法有哪些。

2.4.1. newValue方法

public class SourceInterpreter extends Interpreter<SourceValue> implements Opcodes {
    @Override
    public SourceValue newValue(Type type) {
        if (type == Type.VOID_TYPE) {
            return null;
        }
        // 这里是SourceValue定义的第1个构造方法,不需要指令参与
        return new SourceValue(type == null ? 1 : type.getSize());
    }
}

2.4.2. opcode相关方法

下面7个方法中的6个,遵循一个共同特点:“创建SourceValue对象”。

public class SourceInterpreter extends Interpreter<SourceValue> implements Opcodes {
    @Override
    public SourceValue newOperation(AbstractInsnNode insn) {
        int size = 1; // 或者 size = 2
        // 这里是SourceValue定义的第2个构造方法,需要1个指令参与
        return new SourceValue(size, insn);
    }

    @Override
    public SourceValue copyOperation(AbstractInsnNode insn, SourceValue value) {
        // 这里是SourceValue定义的第2个构造方法,需要1个指令参与
        return new SourceValue(value.getSize(), insn);
    }

    @Override
    public SourceValue unaryOperation(AbstractInsnNode insn,
                                      SourceValue value) {
        int size = 1; // 或者 size = 2
        // 这里是SourceValue定义的第2个构造方法,需要1个指令参与
        return new SourceValue(size, insn);
    }

    @Override
    public SourceValue binaryOperation(AbstractInsnNode insn,
                                       SourceValue value1,
                                       SourceValue value2) {
        int size = 1; // 或者 size = 2
        // 这里是SourceValue定义的第2个构造方法,需要1个指令参与
        return new SourceValue(size, insn);
    }

    @Override
    public SourceValue ternaryOperation(AbstractInsnNode insn,
                                        SourceValue value1,
                                        SourceValue value2,
                                        SourceValue value3) {
        // 这里是SourceValue定义的第2个构造方法,需要1个指令参与
        return new SourceValue(1, insn);
    }

    @Override
    public SourceValue naryOperation(AbstractInsnNode insn,
                                     List<? extends SourceValue> values) {
        int size = 1; // 或者 size = 2
        // 这里是SourceValue定义的第2个构造方法,需要1个指令参与
        return new SourceValue(size, insn);
    }

    @Override
    public void returnOperation(AbstractInsnNode insn,
                                SourceValue value,
                                SourceValue expected) {
        // Nothing to do.
    }
}

2.4.3. merge方法

public class SourceInterpreter extends Interpreter<SourceValue> implements Opcodes {
    @Override
    public SourceValue merge(SourceValue value1, SourceValue value2) {
        // 第一种情况,SmallSet类型
        if (value1.insns instanceof SmallSet && value2.insns instanceof SmallSet) {
            Set<AbstractInsnNode> setUnion = value1.insns + value2.insns;
            if (setUnion == value1.insns && value1.size == value2.size) {
                // value1包含了value2
                return value1;
            } else {
                // 这里是SourceValue定义的第3个构造方法,需要多个指令参与
                // value1不能包含value2,那就生成一个新的SourceValue对象
                return new SourceValue(Math.min(value1.size, value2.size), setUnion);
            }
        }

        // 第二种情况,其它类型,value1不能包含value2,那就生成一个新的SourceValue对象
        if (value1.size != value2.size || !containsAll(value1.insns, value2.insns)) {
            HashSet<AbstractInsnNode> setUnion = new HashSet<>();
            setUnion.addAll(value1.insns);
            setUnion.addAll(value2.insns);
            // 这里是SourceValue定义的第3个构造方法,需要多个指令参与
            return new SourceValue(Math.min(value1.size, value2.size), setUnion);
        }

        // 第三种情况,其它类型,value1包含了value2
        return value1;
    }
}

3. 测试代码

3.1. simple

public class HelloWorld {
    public void test() {
        int a = 1;
        int b = 2;
        int c = a + b;
        System.out.println(c);
    }
}

对应的Frame变化如下:

test:()V
000:    iconst_1                                {[], [], [], []} | {}
001:    istore_1                                {[], [], [], []} | {[iconst_1]}
002:    iconst_2                                {[], [istore_1], [], []} | {}
003:    istore_2                                {[], [istore_1], [], []} | {[iconst_2]}
004:    iload_1                                 {[], [istore_1], [istore_2], []} | {}
005:    iload_2                                 {[], [istore_1], [istore_2], []} | {[iload_1]}
006:    iadd                                    {[], [istore_1], [istore_2], []} | {[iload_1], [iload_2]}
007:    istore_3                                {[], [istore_1], [istore_2], []} | {[iadd]}
008:    getstatic System.out                    {[], [istore_1], [istore_2], [istore_3]} | {}
009:    iload_3                                 {[], [istore_1], [istore_2], [istore_3]} | {[getstatic System.out]}
010:    invokevirtual PrintStream.println       {[], [istore_1], [istore_2], [istore_3]} | {[getstatic System.out], [iload_3]}
011:    return                                  {[], [istore_1], [istore_2], [istore_3]} | {}
================================================================

3.2. if

public class HelloWorld {
    public void test(int a, int b) {
        Object obj;
        int c = a + b;
        if (c > 10) {
            obj = Integer.valueOf(c);
        }
        else {
            obj = Float.valueOf(c);
        }
        System.out.println(obj);
    }
}

4. 总结

本文内容总结如下:

  • 第一点,SourceValue类是“记录”instruction(AbstractInsnNode)与Frame当中的值(SourceValue)之间的关系,而SourceInterpreter类是“建立”两者之间的联系。
  • 第二点,SourceInterpreter类是属于Interpreter的部分,它使用SourceValue的逻辑分成三个部分:
    • 在指令(instruction)执行之前,计算方法的初始Frame(initial frame),this、方法的参数和未初始化的值,它们对应的SourceValue对象不与任何的指令(instruction)相关,对应于SourceValue第1个构造方法。
    • 在指令(instruction)开始执行之后,如果某一条指令(instruction)改变了local variable和operand stack上的值,那么对应的SourceValue对象就记录该instruction(AbstractInsnNode)与SourceValue对象的联系,对应于SourceValue第2个构造方法。
    • 在指令(instruction)开始执行之后,control flow有分支(brach),也就意味着将来的合并(merge);在合并(merge)时对应的SourceValue对象就记录该多个instruction(Set&lt;AbstractInsnNode&gt;)与SourceValue对象的联系,对应于SourceValue第3个构造方法。