AST操作
抽象语法树AST的全面分析(一)
抽象语法树AST的全面分析(二)
前面两篇文章写到了抽象语法树的生成过程和语法树的节点访问,这篇文章来写一下如何操作抽象语法树。
操作AST可以完成什么事情?
拿到了抽象语法树,等于我们拿到了整份的代码,我们可以对所有的代码进行扫描,可以在特定的代码中写入一些逻辑:
清除或者添加日志;
对象调用的非空判断;
编写我们特定的语法规则,对不符合规则的代码进行修改或优化;
增删改查。。。
AST的优缺点
优点:AST操作属于编译器级别,对程序运行完全没有影响,效率相对其他AOP更高;
缺点:没有官方文档,操作比较复杂,需要自己摸索。
AST实操
一、清除Log日志
创建一个java-library,主module依赖这个library,library的gradle配置如下
apply plugin: 'java-library'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.auto.service:auto-service:1.0-rc2'
implementation files('libs/tools.jar')
implementation project(':annotations')
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
主项目依赖ast_processor
annotationProcessor project(':ast_processor')
library中创建一个ASTProcessor类,让它继承AbstractProcessor,实现最基本的配置。
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ASTProcessor extends AbstractProcessor {
private Messager mMessager; //用于打印数据
private Trees trees; //提供了待处理的抽象语法树
private TreeMaker treeMaker;//TreeMaker 封装了创建AST节点的一些方法
private Names names; //提供了创建标识符的方法
private ASTInterf astInterf;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mMessager = processingEnvironment.getMessager();
trees = Trees.instance(processingEnvironment);//通过trees可以获取到抽象语法书
Context context = ((JavacProcessingEnvironment) processingEnvironment).getContext();
treeMaker = TreeMaker.instance(context);
names = Names.instance(context);
}
@Override
public Set getSupportedAnnotationTypes() {
Set stringSet = new LinkedHashSet<>();
stringSet.add("*");//* 指定所有注解
return stringSet;
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (!roundEnvironment.processingOver()) {
for (Element element : roundEnv.getRootElements()) {
if (element.getKind() == ElementKind.CLASS) {
JCTree tree = (JCTree) trees.getTree(element);
LogClearTranslator logClearTranslator = new LogClearTranslator(mMessager);
tree.accept(logClearTranslator);
}
}
}
return false;
}
}
LogClearTranslator类
public class LogClearTranslator extends TreeTranslator {
public final static String LOG_TAG = "Log.";
private Messager messager;
public LogClearTranslator(Messager messager) {
this.messager = messager;
}
/**
* 访问代码块
* */
@Override
public void visitBlock(JCTree.JCBlock jcBlock) {
super.visitBlock(jcBlock);
//获取所有语句,JCStatement代表一行代码
List jcStatementList = jcBlock.getStatements();
if (jcStatementList == null || jcStatementList.isEmpty()){
return;
}
List newList = List.nil();//创建一个新的list,用于装载不包含log的代码语句
for (JCTree.JCStatement jcStatement : jcStatementList) {
if (!jcStatement.toString().contains(LOG_TAG)){
newList = newList.append(jcStatement);//加入非log的代码语句
}else{
messager.printMessage(Diagnostic.Kind.NOTE, "clearLog: " + jcStatement.toString());
}
}
jcBlock.stats = newList;//修改代码块的语句list
}
}
小结:复写visitBlock()方法,获取到所有的代码块,对代码块中所有的代码语句进行遍历,去掉包括Log的代码行,重新赋值给jcBlock,非常简单。
MainActivit的java文件.png
MainActivit的class文件.png
上面两张图片,带Log语句的java文件编译成.class文件后,Log语句成功的被去掉了。
二、难度升级一下,手撸Getter, Setter, toString, hashCode, equals
1、自定义Data注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Data {}
2.给自定义的Bean添加@Data注解
@Data
public class TestBean {
private int heigth;
private int age;
private String nickName;
private int sex;
}
3、创建DataOperationTranslator类(继承TreeTranslator),并且在ASTProcessor中调用
public class DataOperationTranslator extends TreeTranslator {
}
public class ASTProcessor extends AbstractProcessor {
...
@Override
public Set getSupportedAnnotationTypes() {
Set stringSet = new LinkedHashSet<>();
stringSet.add("com.example.adams.annotations.Data");
return stringSet;
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (!roundEnvironment.processingOver()) {
for (Element element : roundEnv.getElementsAnnotatedWith(Data.class)) {//获取@Data注解的元素
if (element.getKind() == ElementKind.CLASS) {
JCTree tree = (JCTree) trees.getTree(element);
//创建DataOperationTranslator,传给tree
DataOperationTranslator operationTranslator = new DataOperationTranslator(mMessager, treeMaker, names);
tree.accept(operationTranslator);
}
}
}
return false;
}
}
4、getter
private JCTree.JCMethodDecl makeGetterMethod(JCTree.JCVariableDecl jcVariableDecl){
JCTree.JCModifiers jcModifiers = treeMaker.Modifiers(Flags.PUBLIC);//public
JCTree.JCExpression retrunType = jcVariableDecl.vartype;//方法返回类型
Name name = getterMethodName(jcVariableDecl);// 方法名getXxx
JCTree.JCStatement jcStatement = // retrun this.xxx
treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.name));
List jcStatementList = List.nil();
jcStatementList = jcStatementList.append(jcStatement);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);//构建代码块
List methodGenericParams = List.nil();//泛型参数列表
List parameters = List.nil();//参数列表
List throwsClauses = List.nil();//异常抛出列表
JCTree.JCExpression defaultValue = null;//非自定义注解类中的方法,defaultValue为null
//最后构建getter方法
JCTree.JCMethodDecl jcMethodDecl = treeMaker.MethodDef(jcModifiers, name, retrunType, methodGenericParams, parameters, throwsClauses, jcBlock, defaultValue);
return jcMethodDecl;
}
5、setter
private JCTree.JCMethodDecl makeSetterMethod(JCTree.JCVariableDecl jcVariableDecl){
JCTree.JCModifiers jcModifiers = treeMaker.Modifiers(Flags.PUBLIC);//public
JCTree.JCExpression retrunType = treeMaker.TypeIdent(TypeTag.VOID);//或 treeMaker.Type(new Type.JCVoidType())
Name name = setterMethodName(jcVariableDecl);// setXxx()
List parameters = List.nil();//参数列表
JCTree.JCVariableDecl param = treeMaker.VarDef(
treeMaker.Modifiers(Flags.PARAMETER), jcVariableDecl.name, jcVariableDecl.vartype, null);
param.pos = jcVariableDecl.pos;//设置形参这一句不能少,不然会编译报错(java.lang.AssertionError: Value of x -1)
parameters = parameters.append(param);
//this.xxx = xxx; setter方法中的赋值语句
JCTree.JCStatement jcStatement = treeMaker.Exec(treeMaker.Assign(
treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.name),
treeMaker.Ident(jcVariableDecl.name)));
List jcStatementList = List.nil();
jcStatementList = jcStatementList.append(jcStatement);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);//代码块
List methodGenericParams = List.nil();//泛型参数列表
List throwsClauses = List.nil();//异常抛出列表
JCTree.JCExpression defaultValue = null;
//最后构建setter方法
JCTree.JCMethodDecl jcMethodDecl = treeMaker.MethodDef(jcModifiers, name, retrunType, methodGenericParams, parameters, throwsClauses, jcBlock, defaultValue);
return jcMethodDecl;
}
6、toString
private JCTree.JCMethodDecl makeToStringMethod(JCTree.JCClassDecl jcClassDecl){
List jcVariableDeclList = List.nil();
for (JCTree jcTree : jcClassDecl.defs) {
if (jcTree instanceof JCTree.JCVariableDecl){
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) jcTree;
jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
}
}
List jcAnnotationList = List.nil();
JCTree.JCAnnotation jcAnnotation = treeMaker.Annotation(memberAccess("java.lang.Override"), List.nil());
jcAnnotationList = jcAnnotationList.append(jcAnnotation);
JCTree.JCModifiers jcModifiers = treeMaker.Modifiers(Flags.PUBLIC, jcAnnotationList);
JCTree.JCExpression retrunType = memberAccess("java.lang.String");
Name name = names.fromString("toString");
JCTree.JCExpression jcExpression = treeMaker.Literal(jcClassDecl.name + "{");
for (int i = 0; i < jcVariableDeclList.size(); i++) {
JCTree.JCVariableDecl jcVariableDecl = jcVariableDeclList.get(i);
if (i != 0){
jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal("," + jcVariableDecl.name.toString() + "="));
}else{
jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal(jcVariableDecl.name.toString() + "="));
}
if (jcVariableDecl.vartype.toString().contains("String")){
jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal("'"));
}
jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Ident(jcVariableDecl.name));
if (jcVariableDecl.vartype.toString().contains("String")){
jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal("'"));
}
}
jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal("}"));
JCTree.JCStatement jcStatement = treeMaker.Return(jcExpression);
List jcStatementList = List.nil();
jcStatementList = jcStatementList.append(jcStatement);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);
List methodGenericParams = List.nil();//泛型参数列表
List parameters = List.nil();//参数列表
List throwsClauses = List.nil();//异常抛出列表
JCTree.JCExpression defaultValue = null;
JCTree.JCMethodDecl jcMethodDecl = treeMaker.MethodDef(jcModifiers, name, retrunType, methodGenericParams, parameters, throwsClauses, jcBlock, defaultValue);
return jcMethodDecl;
}
7、hashCode
private JCTree.JCMethodDecl makeHashCodeMethod(JCTree.JCClassDecl jcClassDecl){
List jcVariableDeclList = List.nil();
for (JCTree jcTree : jcClassDecl.defs) {
if (jcTree instanceof JCTree.JCVariableDecl){
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) jcTree;
jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
}
}
List jcAnnotationList = List.nil();
JCTree.JCAnnotation jcAnnotation = treeMaker.Annotation(memberAccess("java.lang.Override"), List.nil());
jcAnnotationList = jcAnnotationList.append(jcAnnotation);
JCTree.JCModifiers jcModifiers = treeMaker.Modifiers(Flags.PUBLIC, jcAnnotationList);
Name name = names.fromString("hashCode");
JCTree.JCExpression retrunType = treeMaker.TypeIdent(TypeTag.INT);
List var1 = List.nil();
List var2 = List.nil();
for (JCTree.JCVariableDecl variableDecl:jcVariableDeclList) {
var1 = var1.append(typeTranslator(variableDecl.vartype));
var2 = var2.append(treeMaker.Ident(variableDecl.name));
}
//创建代码:Objects.hash(xxx ...)
JCTree.JCStatement jcStatement =
treeMaker.Return(treeMaker.Apply(var1, memberAccess("java.util.Objects.hash"), var2));
List jcStatementList = List.nil();
jcStatementList = jcStatementList.append(jcStatement);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);
List methodGenericParams = List.nil();//泛型参数列表
List parameters = List.nil();//参数列表
List throwsClauses = List.nil();//异常抛出列表
JCTree.JCExpression defaultValue = null;
JCTree.JCMethodDecl jcMethodDecl = treeMaker.MethodDef(jcModifiers, name, retrunType, methodGenericParams, parameters, throwsClauses, jcBlock, defaultValue);
return jcMethodDecl;
}
8、equals
private JCTree.JCMethodDecl makeEqualsMethod(JCTree.JCClassDecl jcClassDecl){
List notStringJcVariableDeclList = List.nil();
List stringJcVariableDeclList = List.nil();
for (JCTree jcTree : jcClassDecl.defs) {
if (jcTree instanceof JCTree.JCVariableDecl){
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) jcTree;
if (jcVariableDecl.vartype.toString().equals("String")){
stringJcVariableDeclList = stringJcVariableDeclList.append(jcVariableDecl);
}else{
notStringJcVariableDeclList = notStringJcVariableDeclList.append(jcVariableDecl);
}
}
}
List jcAnnotationList = List.nil();
JCTree.JCAnnotation jcAnnotation = treeMaker.Annotation(memberAccess("java.lang.Override"), List.nil());
jcAnnotationList = jcAnnotationList.append(jcAnnotation);
JCTree.JCModifiers jcModifiers = treeMaker.Modifiers(Flags.PUBLIC, jcAnnotationList);
Name name = names.fromString("equals");
JCTree.JCExpression retrunType = treeMaker.TypeIdent(TypeTag.BOOLEAN);
List jcStatementList = List.nil();
// if (this == o) return false;
JCTree.JCStatement zeroth = treeMaker.If(treeMaker.Binary(JCTree.Tag.EQ, treeMaker.Ident(names.fromString("this")), treeMaker.Ident(names.fromString("o"))),
treeMaker.Return(treeMaker.Literal(false)), null);
jcStatementList = jcStatementList.append(zeroth);
//if (!(o instanceof TestBean)) return false;
JCTree.JCStatement first = treeMaker.If(treeMaker.Unary(JCTree.Tag.NOT, treeMaker.TypeTest(treeMaker.Ident(names.fromString("o")), treeMaker.Ident(jcClassDecl.name))),
treeMaker.Return(treeMaker.Literal(false)), null);
jcStatementList = jcStatementList.append(first);
//TestBean testBean = (TestBean)o;
JCTree.JCVariableDecl second = treeMaker.VarDef(
treeMaker.Modifiers(0), names.fromString(toLowerCaseFirstOne(jcClassDecl.name.toString())), treeMaker.Ident(jcClassDecl.name),
treeMaker.TypeCast(treeMaker.Ident(jcClassDecl.name), treeMaker.Ident(names.fromString("o"))));
jcStatementList = jcStatementList.append(second);
JCTree.JCExpression jcExpression = null;
for (int i = 0; i < notStringJcVariableDeclList.size(); i++) {
JCTree.JCExpression isEq = treeMaker.Binary(JCTree.Tag.EQ,
treeMaker.Ident(notStringJcVariableDeclList.get(i).name),
treeMaker.Select(treeMaker.Ident(names.fromString(toLowerCaseFirstOne(jcClassDecl.name.toString()))),
notStringJcVariableDeclList.get(i).name));
if (jcExpression != null){
//&& this.age == testBean.age
jcExpression = treeMaker.Binary(JCTree.Tag.AND, jcExpression, isEq);
}else{
jcExpression = isEq;
}
}
for (int i = 0; i < stringJcVariableDeclList.size(); i++) {
List var1 = List.nil();
var1 = var1.append(memberAccess("java.lang.String"));
var1 = var1.append(memberAccess("java.lang.String"));
List var2 = List.nil();
var2 = var2.append(treeMaker.Ident(stringJcVariableDeclList.get(i).name));
var2 = var2.append(treeMaker.Select(treeMaker.Ident(names.fromString(toLowerCaseFirstOne(jcClassDecl.name.toString()))),
stringJcVariableDeclList.get(i).name));
JCTree.JCExpression isEq = treeMaker.Apply(var1, memberAccess("java.util.Objects.equals"), var2);
if (jcExpression != null){
//&& Objects.equals(this.nickName, testBean.nickName);
jcExpression = treeMaker.Binary(JCTree.Tag.AND, jcExpression, isEq);
}else{
jcExpression = isEq;
}
}
JCTree.JCStatement fourth = treeMaker.Return(jcExpression);//return语句
jcStatementList = jcStatementList.append(fourth);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);
List methodGenericParams = List.nil();//泛型参数列表
List parameters = List.nil();//参数列表
JCTree.JCVariableDecl param = treeMaker.VarDef(
treeMaker.Modifiers(Flags.PARAMETER), names.fromString("o"), memberAccess("java.lang.Object"), null);
param.pos = jcClassDecl.pos;
parameters = parameters.append(param);//添加参数 Object o
List throwsClauses = List.nil();//异常抛出列表
JCTree.JCExpression defaultValue = null;
JCTree.JCMethodDecl jcMethodDecl = treeMaker.MethodDef(jcModifiers, name, retrunType, methodGenericParams, parameters, throwsClauses, jcBlock, defaultValue);
return jcMethodDecl;
}
9、把上面的方法加入Bean中
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
super.visitClassDef(jcClassDecl);
for (JCTree jcTree : jcClassDecl.defs) {
if (jcTree instanceof JCTree.JCVariableDecl){
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) jcTree;
jcClassDecl.defs = jcClassDecl.defs.append(makeGetterMethod(jcVariableDecl));
jcClassDecl.defs = jcClassDecl.defs.append(makeSetterMethod(jcVariableDecl));
}
}
JCTree.JCMethodDecl toString = makeToStringMethod(jcClassDecl);
jcClassDecl.defs = jcClassDecl.defs.append(toString);
JCTree.JCMethodDecl hashCodeMethod = makeHashCodeMethod(jcClassDecl);
jcClassDecl.defs = jcClassDecl.defs.append(hashCodeMethod);
JCTree.JCMethodDecl equalsMethod = makeEqualsMethod(jcClassDecl);
jcClassDecl.defs = jcClassDecl.defs.append(equalsMethod);
}
结果展示:
TestBean的java文件.jpg
TestBean的class文件.jpg
项目源码:ASTDemo
总结:AST操作主要还是复写JCTree.Visitor中的visistXxx()的方法,获取对应的语法节点,对该语法节点进行操作;调用TreeMaker的方法,利用TreeMaker生成语法节点以及实现代码的调用。由于没有官方的文档,总体比较难的还是API的调用,以及编译出错时无法准确定位问题代码。