more precise omni handler filter

This commit is contained in:
金戟 2021-04-06 14:22:17 +08:00
parent b41923bfa0
commit f2feb3cc68
3 changed files with 63 additions and 4 deletions

View File

@ -4,11 +4,15 @@ import com.alibaba.testable.agent.handler.test.JUnit4Framework;
import com.alibaba.testable.agent.handler.test.JUnit5Framework;
import com.alibaba.testable.agent.util.ClassUtil;
import com.alibaba.testable.agent.util.CollectionUtil;
import com.alibaba.testable.core.tool.PrivateAccessor;
import org.objectweb.asm.Label;
import org.objectweb.asm.tree.*;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.util.List;
import static com.alibaba.testable.agent.constant.ByteCodeConst.TYPE_LONG;
import static com.alibaba.testable.agent.util.ClassUtil.CLASS_OBJECT;
import static com.alibaba.testable.core.constant.ConstPool.CONSTRUCTOR;
import static com.alibaba.testable.core.constant.ConstPool.THIS_REF;
@ -24,16 +28,42 @@ public class OmniClassHandler extends BaseClassHandler {
private static final String METHOD_START = "(";
private static final String VOID_METHOD_END = ")V";
private static final String VOID_METHOD = "()V";
private static final String ENABLE_CONFIGURATION_PROPERTIES
= "Lorg/springframework/boot/context/properties/EnableConfigurationProperties;";
private static final String[] JUNIT_TEST_ANNOTATIONS = new String[] {
JUnit4Framework.ANNOTATION_TEST, JUnit5Framework.ANNOTATION_TEST, JUnit5Framework.ANNOTATION_PARAMETERIZED_TEST
};
private static final String SERIAL_VERSION_UID = "serialVersionUID";
private final Class<?> rawClass;
public OmniClassHandler(Class<?> clazz) {
this.rawClass = clazz;
}
@Override
protected void transform(ClassNode cn) {
if (isInterface(cn) || isJunitTestClass(cn) || isUninstantiableClass(cn)) {
if (isInterface(cn) || isJunitTestClass(cn) || isUninstantiableClass(cn) || hasSpecialAnnotation(cn)) {
return;
}
addSerialVersionUid(cn);
addConstructorWithNullTypeParameter(cn);
}
private void addSerialVersionUid(ClassNode cn) {
if (ClassUtil.hasImplement(rawClass, Serializable.class)) {
for (FieldNode fn : cn.fields) {
if (SERIAL_VERSION_UID.equals(fn.name)) {
return;
}
}
cn.fields.add(new FieldNode(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, SERIAL_VERSION_UID,
String.valueOf(TYPE_LONG), null,
PrivateAccessor.invokeStatic(ObjectStreamClass.class, "computeDefaultSUID", rawClass)));
}
}
private void addConstructorWithNullTypeParameter(ClassNode cn) {
MethodNode constructor = new MethodNode(ACC_PUBLIC, CONSTRUCTOR,
METHOD_START + ClassUtil.toByteCodeClassName(NULL_TYPE) + VOID_METHOD_END, null, null);
LabelNode start = new LabelNode(new Label());
@ -51,6 +81,18 @@ public class OmniClassHandler extends BaseClassHandler {
cn.methods.add(constructor);
}
private boolean hasSpecialAnnotation(ClassNode cn) {
if (cn.visibleAnnotations == null) {
return false;
}
for (AnnotationNode an : cn.visibleAnnotations) {
if (an.desc.equals(ENABLE_CONFIGURATION_PROPERTIES)) {
return true;
}
}
return false;
}
private boolean isUninstantiableClass(ClassNode cn) {
// if the class has no even default constructor, skip it
for (MethodNode mn : cn.methods) {

View File

@ -40,8 +40,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
/**
* Just avoid spend time to scan those surely non-user classes, should keep these lists as tiny as possible
*/
private final String[] BLACKLIST_PREFIXES = new String[] { "sun/", "com/sun/", "org/gradle/",
"org/springframework/boot/autoconfigure/" };
private final String[] BLACKLIST_PREFIXES = new String[] { "sun/", "com/sun/" };
public MockClassParser mockClassParser = new MockClassParser();
@ -53,7 +52,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
return null;
}
LogUtil.verbose("Handle class: " + className);
byte[] bytes = new OmniClassHandler().getBytes(classFileBuffer);
byte[] bytes = new OmniClassHandler(classBeingRedefined).getBytes(classFileBuffer);
ClassNode cn = ClassUtil.getClassNode(className);
if (cn != null) {
return transformMock(bytes, cn);

View File

@ -234,6 +234,24 @@ public class ClassUtil {
return cn;
}
/**
* Check whether a class has implement specified interface
* @param clazz class to check
* @param intf target interface
* @return has or not
*/
public static boolean hasImplement(Class<?> clazz, Class<?> intf) {
if (clazz == null) {
return false;
}
for (Class<?> i : clazz.getInterfaces()) {
if (i.equals(intf)) {
return true;
}
}
return hasImplement(clazz.getSuperclass(), intf);
}
private static String toDescriptor(Byte type, String objectType) {
return "(" + (char)type.byteValue() + ")L" + objectType + ";";
}