在本章内容当中,最核心的内容就是下面两行代码。这两行代码包含了asm-analysis.jar当中Analyzer、Frame、Interpreter和Value最重要的四个类:
┌── Analyzer
│ ┌── Value ┌── Interpreter
│ │ │
Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicInterpreter());
Frame<BasicValue>[] frames = analyzer.analyze(owner, mn);
│ │
│ └── Value
└── Frame
在本文当中,我们将介绍BasicInterpreter和BasicValue类:
┌───┬───────────────────┬─────────────┬───────┐
│ 0 │ Interpreter │ Value │ Range │
├───┼───────────────────┼─────────────┼───────┤
│ 1 │ BasicInterpreter │ BasicValue │ 7 │
├───┼───────────────────┼─────────────┼───────┤
│ 2 │ BasicVerifier │ BasicValue │ 7 │
├───┼───────────────────┼─────────────┼───────┤
│ 3 │ SimpleVerifier │ BasicValue │ N │
├───┼───────────────────┼─────────────┼───────┤
│ 4 │ SourceInterpreter │ SourceValue │ N │
└───┴───────────────────┴─────────────┴───────┘
BasicValue
class info
第一个部分,BasicValue类实现了Value接口。
public class BasicValue implements Value {
}
fields
第二个部分,BasicValue类定义的字段有哪些。
public class BasicValue implements Value {
private final Type type;
public Type getType() {
return type;
}
}
通过以下三点来理解BasicValue和Type之间的关系:
- 第一点,
BasicValue是asm-analysis.jar当中定义的类,是local variable和operand stack当中存储的数据类型。 - 第二点,
Type是asm.jar当中定义的类,它是对具体的.class文件当中的Internal Name和Descriptor的一种ASM表示方式。 - 第三点,
BasicValue类就是对Type类的封装。
| 领域 | ClassFile | ASM | Frame(local variable+operand stack) |
|---|---|---|---|
| 类型 | Internal Name/Descriptor | Type | Value |
| 示例 | Ljava/lang/String; |
Type t = Type.getObjectType("java/lang/String"); |
BasicValue val = BasicValue.INT_VALUE; |
┌─────────────────────────────┐ | ┌──────────────────────────────────────────────┐
│ asm.jar │ | │ asm-analysis.jar │
│ ClassReader Type │ | │ │
│ ClassVisitor ClassWriter │ | │ Value Interpreter Frame Analyzer │
└─────────────────────────────┘ | └──────────────────────────────────────────────┘
---------------------------------------------------------------------------------------------------
| ┌──────────────────────────────────────────────────────┐
| │ operand stack │
┌────────────────────────┐ | │ ┌──────────────┐ JVM │
│ Internal Name │ | │ ├──────────────┤ Stack Frame │
│ Descriptor │ | │ ├──────────────┤ │
│ │ | │ ├──────────────┤ │
│ .class │ | │ ├──────────────┤ │
│ ClassFile │ | │ ├──────────────┤ local variable │
│ │ | │ ├──────────────┤ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │
└────────────────────────┘ | │ └──────────────┘ └──┘ └──┘ └──┘ └──┘ └──┘ │
| │ 0 1 2 3 4 │
| └──────────────────────────────────────────────────────┘
constructors
第三个部分,BasicValue类定义的构造方法有哪些。
public class BasicValue implements Value {
public BasicValue(Type type) {
this.type = type;
}
}
methods
第四个部分,BasicValue类定义的方法有哪些。
public class BasicValue implements Value {
@Override
public int getSize() {
return type == Type.LONG_TYPE || type == Type.DOUBLE_TYPE ? 2 : 1;
}
public boolean isReference() {
return type != null && (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY);
}
@Override
public String toString() {
if (this == UNINITIALIZED_VALUE) {
return ".";
} else if (this == RETURNADDRESS_VALUE) {
return "A";
} else if (this == REFERENCE_VALUE) {
return "R";
} else {
return type.getDescriptor();
}
}
}
static fields
在BasicValue类当中,定义了7个静态字段:
UNINITIALIZED_VALUEmeans “all possible values”.INT_VALUEmeans “all int, short, byte, boolean or char values”.FLOAT_VALUEmeans “all float values”.LONG_VALUEmeans “all long values”.DOUBLE_VALUEmeans “all double values”.REFERENCE_VALUEmeans “all object and array values”.RETURNADDRESS_VALUEis used for subroutines.
public class BasicValue implements Value {
public static final BasicValue UNINITIALIZED_VALUE = new BasicValue(null);
public static final BasicValue INT_VALUE = new BasicValue(Type.INT_TYPE);
public static final BasicValue FLOAT_VALUE = new BasicValue(Type.FLOAT_TYPE);
public static final BasicValue LONG_VALUE = new BasicValue(Type.LONG_TYPE);
public static final BasicValue DOUBLE_VALUE = new BasicValue(Type.DOUBLE_TYPE);
public static final BasicValue REFERENCE_VALUE = new BasicValue(Type.getObjectType("java/lang/Object"));
public static final BasicValue RETURNADDRESS_VALUE = new BasicValue(Type.VOID_TYPE);
}
在这里,我们要注意:虽然BasicValue类定义了这7个静态字段,但是并不是表示说BasicValue只能有这7个字段的值,它还可以创建许许多的对象实例。为什么我们要说这样一件事情呢?因为在刚开始,我们会经常用到这7个静态字段的值,很容易误导我们,让我们觉得只有这7个静态字段的值。实际上,只有BasicInterpreter和BasicVerifier两个类完全限定于使用这7个静态字段,而SimpleVerifier就会创建许许多的BasicValue对象。
┌───┬───────────────────┬─────────────┬───────┐
│ 0 │ Interpreter │ Value │ Range │
├───┼───────────────────┼─────────────┼───────┤
│ 1 │ BasicInterpreter │ BasicValue │ 7 │
├───┼───────────────────┼─────────────┼───────┤
│ 2 │ BasicVerifier │ BasicValue │ 7 │
├───┼───────────────────┼─────────────┼───────┤
│ 3 │ SimpleVerifier │ BasicValue │ N │
├───┼───────────────────┼─────────────┼───────┤
│ 4 │ SourceInterpreter │ SourceValue │ N │
└───┴───────────────────┴─────────────┴───────┘
用一个比喻来加深印象。BasicInterpreter和BasicVerifier两个类就像两个小孩儿,他们只会7个单词,说的所有的话都是由这7个单词组成;而SimpleVerifier就像是一个词汇量丰富的初中学生,可以描述事物的具体细节。
BasicInterpreter
The BasicInterpreter class is one of the predefined subclass of the Interpreter abstract class.
It simulates the effect of bytecode instructions by using seven sets of values, defined in the BasicValue class.
class info
第一个部分,BasicInterpreter类继承自Interpreter<BasicValue>类。
public class BasicInterpreter extends Interpreter<BasicValue> implements Opcodes {
}
fields
第二个部分,BasicInterpreter类定义的字段有哪些。
public class BasicInterpreter extends Interpreter<BasicValue> implements Opcodes {
public static final Type NULL_TYPE = Type.getObjectType("null");
}
constructors
第三个部分,BasicInterpreter类定义的构造方法有哪些。
public class BasicInterpreter extends Interpreter<BasicValue> implements Opcodes {
public BasicInterpreter() {
super(ASM9);
if (getClass() != BasicInterpreter.class) {
throw new IllegalStateException();
}
}
protected BasicInterpreter(int api) {
super(api);
}
}
methods
第四个部分,BasicInterpreter类定义的方法有哪些。下面这几个方法都是对Interpreter定义的方法进行重写。
public class BasicInterpreter extends Interpreter<BasicValue> implements Opcodes {
@Override
public BasicValue newValue(Type type) {
// 返回BasicValue定义的7个静态字段之一
}
@Override
public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException {
// 返回BasicValue定义的7个静态字段之一
}
@Override
public BasicValue copyOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException {
return value;
}
public BasicValue unaryOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException {
// 返回BasicValue定义的7个静态字段之一
}
public BasicValue binaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2) throws AnalyzerException {
// 返回BasicValue定义的7个静态字段之一
}
@Override
public BasicValue ternaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2, BasicValue value3) throws AnalyzerException {
return null;
}
@Override
public BasicValue naryOperation(AbstractInsnNode insn, List<? extends BasicValue> values) throws AnalyzerException {
// 返回BasicValue定义的7个静态字段之一
}
@Override
public void returnOperation(AbstractInsnNode insn, BasicValue value, BasicValue expected) throws AnalyzerException {
// Nothing to do.
}
@Override
public BasicValue merge(BasicValue value1, BasicValue value2) {
if (!value1.equals(value2)) {
return BasicValue.UNINITIALIZED_VALUE;
}
return value1;
}
}
示例:打印Frame的状态
Get type info for each variable and stack slot for each method instruction
Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicInterpreter());
Frame<BasicValue>[] frames = analyzer.analyze(className, mn);
for (Frame<BasicValue> f : frames) {
for (int i = 0; i < f.getLocals(); ++i) {
BasicValue local = f.getLocal(i);
// ... local.getType()
}
for (int j = 0; j < f.getStackSize(); ++j) {
BasicValue stack = f.getStack(j);
// ...
}
}
打印每条instruction对应的Frame状态:
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.BasicInterpreter;
import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.Frame;
import java.util.ArrayList;
import java.util.List;
public class HelloWorldAnalysisTree {
public static void main(String[] args) throws Exception {
String relative_path = "sample/HelloWorld.class";
String filepath = FileUtils.getFilePath(relative_path);
byte[] bytes = FileUtils.readBytes(filepath);
//(1)构建ClassReader
ClassReader cr = new ClassReader(bytes);
//(2)生成ClassNode
int api = Opcodes.ASM9;
ClassNode cn = new ClassNode(api);
int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
cr.accept(cn, parsingOptions);
//(3)进行分析
String className = cn.name;
List<MethodNode> methods = cn.methods;
MethodNode mn = methods.get(1);
Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicInterpreter());
Frame<BasicValue>[] frames = analyzer.analyze(className, mn);
for (Frame<BasicValue> f : frames) {
List<BasicValue> localList = new ArrayList<>();
for (int i = 0; i < f.getLocals(); ++i) {
BasicValue local = f.getLocal(i);
localList.add(local);
}
List<BasicValue> stackList = new ArrayList<>();
for (int j = 0; j < f.getStackSize(); ++j) {
BasicValue stack = f.getStack(j);
stackList.add(stack);
}
String line = FrameUtils.toLine(localList, stackList);
System.out.println(line);
}
}
}
细节:top、null和void的处理
在BasicInterpreter类当中,对于top值、null值和void进行了处理,但是其中有一些让人容易混淆的地方。
三者分别指什么
top:在JVM文档的4.7.4. The StackMapTable Attribute定义的一个类型,它表示当前local variable当中的某一个位置处于未初始化的状态。null或aconst_null:在Java语言当中,表现为null值;在.class文件中,表现为aconst_null,是JVM文档定义的一个指令,将一个null加载到operand stack上。void:方法的返回值是void类型。- 如果方法的返回值类型不是
void类型,假如说是一个int类型,那么它会将返回的int值加载到operand stack上。 - 如果返回值类型是
void类型,那么则不需要加载任何值到operand stack上。
- 如果方法的返回值类型不是
概念领域的转换
刚刚介绍的top、null和void都是在.class文件所存在的内容,接下来它进行转换成ASM当中的Type类型,再接着转换成ASM当中的Value类型,之后就可以结合Frame类来模拟local variable和operand stack的变化了。
.class --> ASM Type --> ASM Value
三个概念领域的关系可以表示成下图:
┌─────────────────────────────┐ | ┌──────────────────────────────────────────────┐
│ asm.jar │ | │ asm-analysis.jar │
│ ClassReader Type │ | │ │
│ ClassVisitor ClassWriter │ | │ Value Interpreter Frame Analyzer │
└─────────────────────────────┘ | └──────────────────────────────────────────────┘
---------------------------------------------------------------------------------------------------
| ┌──────────────────────────────────────────────────────┐
| │ operand stack │
┌────────────────────────┐ | │ ┌──────────────┐ JVM │
│ Internal Name │ | │ ├──────────────┤ Stack Frame │
│ Descriptor │ | │ ├──────────────┤ │
│ │ | │ ├──────────────┤ │
│ top, aconst_null, void │ | │ ├──────────────┤ │
│ │ | │ ├──────────────┤ local variable │
│ .class │ | │ ├──────────────┤ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │
│ ClassFile │ | │ └──────────────┘ └──┘ └──┘ └──┘ └──┘ └──┘ │
└────────────────────────┘ | │ 0 1 2 3 4 │
| └──────────────────────────────────────────────────────┘
转换过程
在下表当中,就是top、null和void三者相对应的转换值:
┌─────────────┬────────────────────────────┬────────────────────────────────┐
│ .class │ ASM Type │ ASM Value in Frame │
├─────────────┼────────────────────────────┼────────────────────────────────┤
│ top │ null │ BasicValue.UNINITIALIZED_VALUE │
├─────────────┼────────────────────────────┼────────────────────────────────┤
│ aconst_null │ BasicInterpreter.NULL_TYPE │ BasicValue.REFERENCE_VALUE │
├─────────────┼────────────────────────────┼────────────────────────────────┤
│ void │ Type.VOID_TYPE │ null │
└─────────────┴────────────────────────────┴────────────────────────────────┘
在上表当中,容易混淆的地方就是:第一列有aconst_null,第二列有null,第三列有null,但是这三者不在同一行,且所隶属的“领域”是不同的。
首先,是top的查找过程:
Analyzer.analyze()方法 –>Analyzer.computeInitialFrame()方法Interpreter.newEmptyValue()方法BasicInterpreter.newValue()方法
public abstract class Interpreter<V extends Value> {
public V newEmptyValue(int local) {
return newValue(null);
}
}
其次,是null或aconst_null的查找过程:
Analyzer.analyze()方法Frame.execute()方法的ACONST_NULL指令BasicInterpreter.newOperation()方法当中的ACONST_NULL指令 –>BasicInterpreter.newValue()方法
最后,是void的查找过程:
Analyzer.analyze()方法Frame.execute()方法调用方法的指令 –>Frame.executeInvokeInsn()方法BasicInterpreter.naryOperation()方法 –>BasicInterpreter.newValue()方法
如果遇到void类型的时候,它不应该向Frame当中添加任何值,因此其相应的BasicValue值为null。
在BasicInterpreter类当中,定义了一个NULL_TYPE字段:
public class BasicInterpreter extends Interpreter<BasicValue> implements Opcodes {
public static final Type NULL_TYPE = Type.getObjectType("null");
}
这个NULL_TYPE字段在newOperation方法中处理aconst_null指令时用到。
public class BasicInterpreter extends Interpreter<BasicValue> implements Opcodes {
@Override
public BasicValue newOperation(final AbstractInsnNode insn) throws AnalyzerException {
switch (insn.getOpcode()) {
case ACONST_NULL:
return newValue(NULL_TYPE); // aconst_null --> NULL_TYPE
// ...
case GETSTATIC:
return newValue(Type.getType(((FieldInsnNode) insn).desc));
case NEW:
return newValue(Type.getObjectType(((TypeInsnNode) insn).desc));
default:
throw new AssertionError();
}
}
@Override
public BasicValue newValue(final Type type) {
if (type == null) {
return BasicValue.UNINITIALIZED_VALUE; // top --> null --> UNINITIALIZED_VALUE
}
switch (type.getSort()) {
case Type.VOID:
return null; // void --> Type.VOID_TYPE --> null
case Type.BOOLEAN:
case Type.CHAR:
case Type.BYTE:
case Type.SHORT:
case Type.INT:
return BasicValue.INT_VALUE;
case Type.FLOAT:
return BasicValue.FLOAT_VALUE;
case Type.LONG:
return BasicValue.LONG_VALUE;
case Type.DOUBLE:
return BasicValue.DOUBLE_VALUE;
case Type.ARRAY:
case Type.OBJECT:
return BasicValue.REFERENCE_VALUE;
default:
throw new AssertionError();
}
}
@Override
public BasicValue naryOperation(AbstractInsnNode insn, final List<? extends BasicValue> values)
throws AnalyzerException {
int opcode = insn.getOpcode();
if (opcode == MULTIANEWARRAY) {
return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc));
} else if (opcode == INVOKEDYNAMIC) {
return newValue(Type.getReturnType(((InvokeDynamicInsnNode) insn).desc));
} else {
return newValue(Type.getReturnType(((MethodInsnNode) insn).desc));
}
}
}
总结
本文内容总结如下:
- 第一点,
BasicValue类实现了Value接口,它本质是在Type类型基础的进一步封装。在BasicValue类当中,定义了7个静态字段。 - 第二点,
BasicInterpreter类继承自Interpreter类,它就是利用BasicValue类当中定义的7个静态字段进行运算,得到的结果是这7个字段当中的任意一个值,或者是null值。 - 第三点,代码示例,打印出Frame(local variable和operand stack)当中的元素。
- 第四点,从细节上来说,对top、null和void进行区分。