From 0a1b46b3528c010c115a5201ea1f0374de088f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Tue, 5 Jan 2021 23:28:35 +0800 Subject: [PATCH] fit for kotlin auto generated accessor method --- .../alibaba/testable/demo/DemoMockTest.java | 3 ++- .../com/alibaba/testable/demo/DemoMock.kt | 4 ++++ .../com/alibaba/testable/demo/DemoMockTest.kt | 9 ++++++- .../testable/agent/constant/ConstPool.java | 3 +++ .../agent/handler/SourceClassHandler.java | 5 +++- .../testable/agent/util/ClassUtil.java | 24 +++++++++++++++++-- 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoMockTest.java b/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoMockTest.java index 6d50c5e..e7acc6b 100644 --- a/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoMockTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoMockTest.java @@ -31,7 +31,7 @@ class DemoMockTest { } @MockMethod - private String staticFunc(DemoMock self) { + private String staticFunc(DemoMock ignore) { return "_MOCK_TAIL"; } @@ -77,6 +77,7 @@ class DemoMockTest { void should_able_to_mock_member_method() throws Exception { assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello")); verify("innerFunc").with("hello"); + verify("staticFunc").with(); } @Test diff --git a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoMock.kt b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoMock.kt index 7e896a6..c4c333c 100644 --- a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoMock.kt +++ b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoMock.kt @@ -61,5 +61,9 @@ class DemoMock { private fun staticFunc(): String { return "_STATIC_TAIL" } + +// fun callStaticFunc(): String { +// return "CALL${staticFunc()}" +// } } } diff --git a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoMockTest.kt b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoMockTest.kt index ba2be22..9ffe28b 100644 --- a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoMockTest.kt +++ b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoMockTest.kt @@ -26,7 +26,7 @@ internal class DemoMockTest { private fun innerFunc(self: DemoMock, text: String) = "mock_$text" @MockMethod - private fun staticFunc(self: DemoMock): String { + private fun staticFunc(ignore: DemoMock): String { return "_MOCK_TAIL"; } @@ -72,8 +72,15 @@ internal class DemoMockTest { fun should_able_to_mock_member_method() { assertEquals("{ \"res\": \"mock_hello_MOCK_TAIL\"}", demoMock.outerFunc("hello")) verify("innerFunc").with("hello") + verify("staticFunc").with() } +// @Test +// fun should_able_to_mock_method_in_companion_object() { +// assertEquals("CALL_MOCK_TAIL", DemoMock.callStaticFunc()) +// verify("staticFunc").with() +// } + @Test fun should_able_to_mock_common_method() { assertEquals("trim_string__sub_string__false", demoMock.commonFunc()) diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/constant/ConstPool.java b/testable-agent/src/main/java/com/alibaba/testable/agent/constant/ConstPool.java index 23c1f7d..e23feb5 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/constant/ConstPool.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/constant/ConstPool.java @@ -20,6 +20,9 @@ public class ConstPool { public static final String CGLIB_CLASS_INFIX = "$$EnhancerBy"; + public static final String KOTLIN_POSTFIX_COMPANION = "$Companion"; + public static final String KOTLIN_PREFIX_ACCESS = "access$"; + /** * Name of the constructor method */ diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java index a47dcbd..a50d874 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java @@ -107,7 +107,10 @@ public class SourceClassHandler extends BaseClassHandler { private MethodInfo getMemberInjectMethodName(Set memberInjectMethods, MethodInsnNode node) { for (MethodInfo m : memberInjectMethods) { String nodeOwner = ClassUtil.fitCompanionClassName(node.owner); - if (m.getClazz().equals(nodeOwner) && m.getName().equals(node.name) && m.getDesc().equals(node.desc)) { + String nodeName = ClassUtil.fitKotlinAccessorName(node.name); + // Kotlin accessor method will append a extra type parameter + String nodeDesc = nodeName.equals(node.name) ? node.desc : ClassUtil.removeFirstParameter(node.desc); + if (m.getClazz().equals(nodeOwner) && m.getName().equals(nodeName) && m.getDesc().equals(nodeDesc)) { return m; } } diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/util/ClassUtil.java b/testable-agent/src/main/java/com/alibaba/testable/agent/util/ClassUtil.java index f7a9870..1eabad8 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/util/ClassUtil.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/util/ClassUtil.java @@ -61,7 +61,7 @@ public class ClassUtil { * @return is companion class or not */ public static boolean isCompanionClassName(String name) { - return name.endsWith("$Companion"); + return name.endsWith(ConstPool.KOTLIN_POSTFIX_COMPANION); } /** @@ -70,7 +70,18 @@ public class ClassUtil { * @return original name */ public static String fitCompanionClassName(String name) { - return name.replaceAll("\\$Companion$", ""); + return isCompanionClassName(name) ? + name.substring(0, name.length() - ConstPool.KOTLIN_POSTFIX_COMPANION.length()) : name; + } + + /** + * fit kotlin accessor method name to original name + * @param name a accessor name (which could be a common kotlin method) + * @return original name + */ + public static String fitKotlinAccessorName(String name) { + return name.startsWith(ConstPool.KOTLIN_PREFIX_ACCESS) ? + name.substring(ConstPool.KOTLIN_PREFIX_ACCESS.length()) : name; } /** @@ -198,6 +209,15 @@ public class ClassUtil { return toSlashSeparatedName(className).substring(1, className.length() - 1); } + /** + * remove first parameter from method descriptor + * @param desc original descriptor + * @return descriptor without first parameter + */ + public static String removeFirstParameter(String desc) { + return "(" + desc.substring(desc.indexOf(";") + 1); + } + private static String toDescriptor(Byte type, String objectType) { return "(" + (char)type.byteValue() + ")L" + objectType + ";"; }