AST操作
抽象語(yǔ)法樹AST的全面分析(一)
抽象語(yǔ)法樹AST的全面分析(二)
前面兩篇文章寫到了抽象語(yǔ)法樹的生成過程和語(yǔ)法樹的節(jié)點(diǎn)訪問,這篇文章來(lái)寫一下如何操作抽象語(yǔ)法樹骂蓖。
操作AST可以完成什么事情?
拿到了抽象語(yǔ)法樹定踱,等于我們拿到了整份的代碼动看,我們可以對(duì)所有的代碼進(jìn)行掃描,可以在特定的代碼中寫入一些邏輯:
- 清除或者添加日志洞慎;
- 對(duì)象調(diào)用的非空判斷痛单;
- 編寫我們特定的語(yǔ)法規(guī)則,對(duì)不符合規(guī)則的代碼進(jìn)行修改或優(yōu)化劲腿;
- 增刪改查旭绒。。焦人。
AST的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):AST操作屬于編譯器級(jí)別挥吵,對(duì)程序運(yùn)行完全沒有影響,效率相對(duì)其他AOP更高花椭;
缺點(diǎn):沒有官方文檔忽匈,操作比較復(fù)雜,需要自己摸索矿辽。
AST實(shí)操
一丹允、清除Log日志
創(chuàng)建一個(gè)java-library,主module依賴這個(gè)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"
主項(xiàng)目依賴ast_processor
annotationProcessor project(':ast_processor')
library中創(chuàng)建一個(gè)ASTProcessor類嫌松,讓它繼承AbstractProcessor,實(shí)現(xiàn)最基本的配置奕污。
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ASTProcessor extends AbstractProcessor {
private Messager mMessager; //用于打印數(shù)據(jù)
private Trees trees; //提供了待處理的抽象語(yǔ)法樹
private TreeMaker treeMaker;//TreeMaker 封裝了創(chuàng)建AST節(jié)點(diǎn)的一些方法
private Names names; //提供了創(chuàng)建標(biāo)識(shí)符的方法
private ASTInterf astInterf;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mMessager = processingEnvironment.getMessager();
trees = Trees.instance(processingEnvironment);//通過trees可以獲取到抽象語(yǔ)法書
Context context = ((JavacProcessingEnvironment) processingEnvironment).getContext();
treeMaker = TreeMaker.instance(context);
names = Names.instance(context);
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> 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);
//獲取所有語(yǔ)句,JCStatement代表一行代碼
List<JCTree.JCStatement> jcStatementList = jcBlock.getStatements();
if (jcStatementList == null || jcStatementList.isEmpty()){
return;
}
List<JCTree.JCStatement> newList = List.nil();//創(chuàng)建一個(gè)新的list,用于裝載不包含log的代碼語(yǔ)句
for (JCTree.JCStatement jcStatement : jcStatementList) {
if (!jcStatement.toString().contains(LOG_TAG)){
newList = newList.append(jcStatement);//加入非log的代碼語(yǔ)句
}else{
messager.printMessage(Diagnostic.Kind.NOTE, "clearLog: " + jcStatement.toString());
}
}
jcBlock.stats = newList;//修改代碼塊的語(yǔ)句list
}
}
小結(jié):復(fù)寫visitBlock()方法液走,獲取到所有的代碼塊碳默,對(duì)代碼塊中所有的代碼語(yǔ)句進(jìn)行遍歷,去掉包括Log的代碼行缘眶,重新賦值給jcBlock嘱根,非常簡(jiǎn)單。
上面兩張圖片巷懈,帶Log語(yǔ)句的java文件編譯成.class文件后该抒,Log語(yǔ)句成功的被去掉了。
二顶燕、難度升級(jí)一下凑保,手?jǐn)]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、創(chuàng)建DataOperationTranslator類(繼承TreeTranslator)欧引,并且在ASTProcessor中調(diào)用
public class DataOperationTranslator extends TreeTranslator {
}
public class ASTProcessor extends AbstractProcessor {
...
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> 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);
//創(chuàng)建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<JCTree.JCStatement> jcStatementList = List.nil();
jcStatementList = jcStatementList.append(jcStatement);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);//構(gòu)建代碼塊
List<JCTree.JCTypeParameter> methodGenericParams = List.nil();//泛型參數(shù)列表
List<JCTree.JCVariableDecl> parameters = List.nil();//參數(shù)列表
List<JCTree.JCExpression> throwsClauses = List.nil();//異常拋出列表
JCTree.JCExpression defaultValue = null;//非自定義注解類中的方法芝此,defaultValue為null
//最后構(gòu)建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<JCTree.JCVariableDecl> parameters = List.nil();//參數(shù)列表
JCTree.JCVariableDecl param = treeMaker.VarDef(
treeMaker.Modifiers(Flags.PARAMETER), jcVariableDecl.name, jcVariableDecl.vartype, null);
param.pos = jcVariableDecl.pos;//設(shè)置形參這一句不能少,不然會(huì)編譯報(bào)錯(cuò)(java.lang.AssertionError: Value of x -1)
parameters = parameters.append(param);
//this.xxx = xxx; setter方法中的賦值語(yǔ)句
JCTree.JCStatement jcStatement = treeMaker.Exec(treeMaker.Assign(
treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.name),
treeMaker.Ident(jcVariableDecl.name)));
List<JCTree.JCStatement> jcStatementList = List.nil();
jcStatementList = jcStatementList.append(jcStatement);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);//代碼塊
List<JCTree.JCTypeParameter> methodGenericParams = List.nil();//泛型參數(shù)列表
List<JCTree.JCExpression> throwsClauses = List.nil();//異常拋出列表
JCTree.JCExpression defaultValue = null;
//最后構(gòu)建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<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
for (JCTree jcTree : jcClassDecl.defs) {
if (jcTree instanceof JCTree.JCVariableDecl){
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) jcTree;
jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
}
}
List<JCTree.JCAnnotation> 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<JCTree.JCStatement> jcStatementList = List.nil();
jcStatementList = jcStatementList.append(jcStatement);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);
List<JCTree.JCTypeParameter> methodGenericParams = List.nil();//泛型參數(shù)列表
List<JCTree.JCVariableDecl> parameters = List.nil();//參數(shù)列表
List<JCTree.JCExpression> 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<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
for (JCTree jcTree : jcClassDecl.defs) {
if (jcTree instanceof JCTree.JCVariableDecl){
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) jcTree;
jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
}
}
List<JCTree.JCAnnotation> 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<JCTree.JCExpression> var1 = List.nil();
List<JCTree.JCExpression> var2 = List.nil();
for (JCTree.JCVariableDecl variableDecl:jcVariableDeclList) {
var1 = var1.append(typeTranslator(variableDecl.vartype));
var2 = var2.append(treeMaker.Ident(variableDecl.name));
}
//創(chuàng)建代碼:Objects.hash(xxx ...)
JCTree.JCStatement jcStatement =
treeMaker.Return(treeMaker.Apply(var1, memberAccess("java.util.Objects.hash"), var2));
List<JCTree.JCStatement> jcStatementList = List.nil();
jcStatementList = jcStatementList.append(jcStatement);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);
List<JCTree.JCTypeParameter> methodGenericParams = List.nil();//泛型參數(shù)列表
List<JCTree.JCVariableDecl> parameters = List.nil();//參數(shù)列表
List<JCTree.JCExpression> 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<JCTree.JCVariableDecl> notStringJcVariableDeclList = List.nil();
List<JCTree.JCVariableDecl> 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<JCTree.JCAnnotation> 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<JCTree.JCStatement> 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<JCTree.JCExpression> var1 = List.nil();
var1 = var1.append(memberAccess("java.lang.String"));
var1 = var1.append(memberAccess("java.lang.String"));
List<JCTree.JCExpression> 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語(yǔ)句
jcStatementList = jcStatementList.append(fourth);
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatementList);
List<JCTree.JCTypeParameter> methodGenericParams = List.nil();//泛型參數(shù)列表
List<JCTree.JCVariableDecl> parameters = List.nil();//參數(shù)列表
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);//添加參數(shù) Object o
List<JCTree.JCExpression> 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);
}
結(jié)果展示:
項(xiàng)目源碼:ASTDemo
總結(jié):AST操作主要還是復(fù)寫JCTree.Visitor中的visistXxx()的方法怎炊,獲取對(duì)應(yīng)的語(yǔ)法節(jié)點(diǎn),對(duì)該語(yǔ)法節(jié)點(diǎn)進(jìn)行操作用僧;調(diào)用TreeMaker的方法结胀,利用TreeMaker生成語(yǔ)法節(jié)點(diǎn)以及實(shí)現(xiàn)代碼的調(diào)用。由于沒有官方的文檔责循,總體比較難的還是API的調(diào)用糟港,以及編譯出錯(cuò)時(shí)無(wú)法準(zhǔn)確定位問題代碼。
參考:
AOP 最后一塊拼圖 | AST 抽象語(yǔ)法樹—— 最輕量級(jí)的AOP方法
安卓AOP之AST:抽象語(yǔ)法樹