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.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.util.ConstPool;
import com.alibaba.testable.util.StringUtil;
import com.squareup.javapoet.*;
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.tree.JCTree;
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.Modifier;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@ -30,15 +30,17 @@ public class TestableClassDevRoleGenerator {
private final JavacTrees trees;
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.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);
TestableClassDevRoleTranslator translator = new TestableClassDevRoleTranslator(treeMaker);
TestableClassDevRoleTranslator translator = new TestableClassDevRoleTranslator(treeMaker, names);
tree.accept(translator);
List<MethodSpec> methodSpecs = new ArrayList<>();
@ -81,27 +83,36 @@ public class TestableClassDevRoleGenerator {
.build();
}
private MethodSpec buildFieldGetter(Element classElement, JCTree.JCVariableDecl field) {
return buildFieldAccessor(classElement, field, "TestableGet",
TypeName.get(field.vartype.type), new FieldGetterStatementGenerator());
}
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)
private MethodSpec buildFieldGetter(Symbol.ClassSymbol classElement, JCTree.JCVariableDecl field) {
String fieldName = field.name.toString();
return MethodSpec.methodBuilder(fieldName + ConstPool.TESTABLE_GET_METHOD_PREFIX)
.addModifiers(Modifier.PUBLIC)
.returns(returnType);
String className = classElement.getSimpleName().toString();
Statement[] statements = generator.fetch(className, field);
for (Statement s : statements) {
builder.addStatement(s.getLine(), s.getParams());
.returns(TypeName.get(field.vartype.type))
.beginControlFlow("try")
.addStatement("$T field = $T.class.getDeclaredField(\"$N\")", Field.class, classElement.type, fieldName)
.addStatement("field.setAccessible(true)")
.addStatement("return ($T)field.get(this)", field.vartype.type)
.nextControlFlow("catch ($T e)", Exception.class)
.addStatement("e.printStackTrace()")
.addStatement("return null")
.endControlFlow()
.build();
}
return builder.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) {

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;
try {
writeSourceFile(fullQualityTypeName,
new TestableClassDevRoleGenerator(trees, treeMaker).fetch(clazz, packageName, testableTypeName));
new TestableClassDevRoleGenerator(trees, treeMaker, names).fetch(clazz, packageName, testableTypeName));
} catch (IOException e) {
e.printStackTrace();
}
@ -94,7 +94,7 @@ public class TestableProcessor extends BaseProcessor {
private void processTestRoleClassElement(Symbol.ClassSymbol 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) {

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

View File

@ -1,10 +1,13 @@
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.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import java.util.ArrayList;
import java.util.Arrays;
@ -18,13 +21,15 @@ import java.util.List;
public class TestableClassTestRoleTranslator extends TreeTranslator {
private TreeMaker treeMaker;
private Names names;
private String sourceClassName;
private List<Name> sourceClassIns = 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.treeMaker = treeMaker;
this.names = names;
try {
stubbornFields = Arrays.asList(
(String[])Class.forName(pkgName + "." + className + ConstPool.TESTABLE)
@ -72,11 +77,20 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
public void visitExec(JCTree.JCExpressionStatement jcExpressionStatement) {
if (jcExpressionStatement.expr.getClass().equals(JCTree.JCAssign.class) &&
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);
}
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) {
return expr.lhs.getClass().equals(JCTree.JCFieldAccess.class) &&
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) {
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_PKG_CLS = "n.e";
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";
}