generate correct setter and getter

This commit is contained in:
金戟 2020-05-17 01:22:51 +08:00
parent bc49fda272
commit ce296710d9
8 changed files with 69 additions and 104 deletions

View File

@ -2,20 +2,20 @@ package com.alibaba.testable.generator;
import com.alibaba.testable.generator.model.Statement; import com.alibaba.testable.generator.model.Statement;
import com.alibaba.testable.generator.statement.CallSuperMethodStatementGenerator; import com.alibaba.testable.generator.statement.CallSuperMethodStatementGenerator;
import com.alibaba.testable.generator.statement.FieldGetterStatementGenerator;
import com.alibaba.testable.generator.statement.FieldSetterStatementGenerator;
import com.alibaba.testable.generator.statement.FieldStatementGenerator;
import com.alibaba.testable.translator.TestableClassDevRoleTranslator; import com.alibaba.testable.translator.TestableClassDevRoleTranslator;
import com.alibaba.testable.util.ConstPool; import com.alibaba.testable.util.ConstPool;
import com.alibaba.testable.util.StringUtil; import com.alibaba.testable.util.StringUtil;
import com.squareup.javapoet.*; import com.squareup.javapoet.*;
import com.sun.tools.javac.api.JavacTrees; import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Names;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -30,15 +30,17 @@ public class TestableClassDevRoleGenerator {
private final JavacTrees trees; private final JavacTrees trees;
private final TreeMaker treeMaker; private final TreeMaker treeMaker;
private final Names names;
public TestableClassDevRoleGenerator(JavacTrees trees, TreeMaker treeMaker) { public TestableClassDevRoleGenerator(JavacTrees trees, TreeMaker treeMaker, Names names) {
this.trees = trees; this.trees = trees;
this.treeMaker = treeMaker; this.treeMaker = treeMaker;
this.names = names;
} }
public String fetch(Element clazz, String packageName, String className) { public String fetch(Symbol.ClassSymbol clazz, String packageName, String className) {
JCTree tree = trees.getTree(clazz); JCTree tree = trees.getTree(clazz);
TestableClassDevRoleTranslator translator = new TestableClassDevRoleTranslator(treeMaker); TestableClassDevRoleTranslator translator = new TestableClassDevRoleTranslator(treeMaker, names);
tree.accept(translator); tree.accept(translator);
List<MethodSpec> methodSpecs = new ArrayList<>(); List<MethodSpec> methodSpecs = new ArrayList<>();
@ -81,27 +83,36 @@ public class TestableClassDevRoleGenerator {
.build(); .build();
} }
private MethodSpec buildFieldGetter(Element classElement, JCTree.JCVariableDecl field) { private MethodSpec buildFieldGetter(Symbol.ClassSymbol classElement, JCTree.JCVariableDecl field) {
return buildFieldAccessor(classElement, field, "TestableGet", String fieldName = field.name.toString();
TypeName.get(field.vartype.type), new FieldGetterStatementGenerator()); return MethodSpec.methodBuilder(fieldName + ConstPool.TESTABLE_GET_METHOD_PREFIX)
}
private MethodSpec buildFieldSetter(Element classElement, JCTree.JCVariableDecl field) {
return buildFieldAccessor(classElement, field, "TestableSet",
TypeName.VOID, new FieldSetterStatementGenerator());
}
private MethodSpec buildFieldAccessor(Element classElement, JCTree.JCVariableDecl field, String prefix,
TypeName returnType, FieldStatementGenerator generator) {
MethodSpec.Builder builder = MethodSpec.methodBuilder(field.name.toString() + prefix)
.addModifiers(Modifier.PUBLIC) .addModifiers(Modifier.PUBLIC)
.returns(returnType); .returns(TypeName.get(field.vartype.type))
String className = classElement.getSimpleName().toString(); .beginControlFlow("try")
Statement[] statements = generator.fetch(className, field); .addStatement("$T field = $T.class.getDeclaredField(\"$N\")", Field.class, classElement.type, fieldName)
for (Statement s : statements) { .addStatement("field.setAccessible(true)")
builder.addStatement(s.getLine(), s.getParams()); .addStatement("return ($T)field.get(this)", field.vartype.type)
} .nextControlFlow("catch ($T e)", Exception.class)
return builder.build(); .addStatement("e.printStackTrace()")
.addStatement("return null")
.endControlFlow()
.build();
}
private MethodSpec buildFieldSetter(Symbol.ClassSymbol classElement, JCTree.JCVariableDecl field) {
String fieldName = field.name.toString();
return MethodSpec.methodBuilder(fieldName + ConstPool.TESTABLE_SET_METHOD_PREFIX)
.addModifiers(Modifier.PUBLIC)
.addParameter(getParameterSpec(field))
.returns(TypeName.VOID)
.beginControlFlow("try")
.addStatement("$T field = $T.class.getDeclaredField(\"$N\")", Field.class, classElement.type, fieldName)
.addStatement("field.setAccessible(true)")
.addStatement("field.set(this, $N)", fieldName)
.nextControlFlow("catch ($T e)", Exception.class)
.addStatement("e.printStackTrace()")
.endControlFlow()
.build();
} }
private MethodSpec buildMemberMethod(Element classElement, JCTree.JCMethodDecl method) { private MethodSpec buildMemberMethod(Element classElement, JCTree.JCMethodDecl method) {

View File

@ -1,25 +0,0 @@
package com.alibaba.testable.generator.statement;
import com.alibaba.testable.generator.model.Statement;
import com.alibaba.testable.util.ConstPool;
import com.alibaba.testable.util.StringUtil;
import com.sun.tools.javac.tree.JCTree;
import javax.lang.model.element.Modifier;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Generate field getter method statement
*
* @author flin
*/
public class FieldGetterStatementGenerator implements FieldStatementGenerator {
@Override
public Statement[] fetch(String className, JCTree.JCVariableDecl field) {
return new Statement[] {new Statement("return null", new Object[] {})};
}
}

View File

@ -1,25 +0,0 @@
package com.alibaba.testable.generator.statement;
import com.alibaba.testable.generator.model.Statement;
import com.alibaba.testable.util.ConstPool;
import com.alibaba.testable.util.StringUtil;
import com.sun.tools.javac.tree.JCTree;
import javax.lang.model.element.Modifier;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Generate field setter method statement
*
* @author flin
*/
public class FieldSetterStatementGenerator implements FieldStatementGenerator {
@Override
public Statement[] fetch(String className, JCTree.JCVariableDecl field) {
return new Statement[]{};
}
}

View File

@ -1,14 +0,0 @@
package com.alibaba.testable.generator.statement;
import com.alibaba.testable.generator.model.Statement;
import com.sun.tools.javac.tree.JCTree;
/**
* @author flin
*/
public interface FieldStatementGenerator {
/**
* Generate field access code statement
*/
Statement[] fetch(String className, JCTree.JCVariableDecl field);
}

View File

@ -86,7 +86,7 @@ public class TestableProcessor extends BaseProcessor {
String fullQualityTypeName = packageName + "." + testableTypeName; String fullQualityTypeName = packageName + "." + testableTypeName;
try { try {
writeSourceFile(fullQualityTypeName, writeSourceFile(fullQualityTypeName,
new TestableClassDevRoleGenerator(trees, treeMaker).fetch(clazz, packageName, testableTypeName)); new TestableClassDevRoleGenerator(trees, treeMaker, names).fetch(clazz, packageName, testableTypeName));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -94,7 +94,7 @@ public class TestableProcessor extends BaseProcessor {
private void processTestRoleClassElement(Symbol.ClassSymbol clazz) { private void processTestRoleClassElement(Symbol.ClassSymbol clazz) {
JCTree tree = trees.getTree(clazz); JCTree tree = trees.getTree(clazz);
tree.accept(new TestableClassTestRoleTranslator(getPkgName(clazz), getOriginClassName(clazz), treeMaker)); tree.accept(new TestableClassTestRoleTranslator(getPkgName(clazz), getOriginClassName(clazz), treeMaker, names));
} }
private String getPkgName(Symbol.ClassSymbol clazz) { private String getPkgName(Symbol.ClassSymbol clazz) {

View File

@ -10,6 +10,7 @@ import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
@ -21,6 +22,7 @@ import java.lang.reflect.Modifier;
public class TestableClassDevRoleTranslator extends TreeTranslator { public class TestableClassDevRoleTranslator extends TreeTranslator {
private final TreeMaker treeMaker; private final TreeMaker treeMaker;
private final Names names;
/** /**
* Methods to inject * Methods to inject
@ -40,8 +42,9 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
return fields; return fields;
} }
public TestableClassDevRoleTranslator(TreeMaker treeMaker) { public TestableClassDevRoleTranslator(TreeMaker treeMaker, Names names) {
this.treeMaker = treeMaker; this.treeMaker = treeMaker;
this.names = names;
} }
@Override @Override
@ -135,9 +138,8 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
if (isNewOperation(expr)) { if (isNewOperation(expr)) {
JCTree.JCNewClass newClassExpr = (JCTree.JCNewClass)expr; JCTree.JCNewClass newClassExpr = (JCTree.JCNewClass)expr;
Name className = ((JCTree.JCIdent)newClassExpr.clazz).name; Name className = ((JCTree.JCIdent)newClassExpr.clazz).name;
Name.Table nameTable = className.table;
try { try {
return getStaticNewCall(newClassExpr, className, nameTable); return getStaticNewCall(newClassExpr, className);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -149,14 +151,13 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
return expr != null && expr.getClass().equals(JCTree.JCNewClass.class); return expr != null && expr.getClass().equals(JCTree.JCNewClass.class);
} }
private TestableMethodInvocation getStaticNewCall(JCTree.JCNewClass newClassExpr, Name className, private TestableMethodInvocation getStaticNewCall(JCTree.JCNewClass newClassExpr, Name className) {
Name.Table nameTable) { TestableFieldAccess snClass = new TestableFieldAccess(treeMaker.Ident(names.fromString(ConstPool.SN_PKG)),
TestableFieldAccess snClass = new TestableFieldAccess(treeMaker.Ident(nameTable.fromString(ConstPool.SN_PKG)), names.fromString(ConstPool.SN_CLS), null);
nameTable.fromString(ConstPool.SN_CLS), null);
TestableFieldAccess snMethod = new TestableFieldAccess(snClass, TestableFieldAccess snMethod = new TestableFieldAccess(snClass,
nameTable.fromString(ConstPool.SN_METHOD), null); names.fromString(ConstPool.SN_METHOD), null);
JCTree.JCExpression classType = new TestableFieldAccess(treeMaker.Ident(className), JCTree.JCExpression classType = new TestableFieldAccess(treeMaker.Ident(className),
nameTable.fromString("class"), null); names.fromString("class"), null);
ListBuffer<JCTree.JCExpression> args = ListBuffer.of(classType); ListBuffer<JCTree.JCExpression> args = ListBuffer.of(classType);
args.addAll(newClassExpr.args); args.addAll(newClassExpr.args);
return new TestableMethodInvocation(null, snMethod, args.toList()); return new TestableMethodInvocation(null, snMethod, args.toList());

View File

@ -1,10 +1,13 @@
package com.alibaba.testable.translator; package com.alibaba.testable.translator;
import com.alibaba.testable.translator.tree.TestableFieldAccess;
import com.alibaba.testable.translator.tree.TestableMethodInvocation;
import com.alibaba.testable.util.ConstPool; import com.alibaba.testable.util.ConstPool;
import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator; import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -18,13 +21,15 @@ import java.util.List;
public class TestableClassTestRoleTranslator extends TreeTranslator { public class TestableClassTestRoleTranslator extends TreeTranslator {
private TreeMaker treeMaker; private TreeMaker treeMaker;
private Names names;
private String sourceClassName; private String sourceClassName;
private List<Name> sourceClassIns = new ArrayList<>(); private List<Name> sourceClassIns = new ArrayList<>();
private List<String> stubbornFields = new ArrayList<>(); private List<String> stubbornFields = new ArrayList<>();
public TestableClassTestRoleTranslator(String pkgName, String className, TreeMaker treeMaker) { public TestableClassTestRoleTranslator(String pkgName, String className, TreeMaker treeMaker, Names names) {
this.sourceClassName = className; this.sourceClassName = className;
this.treeMaker = treeMaker; this.treeMaker = treeMaker;
this.names = names;
try { try {
stubbornFields = Arrays.asList( stubbornFields = Arrays.asList(
(String[])Class.forName(pkgName + "." + className + ConstPool.TESTABLE) (String[])Class.forName(pkgName + "." + className + ConstPool.TESTABLE)
@ -72,11 +77,20 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
public void visitExec(JCTree.JCExpressionStatement jcExpressionStatement) { public void visitExec(JCTree.JCExpressionStatement jcExpressionStatement) {
if (jcExpressionStatement.expr.getClass().equals(JCTree.JCAssign.class) && if (jcExpressionStatement.expr.getClass().equals(JCTree.JCAssign.class) &&
isAssignStubbornField((JCTree.JCAssign)jcExpressionStatement.expr)) { isAssignStubbornField((JCTree.JCAssign)jcExpressionStatement.expr)) {
//jcExpressionStatement.expr = JCTree.JCAssign assign = (JCTree.JCAssign)jcExpressionStatement.expr;
TestableFieldAccess stubbornSetter = new TestableFieldAccess(((JCTree.JCFieldAccess)assign.lhs).selected,
getStubbornSetterMethodName(assign), null);
jcExpressionStatement.expr = new TestableMethodInvocation(null, stubbornSetter,
com.sun.tools.javac.util.List.of(assign.rhs));
} }
super.visitExec(jcExpressionStatement); super.visitExec(jcExpressionStatement);
} }
private Name getStubbornSetterMethodName(JCTree.JCAssign assign) {
String name = ((JCTree.JCFieldAccess)assign.lhs).name.toString() + ConstPool.TESTABLE_SET_METHOD_PREFIX;
return names.fromString(name);
}
private boolean isAssignStubbornField(JCTree.JCAssign expr) { private boolean isAssignStubbornField(JCTree.JCAssign expr) {
return expr.lhs.getClass().equals(JCTree.JCFieldAccess.class) && return expr.lhs.getClass().equals(JCTree.JCFieldAccess.class) &&
sourceClassIns.contains(((JCTree.JCIdent)((JCTree.JCFieldAccess)(expr).lhs).selected).name) && sourceClassIns.contains(((JCTree.JCIdent)((JCTree.JCFieldAccess)(expr).lhs).selected).name) &&
@ -85,7 +99,7 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
private JCTree.JCIdent getTestableClassIdent(JCTree.JCExpression clazz) { private JCTree.JCIdent getTestableClassIdent(JCTree.JCExpression clazz) {
Name className = ((JCTree.JCIdent)clazz).name; Name className = ((JCTree.JCIdent)clazz).name;
return treeMaker.Ident(className.table.fromString(className + ConstPool.TESTABLE)); return treeMaker.Ident(names.fromString(className + ConstPool.TESTABLE));
} }
} }

View File

@ -13,5 +13,8 @@ public final class ConstPool {
public static final String SN_METHOD = "w"; public static final String SN_METHOD = "w";
public static final String SN_PKG_CLS = "n.e"; public static final String SN_PKG_CLS = "n.e";
public static final String STUBBORN_FIELD_METHOD = "stubbornField"; public static final String STUBBORN_FIELD_METHOD = "stubbornField";
public static final String TESTABLE_GET_METHOD_PREFIX = "TestableGet";
public static final String TESTABLE_SET_METHOD_PREFIX = "TestableSet";
} }