From eb8ccb0a40911bc43f63b5ca8184e05946307474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Wed, 30 Nov 2022 00:02:42 +0800 Subject: [PATCH] feat: choose correct constructor for system preloaded classes --- .../agent/handler/OmniClassHandler.java | 65 +++++++++++++++---- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/OmniClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/OmniClassHandler.java index 5e53973..b3fd14d 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/OmniClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/OmniClassHandler.java @@ -5,15 +5,18 @@ import com.alibaba.testable.agent.handler.test.JUnit5Framework; import com.alibaba.testable.agent.util.AnnotationUtil; import com.alibaba.testable.agent.util.ClassUtil; import com.alibaba.testable.core.util.CollectionUtil; +import com.alibaba.testable.core.util.StringUtil; import org.objectweb.asm.Label; +import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import java.util.List; +import java.util.Map; import static com.alibaba.testable.agent.constant.ConstPool.CLASS_OBJECT; import static com.alibaba.testable.core.constant.ConstPool.CONSTRUCTOR; import static com.alibaba.testable.core.constant.ConstPool.THIS_REF; -import static com.alibaba.testable.core.util.CollectionUtil.contains; +import static com.alibaba.testable.core.util.CollectionUtil.*; /** * @author flin @@ -26,9 +29,40 @@ public class OmniClassHandler extends BaseClassHandler { private static final String VOID_METHOD_END = ")V"; private static final String VOID_METHOD = "()V"; private static final String ENABLE_CONFIGURATION = "org.springframework.context.annotation.Configuration"; + private static final String CLASS_OMNI_CONSTRUCTOR = "com/alibaba/testable/core/tool/OmniConstructor"; + public static final String CLASS_CONSTRUCTION_OPTION = "com/alibaba/testable/core/model/ConstructionOption"; + public static final String METHOD_NEW_INSTANCE = "newInstance"; + public static final String METHOD_DESC_NEW_INSTANCE = "(Ljava/lang/Class;[Lcom/alibaba/testable/core/model/ConstructionOption;)Ljava/lang/Object;"; + private static final String CLASS_ABSTRACT_COLLECTION = "java/util/AbstractCollection"; private static final String CLASS_NUMBER = "java/lang/Number"; private static final String CLASS_HASH_SET = "java/util/HashSet"; + private static final String CLASS_READER = "java/io/Reader"; + private static final String CLASS_WRITER = "java/io/Writer"; + private static final String CLASS_BUFFERED_READER = "java/io/BufferedReader"; + private static final String CLASS_BUFFERED_WRITER = "java/io/BufferedWriter"; + private static final String CLASS_INPUT_STREAM = "java/io/InputStream"; + private static final String CLASS_OUTPUT_STREAM = "java/io/OutputStream"; + private static final String CLASS_BUFFERED_INPUT_STREAM = "java/io/BufferedInputStream"; + private static final String CLASS_BUFFERED_OUTPUT_STREAM = "java/io/BufferedOutputStream"; + private static final String CLASS_THREAD = "java/lang/Thread"; + private static final String CLASS_FILE = "java/io/File"; + + // below classes are loaded before OmniClassHandler, cannot be instrumented + // map of class name to constructor parameters + private static final Map PRELOADED_CLASSES = mapOf( + entryOf(CLASS_OBJECT, CollectionUtil.arrayOf()), + entryOf(CLASS_READER, CollectionUtil.arrayOf()), + entryOf(CLASS_WRITER, CollectionUtil.arrayOf()), + entryOf(CLASS_INPUT_STREAM, CollectionUtil.arrayOf()), + entryOf(CLASS_OUTPUT_STREAM, CollectionUtil.arrayOf()), + entryOf(CLASS_THREAD, CollectionUtil.arrayOf()), + entryOf(CLASS_FILE, arrayOf("Ljava/lang/String;")), + entryOf(CLASS_BUFFERED_READER, arrayOf("Ljava/io/Reader;")), + entryOf(CLASS_BUFFERED_WRITER, arrayOf("Ljava/io/Reader;")), + entryOf(CLASS_BUFFERED_INPUT_STREAM, arrayOf("Ljava/io/Reader;")), + entryOf(CLASS_BUFFERED_OUTPUT_STREAM, arrayOf("Ljava/io/Reader;")) + ); private static final String[] JUNIT_TEST_ANNOTATIONS = new String[] { JUnit4Framework.ANNOTATION_TEST, JUnit5Framework.ANNOTATION_TEST, JUnit5Framework.ANNOTATION_PARAMETERIZED_TEST @@ -51,12 +85,12 @@ public class OmniClassHandler extends BaseClassHandler { METHOD_START + ClassUtil.toByteCodeClassName(VOID_TYPE) + VOID_METHOD_END, null, null); LabelNode start = new LabelNode(new Label()); LabelNode end = new LabelNode(new Label()); - if (cn.superName.equals(CLASS_OBJECT)) { - constructor.instructions = invokeSuperWithoutParameter(start, end); + if (PRELOADED_CLASSES.containsKey(cn.superName)) { + constructor.instructions = invokeSuperWithoutParameter(cn.superName, start, end); constructor.localVariables = createLocalVariables(cn, start, end); - constructor.maxStack = 1; + constructor.maxStack = 1 + PRELOADED_CLASSES.get(cn.superName).length; } else { - constructor.instructions = invokeSuperWithTestableVoidParameter(cn, start, end); + constructor.instructions = invokeSuperWithTestableVoidParameter(cn.superName, start, end); constructor.localVariables = createLocalVariables(cn, start, end); constructor.maxStack = 3; } @@ -102,25 +136,34 @@ public class OmniClassHandler extends BaseClassHandler { return false; } - private InsnList invokeSuperWithoutParameter(LabelNode start, LabelNode end) { + private InsnList invokeSuperWithoutParameter(String superName, LabelNode start, LabelNode end) { + String[] parameters = PRELOADED_CLASSES.get(superName); InsnList il = new InsnList(); il.add(start); il.add(new VarInsnNode(ALOAD, 0)); - il.add(new MethodInsnNode(INVOKESPECIAL, CLASS_OBJECT, CONSTRUCTOR, VOID_METHOD, false)); + for (String p : parameters) { + il.add(new LdcInsnNode(Type.getType(p))); + il.add(new InsnNode(ICONST_0)); + il.add(new TypeInsnNode(ANEWARRAY, CLASS_CONSTRUCTION_OPTION)); + il.add(new MethodInsnNode(INVOKESTATIC, CLASS_OMNI_CONSTRUCTOR, METHOD_NEW_INSTANCE, METHOD_DESC_NEW_INSTANCE, false)); + il.add(new TypeInsnNode(CHECKCAST, ClassUtil.toSlashSeparateJavaStyleName(p))); + } + il.add(new MethodInsnNode(INVOKESPECIAL, superName, CONSTRUCTOR, + METHOD_START + StringUtil.join("", parameters) + VOID_METHOD_END, false)); il.add(new InsnNode(RETURN)); il.add(end); return il; } - private InsnList invokeSuperWithTestableVoidParameter(ClassNode cn, LabelNode start, LabelNode end) { + private InsnList invokeSuperWithTestableVoidParameter(String superName, LabelNode start, LabelNode end) { InsnList il = new InsnList(); il.add(start); il.add(new VarInsnNode(ALOAD, 0)); - if (contains(UNREACHABLE_CLASSES, cn.superName)) { - il.add(new MethodInsnNode(INVOKESPECIAL, cn.superName, CONSTRUCTOR, VOID_METHOD, false)); + if (contains(UNREACHABLE_CLASSES, superName)) { + il.add(new MethodInsnNode(INVOKESPECIAL, superName, CONSTRUCTOR, VOID_METHOD, false)); } else { il.add(new VarInsnNode(ALOAD, 1)); - il.add(new MethodInsnNode(INVOKESPECIAL, cn.superName, CONSTRUCTOR, + il.add(new MethodInsnNode(INVOKESPECIAL, superName, CONSTRUCTOR, METHOD_START + ClassUtil.toByteCodeClassName(VOID_TYPE) + VOID_METHOD_END, false)); } il.add(new InsnNode(RETURN));