mirror of
https://github.com/alibaba/testable-mock.git
synced 2026-08-19 09:43:29 +08:00
feat: find best constructor of super class
This commit is contained in:
@@ -3,9 +3,11 @@ package com.alibaba.testable.agent.handler;
|
|||||||
import com.alibaba.testable.agent.handler.test.JUnit4Framework;
|
import com.alibaba.testable.agent.handler.test.JUnit4Framework;
|
||||||
import com.alibaba.testable.agent.handler.test.JUnit5Framework;
|
import com.alibaba.testable.agent.handler.test.JUnit5Framework;
|
||||||
import com.alibaba.testable.agent.util.AnnotationUtil;
|
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||||
|
import com.alibaba.testable.agent.util.BytecodeUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
import com.alibaba.testable.core.util.CollectionUtil;
|
import com.alibaba.testable.core.util.CollectionUtil;
|
||||||
import com.alibaba.testable.core.util.StringUtil;
|
import com.alibaba.testable.core.util.StringUtil;
|
||||||
|
import com.alibaba.testable.core.util.TypeUtil;
|
||||||
import org.objectweb.asm.Label;
|
import org.objectweb.asm.Label;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
@@ -36,21 +38,7 @@ public class OmniClassHandler extends BaseClassHandler {
|
|||||||
// below classes are loaded before OmniClassHandler, cannot be instrumented
|
// below classes are loaded before OmniClassHandler, cannot be instrumented
|
||||||
// map of class name to constructor parameters
|
// map of class name to constructor parameters
|
||||||
private static final Map<String, String[]> PRELOADED_CLASSES = mapOf(
|
private static final Map<String, String[]> PRELOADED_CLASSES = mapOf(
|
||||||
entryOf(CLASS_OBJECT, CollectionUtil.<String>arrayOf()),
|
entryOf(CLASS_OBJECT, CollectionUtil.<String>arrayOf())
|
||||||
entryOf("java/io/Reader", CollectionUtil.<String>arrayOf()),
|
|
||||||
entryOf("java/io/Writer", CollectionUtil.<String>arrayOf()),
|
|
||||||
entryOf("java/io/InputStream", CollectionUtil.<String>arrayOf()),
|
|
||||||
entryOf("java/io/OutputStream", CollectionUtil.<String>arrayOf()),
|
|
||||||
entryOf("java/io/BufferedReader", arrayOf("Ljava/io/Reader;")),
|
|
||||||
entryOf("java/io/BufferedWriter", arrayOf("Ljava/io/Reader;")),
|
|
||||||
entryOf("java/io/BufferedInputStream", arrayOf("Ljava/io/Reader;")),
|
|
||||||
entryOf("java/io/BufferedOutputStream", arrayOf("Ljava/io/Reader;")),
|
|
||||||
entryOf("java/io/File", arrayOf("Ljava/lang/String;")),
|
|
||||||
entryOf("java/lang/Thread", CollectionUtil.<String>arrayOf()),
|
|
||||||
entryOf("java/lang/Number", CollectionUtil.<String>arrayOf()),
|
|
||||||
entryOf("java/nio/CharBuffer", arrayOf("I", "I", "I", "I")),
|
|
||||||
entryOf("java/util/AbstractCollection", CollectionUtil.<String>arrayOf()),
|
|
||||||
entryOf("java/util/HashSet", CollectionUtil.<String>arrayOf())
|
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final String[] JUNIT_TEST_ANNOTATIONS = new String[] {
|
private static final String[] JUNIT_TEST_ANNOTATIONS = new String[] {
|
||||||
@@ -71,19 +59,53 @@ public class OmniClassHandler extends BaseClassHandler {
|
|||||||
METHOD_START + ClassUtil.toByteCodeClassName(VOID_TYPE) + VOID_METHOD_END, null, null);
|
METHOD_START + ClassUtil.toByteCodeClassName(VOID_TYPE) + VOID_METHOD_END, null, null);
|
||||||
LabelNode start = new LabelNode(new Label());
|
LabelNode start = new LabelNode(new Label());
|
||||||
LabelNode end = new LabelNode(new Label());
|
LabelNode end = new LabelNode(new Label());
|
||||||
|
int extraParameterCount = 2;
|
||||||
|
|
||||||
if (PRELOADED_CLASSES.containsKey(cn.superName)) {
|
if (PRELOADED_CLASSES.containsKey(cn.superName)) {
|
||||||
constructor.instructions = invokeSuperWithoutTestableParameter(cn.superName, start, end);
|
constructor.instructions = invokeSuperWithoutTestableParameter(cn.superName, PRELOADED_CLASSES.get(cn.superName), start, end);
|
||||||
constructor.localVariables = createLocalVariables(cn, start, end);
|
extraParameterCount = PRELOADED_CLASSES.get(cn.superName).length;
|
||||||
constructor.maxStack = 1 + PRELOADED_CLASSES.get(cn.superName).length;
|
} else if (cn.superName.startsWith("java/")) {
|
||||||
|
try {
|
||||||
|
Class<?> superClazz = Class.forName(ClassUtil.toDotSeparatedName(cn.superName));
|
||||||
|
Class<?>[] constructorParameterTypes = TypeUtil.getBestConstructor(superClazz).getParameterTypes();
|
||||||
|
if (constructorParameterTypes.length == 0) {
|
||||||
|
constructor.instructions = invokeSuperWithoutTestableParameter(cn.superName, new String[0], start, end);
|
||||||
|
extraParameterCount = 0;
|
||||||
|
} else if (constructorParameterTypes.length == 1 &&
|
||||||
|
constructorParameterTypes[0].equals(Void.class)) {
|
||||||
|
constructor.instructions = invokeSuperWithTestableVoidParameter(cn.superName, start, end);
|
||||||
|
} else {
|
||||||
|
constructor.instructions = invokeSuperWithoutTestableParameter(cn.superName,
|
||||||
|
toByteCodeClassNames(constructorParameterTypes), start, end);
|
||||||
|
extraParameterCount = constructorParameterTypes.length;
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
constructor.instructions = invokeSuperWithTestableVoidParameter(cn.superName, start, end);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
constructor.instructions = invokeSuperWithTestableVoidParameter(cn.superName, start, end);
|
constructor.instructions = invokeSuperWithTestableVoidParameter(cn.superName, start, end);
|
||||||
constructor.localVariables = createLocalVariables(cn, start, end);
|
|
||||||
constructor.maxStack = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor.localVariables = createLocalVariables(cn, start, end);
|
||||||
|
constructor.maxStack = 1 + extraParameterCount;
|
||||||
constructor.maxLocals = 2;
|
constructor.maxLocals = 2;
|
||||||
cn.methods.add(constructor);
|
cn.methods.add(constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String[] toByteCodeClassNames(Class<?>[] classes) {
|
||||||
|
String[] names = new String[classes.length];
|
||||||
|
for (int i = 0; i < names.length; i++) {
|
||||||
|
if (classes[i].isPrimitive()) {
|
||||||
|
names[i] = BytecodeUtil.PRIMITIVE_TYPE_NAME_MAP.get(classes[i].getName());
|
||||||
|
} else if (classes[i].isArray()) {
|
||||||
|
names[i] = ClassUtil.toSlashSeparatedName(classes[i].getName());
|
||||||
|
} else {
|
||||||
|
names[i] = ClassUtil.toByteCodeClassName(classes[i].getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isUninstantiableClass(ClassNode cn) {
|
private boolean isUninstantiableClass(ClassNode cn) {
|
||||||
// if the class has no even default constructor, skip it
|
// if the class has no even default constructor, skip it
|
||||||
for (MethodNode mn : cn.methods) {
|
for (MethodNode mn : cn.methods) {
|
||||||
@@ -122,8 +144,7 @@ public class OmniClassHandler extends BaseClassHandler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InsnList invokeSuperWithoutTestableParameter(String superName, LabelNode start, LabelNode end) {
|
private InsnList invokeSuperWithoutTestableParameter(String superName, String[] parameters, LabelNode start, LabelNode end) {
|
||||||
String[] parameters = PRELOADED_CLASSES.get(superName);
|
|
||||||
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));
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.io.IOException;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.alibaba.testable.agent.constant.ByteCodeConst.*;
|
||||||
import static com.alibaba.testable.agent.constant.ConstPool.*;
|
import static com.alibaba.testable.agent.constant.ConstPool.*;
|
||||||
import static com.alibaba.testable.core.constant.ConstPool.*;
|
import static com.alibaba.testable.core.constant.ConstPool.*;
|
||||||
import static com.alibaba.testable.core.util.PathUtil.createFolder;
|
import static com.alibaba.testable.core.util.PathUtil.createFolder;
|
||||||
@@ -186,6 +187,17 @@ public class BytecodeUtil {
|
|||||||
put(IFNONNULL, -1);
|
put(IFNONNULL, -1);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
public static Map<String, String> PRIMITIVE_TYPE_NAME_MAP = new HashMap<String, String>() {{
|
||||||
|
put("byte", String.valueOf((char)TYPE_BYTE));
|
||||||
|
put("char", String.valueOf((char)TYPE_CHAR));
|
||||||
|
put("double", String.valueOf((char)TYPE_DOUBLE));
|
||||||
|
put("float", String.valueOf((char)TYPE_FLOAT));
|
||||||
|
put("int", String.valueOf((char)TYPE_INT));
|
||||||
|
put("long", String.valueOf((char)TYPE_LONG));
|
||||||
|
put("short", String.valueOf((char)TYPE_SHORT));
|
||||||
|
put("boolean", String.valueOf((char)TYPE_BOOL));
|
||||||
|
}};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get stack impact of a specified ops code
|
* Get stack impact of a specified ops code
|
||||||
* @param bytecode ops code to check
|
* @param bytecode ops code to check
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ public class OmniConstructor {
|
|||||||
|
|
||||||
private static Object createInstance(Class<?> clazz, Set<Class<?>> classPool, ConstructionOption[] options)
|
private static Object createInstance(Class<?> clazz, Set<Class<?>> classPool, ConstructionOption[] options)
|
||||||
throws InstantiationException, IllegalAccessException, InvocationTargetException {
|
throws InstantiationException, IllegalAccessException, InvocationTargetException {
|
||||||
Constructor<?> constructor = getBestConstructor(clazz);
|
Constructor<?> constructor = TypeUtil.getBestConstructor(clazz);
|
||||||
if (constructor == null) {
|
if (constructor == null) {
|
||||||
throw new ClassConstructionException("Fail to invoke constructor of " + clazz.getName());
|
throw new ClassConstructionException("Fail to invoke constructor of " + clazz.getName());
|
||||||
}
|
}
|
||||||
@@ -284,23 +284,4 @@ public class OmniConstructor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Constructor<?> getBestConstructor(Class<?> clazz) {
|
|
||||||
Constructor<?> bestConstructor = null;
|
|
||||||
int minimalExceptionCount = 999;
|
|
||||||
int minimalParameterCount = 999;
|
|
||||||
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
|
|
||||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
|
||||||
Class<?>[] exceptionTypes = constructor.getExceptionTypes();
|
|
||||||
if (parameterTypes.length == 1 && parameterTypes[0].equals(Void.class)) {
|
|
||||||
return constructor;
|
|
||||||
} else if (exceptionTypes.length < minimalExceptionCount
|
|
||||||
|| (exceptionTypes.length == minimalExceptionCount && parameterTypes.length < minimalParameterCount)) {
|
|
||||||
minimalExceptionCount = exceptionTypes.length;
|
|
||||||
minimalParameterCount = parameterTypes.length;
|
|
||||||
bestConstructor = constructor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bestConstructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,30 @@ public class TypeUtil {
|
|||||||
|| clazz.equals(Class.class) || clazz.equals(String.class);
|
|| clazz.equals(Class.class) || clazz.equals(String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find the simplest constructor for specified class
|
||||||
|
* @param clazz any class
|
||||||
|
* @return best constructor
|
||||||
|
*/
|
||||||
|
public static Constructor<?> getBestConstructor(Class<?> clazz) {
|
||||||
|
Constructor<?> bestConstructor = null;
|
||||||
|
int minimalExceptionCount = 999;
|
||||||
|
int minimalParameterCount = 999;
|
||||||
|
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
|
||||||
|
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||||
|
Class<?>[] exceptionTypes = constructor.getExceptionTypes();
|
||||||
|
if (parameterTypes.length == 1 && parameterTypes[0].equals(Void.class)) {
|
||||||
|
return constructor;
|
||||||
|
} else if (exceptionTypes.length < minimalExceptionCount
|
||||||
|
|| (exceptionTypes.length == minimalExceptionCount && parameterTypes.length < minimalParameterCount)) {
|
||||||
|
minimalExceptionCount = exceptionTypes.length;
|
||||||
|
minimalParameterCount = parameterTypes.length;
|
||||||
|
bestConstructor = constructor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestConstructor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* type equals
|
* type equals
|
||||||
* @param classesLeft class to be compared
|
* @param classesLeft class to be compared
|
||||||
|
|||||||
Reference in New Issue
Block a user