check method parameter count

This commit is contained in:
金戟
2021-01-31 15:10:34 +08:00
parent 7029579534
commit 39c788cd02
8 changed files with 117 additions and 33 deletions

View File

@@ -0,0 +1,30 @@
package com.alibaba.testable.processor.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author flin
*/
public class MemberRecord {
/**
* Record private and final fields
*/
public final List<String> privateOrFinalFields = new ArrayList<String>();
/**
* Record non-private fields
*/
public final List<String> nonPrivateNorFinalFields = new ArrayList<String>();
/**
* Record private methods and possible parameter counts (negative number means large or equals)
*/
public final Map<String, List<Integer>> privateMethods = new HashMap<String, List<Integer>>();
/**
* Record non-private methods and possible parameter counts (negative number means large or equals)
*/
public final Map<String, List<Integer>> nonPrivateMethods = new HashMap<String, List<Integer>>();
}

View File

@@ -13,8 +13,8 @@ public enum MemberType {
STATIC_PRIVATE,
/**
* None private member
* Non-private member
*/
NONE_PRIVATE
NON_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.generator.PrivateAccessStatementGenerator;
import com.alibaba.testable.processor.model.MemberRecord;
import com.alibaba.testable.processor.model.MemberType;
import com.alibaba.testable.processor.model.TestableContext;
import com.alibaba.testable.processor.util.PathUtil;
@@ -17,6 +18,9 @@ import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Travel AST
@@ -39,13 +43,9 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
*/
private final ListBuffer<Name> sourceClassIns = new ListBuffer<Name>();
/**
* Record private and final fields
* Member information of source class
*/
private final ListBuffer<String> privateOrFinalFields = new ListBuffer<String>();
/**
* Record private methods
*/
private final ListBuffer<String> privateMethods = new ListBuffer<String>();
private final MemberRecord memberRecord = new MemberRecord();
private final PrivateAccessStatementGenerator privateAccessStatementGenerator;
private final PrivateAccessChecker privateAccessChecker;
@@ -67,8 +67,7 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
} catch (Exception e) {
e.printStackTrace();
}
this.privateAccessChecker = new PrivateAccessChecker(sourceClassName.toString(),
privateOrFinalFields.toList(), privateMethods.toList());
this.privateAccessChecker = new PrivateAccessChecker(cx, sourceClassName.toString(), memberRecord);
}
/**
@@ -202,38 +201,62 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
if (Modifier.isFinal(f.getModifiers()) || Modifier.isPrivate(f.getModifiers())) {
privateOrFinalFields.add(f.getName());
memberRecord.privateOrFinalFields.add(f.getName());
} else {
memberRecord.nonPrivateNorFinalFields.add(f.getName());
}
}
Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
for (final Method m : methods) {
if (Modifier.isPrivate(m.getModifiers())) {
privateMethods.add(m.getName());
checkAndAdd(memberRecord.privateMethods, m.getName(), getParameterLength(m));
} else {
checkAndAdd(memberRecord.nonPrivateMethods, m.getName(), getParameterLength(m));
}
}
}
private void checkAndAdd(Map<String, List<Integer>> map, String key, final int value) {
if (map.containsKey(key)) {
map.get(key).add(value);
} else {
map.put(key, new ArrayList<Integer>() {{ add(value); }});
}
}
private int getParameterLength(Method m) {
int length = m.getParameterTypes().length;
if (length == 0) {
return 0;
}
if (m.getParameterTypes()[length - 1].getName().startsWith("[")) {
return -(length - 1);
} else {
return length;
}
}
private MemberType checkGetterType(JCFieldAccess access) {
if (access.selected instanceof JCIdent && privateOrFinalFields.contains(access.name.toString())) {
if (access.selected instanceof JCIdent && memberRecord.privateOrFinalFields.contains(access.name.toString())) {
return checkSourceClassOrIns(((JCIdent)access.selected).name);
}
return MemberType.NONE_PRIVATE;
return MemberType.NON_PRIVATE;
}
private MemberType checkSetterType(JCAssign assign) {
if (assign.lhs instanceof JCFieldAccess && ((JCFieldAccess)(assign).lhs).selected instanceof JCIdent &&
privateOrFinalFields.contains(((JCFieldAccess)(assign).lhs).name.toString())) {
memberRecord.privateOrFinalFields.contains(((JCFieldAccess)(assign).lhs).name.toString())) {
return checkSourceClassOrIns(((JCIdent)((JCFieldAccess)(assign).lhs).selected).name);
}
return MemberType.NONE_PRIVATE;
return MemberType.NON_PRIVATE;
}
private MemberType checkInvokeType(JCMethodInvocation expr) {
if (expr.meth instanceof JCFieldAccess && ((JCFieldAccess)(expr).meth).selected instanceof JCIdent &&
privateMethods.contains(((JCFieldAccess)(expr).meth).name.toString())) {
memberRecord.privateMethods.containsKey(((JCFieldAccess)(expr).meth).name.toString())) {
return checkSourceClassOrIns(((JCIdent)((JCFieldAccess)(expr).meth).selected).name);
}
return MemberType.NONE_PRIVATE;
return MemberType.NON_PRIVATE;
}
private MemberType checkSourceClassOrIns(Name name) {
@@ -242,7 +265,7 @@ public class EnablePrivateAccessTranslator extends BaseTranslator {
} else if (sourceClassIns.contains(name)) {
return MemberType.PRIVATE_OR_FINAL;
}
return MemberType.NONE_PRIVATE;
return MemberType.NON_PRIVATE;
}
}

View File

@@ -1,10 +1,13 @@
package com.alibaba.testable.processor.translator;
import com.alibaba.testable.processor.exception.MemberNotExistException;
import com.alibaba.testable.processor.model.MemberRecord;
import com.alibaba.testable.processor.model.TestableContext;
import com.sun.tools.javac.tree.JCTree;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Validate parameter of PrivateAccessor methods to prevent broken by refactor
@@ -21,14 +24,14 @@ public class PrivateAccessChecker {
private static final String TYPE_FIELD = "Field";
private static final String TYPE_METHOD = "Method";
private final TestableContext cx;
private final String className;
private final List<String> privateOrFinalFields;
private final List<String> privateMethods;
private final MemberRecord sourceMembers;
public PrivateAccessChecker(String className, List<String> privateOrFinalFields, List<String> privateMethods) {
public PrivateAccessChecker(TestableContext cx, String className, MemberRecord memberRecord) {
this.cx = cx;
this.className = className;
this.privateOrFinalFields = privateOrFinalFields;
this.privateMethods = privateMethods;
this.sourceMembers = memberRecord;
}
public void validate(JCTree.JCMethodInvocation invocation) {
@@ -39,14 +42,41 @@ public class PrivateAccessChecker {
Object target = ((JCTree.JCLiteral)invocation.args.get(1)).getValue();
if (target instanceof String) {
String methodName = fieldAccess.name.toString();
if (FIELD_ACCESS_METHOD.contains(methodName) && !privateOrFinalFields.contains(target)) {
throw new MemberNotExistException(TYPE_FIELD, className, (String)target);
} else if (FIELD_INVOKE_METHOD.contains(methodName) && !privateMethods.contains(target)) {
throw new MemberNotExistException(TYPE_METHOD, className, (String)target);
if (FIELD_ACCESS_METHOD.contains(methodName)) {
if (sourceMembers.nonPrivateNorFinalFields.contains(target)) {
cx.logger.warn("Field " + className + "::" + target + " is neither private nor final.");
} else if (!sourceMembers.privateOrFinalFields.contains(target)) {
throw new MemberNotExistException(TYPE_FIELD, className, (String)target);
}
} else if (FIELD_INVOKE_METHOD.contains(methodName)) {
int parameterCount = invocation.args.length() - 2;
// Because of override, check private method list first
if (sourceMembers.privateMethods.containsKey(target) &&
checkParameterCount(sourceMembers.privateMethods, (String)target, parameterCount)) {
// Let it go
} else if (sourceMembers.nonPrivateMethods.containsKey(target) &&
checkParameterCount(sourceMembers.privateMethods, (String)target, parameterCount)) {
cx.logger.warn("Method " + className + "::" + target + " is not private.");
} else {
throw new MemberNotExistException(TYPE_METHOD, className, (String)target);
}
}
}
}
}
}
private boolean checkParameterCount(Map<String, List<Integer>> methods, String target, int parameterCount) {
for (Integer expectCount : methods.get(target)) {
if (countMatch(parameterCount, expectCount)) {
return true;
}
}
return false;
}
private boolean countMatch(int parameterCount, Integer expectCount) {
return expectCount == parameterCount || (expectCount < 0 && parameterCount >= -expectCount);
}
}

View File

@@ -17,11 +17,12 @@ public class TestableLogger {
}
public void info(String msg) {
// Message level lower than warning is not shown by default, use stdout instead
System.out.println("[INFO] " + msg);
}
public void warn(String msg) {
messager.printMessage(Diagnostic.Kind.MANDATORY_WARNING, msg);
messager.printMessage(Diagnostic.Kind.WARNING, msg);
}
public void error(String msg) {