fix an omni constructor issue caused by array-in-array type

This commit is contained in:
金戟 2021-04-25 18:24:13 +08:00
parent 338c7828c8
commit db771b9c66
4 changed files with 34 additions and 12 deletions

View File

@ -7,7 +7,9 @@ import com.alibaba.testable.agent.util.CollectionUtil;
import org.objectweb.asm.Label; import org.objectweb.asm.Label;
import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.*;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import static com.alibaba.testable.agent.util.ClassUtil.CLASS_OBJECT; 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.CONSTRUCTOR;
@ -25,10 +27,14 @@ public class OmniClassHandler extends BaseClassHandler {
private static final String VOID_METHOD = "()V"; private static final String VOID_METHOD = "()V";
private static final String ENABLE_CONFIGURATION = "Lorg/springframework/context/annotation/Configuration;"; private static final String ENABLE_CONFIGURATION = "Lorg/springframework/context/annotation/Configuration;";
private static final String CLASS_ABSTRACT_COLLECTION = "java/util/AbstractCollection"; private static final String CLASS_ABSTRACT_COLLECTION = "java/util/AbstractCollection";
private static final String CLASS_NUMBER = "java/lang/Number";
private static final String[] JUNIT_TEST_ANNOTATIONS = new String[] { private static final String[] JUNIT_TEST_ANNOTATIONS = new String[] {
JUnit4Framework.ANNOTATION_TEST, JUnit5Framework.ANNOTATION_TEST, JUnit5Framework.ANNOTATION_PARAMETERIZED_TEST JUnit4Framework.ANNOTATION_TEST, JUnit5Framework.ANNOTATION_TEST, JUnit5Framework.ANNOTATION_PARAMETERIZED_TEST
}; };
private static final Set<String> UNREACHABLE_CLASSES = new HashSet<String>() {{
add(CLASS_ABSTRACT_COLLECTION); add(CLASS_NUMBER);
}};
@Override @Override
protected void transform(ClassNode cn) { protected void transform(ClassNode cn) {
@ -114,7 +120,7 @@ public class OmniClassHandler extends BaseClassHandler {
InsnList il = new InsnList(); InsnList il = new InsnList();
il.add(start); il.add(start);
il.add(new VarInsnNode(ALOAD, 0)); il.add(new VarInsnNode(ALOAD, 0));
if (cn.superName.equals(CLASS_ABSTRACT_COLLECTION)) { if (UNREACHABLE_CLASSES.contains(cn.superName)) {
il.add(new MethodInsnNode(INVOKESPECIAL, cn.superName, CONSTRUCTOR, VOID_METHOD, false)); il.add(new MethodInsnNode(INVOKESPECIAL, cn.superName, CONSTRUCTOR, VOID_METHOD, false));
} else { } else {
il.add(new VarInsnNode(ALOAD, 1)); il.add(new VarInsnNode(ALOAD, 1));

View File

@ -39,7 +39,8 @@ public class TestableClassTransformer implements ClassFileTransformer {
/** /**
* Just avoid spend time to scan those surely non-user classes, should keep these lists as tiny as possible * 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/", "javax/crypto", "org/gradle/" }; private final String[] BLACKLIST_PREFIXES = new String[] { "sun/", "com/sun/", "javax/crypto", "java/util/logging",
"org/gradle/" };
public MockClassParser mockClassParser = new MockClassParser(); public MockClassParser mockClassParser = new MockClassParser();

View File

@ -15,6 +15,7 @@ import static com.alibaba.testable.core.constant.ConstPool.DOLLAR;
public class OmniConstructor { public class OmniConstructor {
private static final int INITIAL_CAPACITY = 6; private static final int INITIAL_CAPACITY = 6;
private static final int FIRST = 1;
private OmniConstructor() {} private OmniConstructor() {}
@ -155,7 +156,7 @@ public class OmniConstructor {
return instance; return instance;
} }
private static <T> void handleCircleReference(T instance, Class<?> type, Map<Class<?>, Object> classPool) private static void handleCircleReference(Object instance, Class<?> type, Map<Class<?>, Object> classPool)
throws IllegalAccessException { throws IllegalAccessException {
if (instance == null) { if (instance == null) {
// don't travel null object // don't travel null object
@ -173,13 +174,11 @@ public class OmniConstructor {
Class<?> fieldType = f.getType(); Class<?> fieldType = f.getType();
if (fieldType.isArray()) { if (fieldType.isArray()) {
Class<?> componentType = fieldType.getComponentType(); Class<?> componentType = fieldType.getComponentType();
if (fieldIns != null && !componentType.isPrimitive() && !TypeUtil.isBasicType(componentType)) { if (fieldIns != null && !TypeUtil.isBasicType(componentType)) {
LogUtil.verbose(classPool.size(), "Field(Array[%d]) %s", Array.getLength(fieldIns), f.getName()); LogUtil.verbose(classPool.size(), "Field(Array[%d]) %s", Array.getLength(fieldIns), f.getName());
for (int i = 0; i < Math.min(Array.getLength(fieldIns), 10); i++) { handleCircleReferenceOfArrayField(fieldIns, componentType, classPool);
handleCircleReference(Array.get(fieldIns, i), componentType, classPool);
}
} }
} else if (!fieldType.isPrimitive() && !TypeUtil.isBasicType(fieldType)) { } else if (!TypeUtil.isBasicType(fieldType)) {
if (fieldIns == null && classPool.containsKey(fieldType)) { if (fieldIns == null && classPool.containsKey(fieldType)) {
f.set(instance, classPool.get(fieldType)); f.set(instance, classPool.get(fieldType));
} else if (!classPool.containsKey(fieldType)) { } else if (!classPool.containsKey(fieldType)) {
@ -191,6 +190,22 @@ public class OmniConstructor {
classPool.remove(type); classPool.remove(type);
} }
private static void handleCircleReferenceOfArrayField(Object instance, Class<?> type, Map<Class<?>, Object> classPool)
throws IllegalAccessException {
if (type.isArray()) {
for (int i = 0; i < Math.min(Array.getLength(instance), FIRST); i++) {
Object arrayIns = Array.get(instance, i);
if (arrayIns != null) {
handleCircleReferenceOfArrayField(arrayIns, arrayIns.getClass().getComponentType(), classPool);
}
}
} else if (!classPool.containsKey(type)) {
for (int i = 0; i < Math.min(Array.getLength(instance), FIRST); i++) {
handleCircleReference(Array.get(instance, i), type, classPool);
}
}
}
private static Object createInstance(Class<?> clazz, Set<Class<?>> classPool) private static Object createInstance(Class<?> clazz, Set<Class<?>> classPool)
throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Constructor<?> constructor = getBestConstructor(clazz); Constructor<?> constructor = getBestConstructor(clazz);

View File

@ -105,10 +105,10 @@ public class TypeUtil {
* @param clazz type to check * @param clazz type to check
*/ */
public static boolean isBasicType(Class<?> clazz) { public static boolean isBasicType(Class<?> clazz) {
return clazz.isEnum() || clazz.equals(Integer.class) || clazz.equals(Short.class) || clazz.equals(Long.class) return clazz.isPrimitive() || clazz.isEnum() || clazz.equals(Integer.class) || clazz.equals(Short.class)
|| clazz.equals(Byte.class) || clazz.equals(Character.class) || clazz.equals(Float.class) || clazz.equals(Long.class) || clazz.equals(Byte.class) || clazz.equals(Character.class)
|| clazz.equals(Double.class) || clazz.equals(Boolean.class) || clazz.equals(Class.class) || clazz.equals(Float.class) || clazz.equals(Double.class) || clazz.equals(Boolean.class)
|| clazz.equals(String.class) || clazz.equals(Date.class); || clazz.equals(Class.class) || clazz.equals(String.class);
} }
/** /**