handle static member access

This commit is contained in:
金戟 2020-12-20 21:30:34 +08:00
parent 6b4467a03b
commit 03253e529d
4 changed files with 107 additions and 29 deletions

View File

@ -32,14 +32,14 @@ class DemoPrivateAccessTest {
@Test @Test
void should_able_to_access_private_static_method() { void should_able_to_access_private_static_method() {
//assertEquals("hello + 1", DemoPrivateAccess.privateStaticFunc("hello", 1)); assertEquals("hello + 1", DemoPrivateAccess.privateStaticFunc("hello", 1));
assertEquals("hello + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFunc", "hello", 1)); assertEquals("hello + 1", PrivateAccessor.invokeStatic(DemoPrivateAccess.class, "privateStaticFunc", "hello", 1));
} }
@Test @Test
void should_able_to_access_private_static_field() { void should_able_to_access_private_static_field() {
//DemoPrivateAccess.staticCount = 2; DemoPrivateAccess.staticCount = 2;
//assertEquals(new Integer(2), DemoPrivateAccess.staticCount); assertEquals(new Integer(2), DemoPrivateAccess.staticCount);
PrivateAccessor.setStatic(DemoPrivateAccess.class, "staticCount", 3); PrivateAccessor.setStatic(DemoPrivateAccess.class, "staticCount", 3);
assertEquals(new Integer(3), PrivateAccessor.getStatic(DemoPrivateAccess.class, "staticCount")); assertEquals(new Integer(3), PrivateAccessor.getStatic(DemoPrivateAccess.class, "staticCount"));

View File

@ -37,4 +37,32 @@ public class PrivateAccessStatementGenerator extends BaseGenerator {
params.addAll(expr.args); params.addAll(expr.args);
return cx.treeMaker.Apply(List.<JCExpression>nil(), invoker, params.toList()); return cx.treeMaker.Apply(List.<JCExpression>nil(), invoker, params.toList());
} }
public JCExpression fetchStaticGetterStatement(JCFieldAccess access) {
JCFieldAccess getter = cx.treeMaker.Select(nameToExpression(ConstPool.TESTABLE_PRIVATE_ACCESSOR),
cx.names.fromString("getStatic"));
JCExpression classField = cx.treeMaker.Select(access.selected, cx.names.fromString("class"));
return cx.treeMaker.Apply(List.<JCExpression>nil(), getter, List.of(classField,
cx.treeMaker.Literal(access.name.toString())));
}
public JCExpression fetchStaticSetterStatement(JCAssign assign) {
JCFieldAccess setter = cx.treeMaker.Select(nameToExpression(ConstPool.TESTABLE_PRIVATE_ACCESSOR),
cx.names.fromString("setStatic"));
JCExpression selected = ((JCFieldAccess)assign.lhs).selected;
JCExpression classField = cx.treeMaker.Select(selected, cx.names.fromString("class"));
return cx.treeMaker.Apply(List.<JCExpression>nil(), setter, List.of(classField,
cx.treeMaker.Literal(((JCFieldAccess)assign.lhs).name.toString()), assign.rhs));
}
public JCExpression fetchStaticInvokeStatement(JCMethodInvocation expr) {
JCFieldAccess invoker = cx.treeMaker.Select(nameToExpression(ConstPool.TESTABLE_PRIVATE_ACCESSOR),
cx.names.fromString("invokeStatic"));
JCExpression selected = ((JCFieldAccess)expr.meth).selected;
JCExpression classField = cx.treeMaker.Select(selected, cx.names.fromString("class"));
ListBuffer<JCExpression> params = ListBuffer.of(classField);
params.add(cx.treeMaker.Literal(((JCFieldAccess)expr.meth).name.toString()));
params.addAll(expr.args);
return cx.treeMaker.Apply(List.<JCExpression>nil(), invoker, params.toList());
}
} }

View File

@ -0,0 +1,20 @@
package com.alibaba.testable.processor.model;
public enum MemberType {
/**
* Private member or final member
*/
PRIVATE_OR_FINAL,
/**
* Static private member or Static final member
*/
STATIC_PRIVATE,
/**
* None private member
*/
NONE_PRIVATE
}

View File

@ -2,6 +2,7 @@ package com.alibaba.testable.processor.translator;
import com.alibaba.testable.processor.constant.ConstPool; import com.alibaba.testable.processor.constant.ConstPool;
import com.alibaba.testable.processor.generator.PrivateAccessStatementGenerator; import com.alibaba.testable.processor.generator.PrivateAccessStatementGenerator;
import com.alibaba.testable.processor.model.MemberType;
import com.alibaba.testable.processor.model.TestableContext; import com.alibaba.testable.processor.model.TestableContext;
import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.ListBuffer;
@ -23,7 +24,7 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
/** /**
* Name of source class * Name of source class
*/ */
private final String sourceClassName; private final Name sourceClassName;
/** /**
* Fields of source class instance in the test class * Fields of source class instance in the test class
*/ */
@ -40,11 +41,12 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
private final PrivateAccessStatementGenerator privateAccessStatementGenerator; private final PrivateAccessStatementGenerator privateAccessStatementGenerator;
public EnablePrivateAccessTranslator(String pkgName, String testClassName, TestableContext cx) { public EnablePrivateAccessTranslator(String pkgName, String testClassName, TestableContext cx) {
this.sourceClassName = testClassName.substring(0, testClassName.length() - ConstPool.TEST_POSTFIX.length()); String sourceClass = testClassName.substring(0, testClassName.length() - ConstPool.TEST_POSTFIX.length());
this.privateAccessStatementGenerator = new PrivateAccessStatementGenerator(cx); this.privateAccessStatementGenerator = new PrivateAccessStatementGenerator(cx);
this.sourceClassName = cx.names.fromString(sourceClass);
try { try {
Class<?> cls = null; Class<?> cls = null;
String sourceClassFullName = pkgName + "." + sourceClassName; String sourceClassFullName = pkgName + "." + sourceClass;
try { try {
cls = Class.forName(sourceClassFullName); cls = Class.forName(sourceClassFullName);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
@ -77,7 +79,7 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
public void visitVarDef(JCVariableDecl jcVariableDecl) { public void visitVarDef(JCVariableDecl jcVariableDecl) {
super.visitVarDef(jcVariableDecl); super.visitVarDef(jcVariableDecl);
if (jcVariableDecl.vartype.getClass().equals(JCIdent.class) && if (jcVariableDecl.vartype.getClass().equals(JCIdent.class) &&
((JCIdent)jcVariableDecl.vartype).name.toString().equals(sourceClassName)) { ((JCIdent)jcVariableDecl.vartype).name.equals(sourceClassName)) {
sourceClassIns.add(jcVariableDecl.name); sourceClassIns.add(jcVariableDecl.name);
} }
} }
@ -89,10 +91,15 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
@Override @Override
public void visitExec(JCExpressionStatement jcExpressionStatement) { public void visitExec(JCExpressionStatement jcExpressionStatement) {
// visitExec could be an assign statement to a private field // visitExec could be an assign statement to a private field
if (jcExpressionStatement.expr.getClass().equals(JCAssign.class) && if (jcExpressionStatement.expr.getClass().equals(JCAssign.class)) {
isPrivateField((JCAssign)jcExpressionStatement.expr)) { MemberType memberType = checkSetterType((JCAssign)jcExpressionStatement.expr);
jcExpressionStatement.expr = privateAccessStatementGenerator.fetchSetterStatement( if (memberType.equals(MemberType.PRIVATE_OR_FINAL)) {
(JCAssign)jcExpressionStatement.expr); jcExpressionStatement.expr = privateAccessStatementGenerator.fetchSetterStatement(
(JCAssign)jcExpressionStatement.expr);
} else if (memberType.equals(MemberType.STATIC_PRIVATE)) {
jcExpressionStatement.expr = privateAccessStatementGenerator.fetchStaticSetterStatement(
(JCAssign)jcExpressionStatement.expr);
}
} }
// visitExec could be an invoke // visitExec could be an invoke
jcExpressionStatement.expr = checkAndExchange(jcExpressionStatement.expr); jcExpressionStatement.expr = checkAndExchange(jcExpressionStatement.expr);
@ -129,36 +136,59 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
@Override @Override
protected JCExpression checkAndExchange(JCExpression expr) { protected JCExpression checkAndExchange(JCExpression expr) {
// check is accessing a private field of source class // check is accessing a private field of source class
if (expr.getClass().equals(JCFieldAccess.class) && if (expr.getClass().equals(JCFieldAccess.class)) {
isPrivateField((JCFieldAccess)expr)) { MemberType memberType = checkGetterType((JCFieldAccess)expr);
expr = privateAccessStatementGenerator.fetchGetterStatement((JCFieldAccess)expr); if (memberType.equals(MemberType.PRIVATE_OR_FINAL)) {
expr = privateAccessStatementGenerator.fetchGetterStatement((JCFieldAccess)expr);
} else if (memberType.equals(MemberType.STATIC_PRIVATE)) {
expr = privateAccessStatementGenerator.fetchStaticGetterStatement((JCFieldAccess)expr);
}
} }
// check is invoking a private method of source class // check is invoking a private method of source class
if (expr.getClass().equals(JCMethodInvocation.class) && if (expr.getClass().equals(JCMethodInvocation.class)) {
isPrivateMethod((JCMethodInvocation)expr)) { MemberType memberType = checkInvokeType((JCMethodInvocation)expr);
expr = privateAccessStatementGenerator.fetchInvokeStatement((JCMethodInvocation)expr); if (memberType.equals(MemberType.PRIVATE_OR_FINAL)) {
expr = privateAccessStatementGenerator.fetchInvokeStatement((JCMethodInvocation)expr);
} else if (memberType.equals(MemberType.STATIC_PRIVATE)) {
expr = privateAccessStatementGenerator.fetchStaticInvokeStatement((JCMethodInvocation)expr);
}
} }
return expr; return expr;
} }
private boolean isPrivateField(JCFieldAccess access) { private MemberType checkGetterType(JCFieldAccess access) {
return access.selected.getClass().equals(JCIdent.class) && if (access.selected.getClass().equals(JCIdent.class) &&
sourceClassIns.contains(((JCIdent)access.selected).name) && privateOrFinalFields.contains(access.name.toString())) {
privateOrFinalFields.contains(access.name.toString()); return checkSourceClassOrIns(((JCIdent)access.selected).name);
}
return MemberType.NONE_PRIVATE;
} }
private boolean isPrivateField(JCAssign assign) { private MemberType checkSetterType(JCAssign assign) {
return assign.lhs.getClass().equals(JCFieldAccess.class) && if (assign.lhs.getClass().equals(JCFieldAccess.class) &&
((JCFieldAccess)(assign).lhs).selected.getClass().equals(JCIdent.class) && ((JCFieldAccess)(assign).lhs).selected.getClass().equals(JCIdent.class) &&
sourceClassIns.contains(((JCIdent)((JCFieldAccess)(assign).lhs).selected).name) && privateOrFinalFields.contains(((JCFieldAccess)(assign).lhs).name.toString())) {
privateOrFinalFields.contains(((JCFieldAccess)(assign).lhs).name.toString()); return checkSourceClassOrIns(((JCIdent)((JCFieldAccess)(assign).lhs).selected).name);
}
return MemberType.NONE_PRIVATE;
} }
private boolean isPrivateMethod(JCMethodInvocation expr) { private MemberType checkInvokeType(JCMethodInvocation expr) {
return expr.meth.getClass().equals(JCFieldAccess.class) && if (expr.meth.getClass().equals(JCFieldAccess.class) &&
((JCFieldAccess)(expr).meth).selected.getClass().equals(JCIdent.class) && ((JCFieldAccess)(expr).meth).selected.getClass().equals(JCIdent.class) &&
sourceClassIns.contains(((JCIdent)((JCFieldAccess)(expr).meth).selected).name) && privateMethods.contains(((JCFieldAccess)(expr).meth).name.toString())) {
privateMethods.contains(((JCFieldAccess)(expr).meth).name.toString()); return checkSourceClassOrIns(((JCIdent)((JCFieldAccess)(expr).meth).selected).name);
}
return MemberType.NONE_PRIVATE;
}
private MemberType checkSourceClassOrIns(Name name) {
if (sourceClassName.equals(name)) {
return MemberType.STATIC_PRIVATE;
} else if (sourceClassIns.contains(name)) {
return MemberType.PRIVATE_OR_FINAL;
}
return MemberType.NONE_PRIVATE;
} }
} }