From bdd16c0948df01a4eb1ca24ea3895db208d1bca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Mon, 15 Mar 2021 23:51:21 +0800 Subject: [PATCH] generate member index of specified class --- .../testable/agent/constant/ConstPool.java | 5 - .../transformer/TestableClassTransformer.java | 3 +- .../testable/agent/util/ClassUtil.java | 7 +- .../testable/core/constant/ConstPool.java | 8 ++ .../testable/core/tool/OmniAccessor.java | 118 ++++++++++++++++++ .../testable/core/tool/OmniConstructor.java | 20 +++ .../testable/core/util/UnnullableMap.java | 10 ++ .../alibaba/testable/core/tool/DemoChild.java | 17 +++ .../testable/core/tool/DemoGrandChild.java | 4 + .../testable/core/tool/DemoParent.java | 13 ++ .../testable/core/tool/OmniAccessorTest.java | 61 +++++++++ 11 files changed, 256 insertions(+), 10 deletions(-) create mode 100644 testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java create mode 100644 testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java create mode 100644 testable-core/src/test/java/com/alibaba/testable/core/tool/DemoChild.java create mode 100644 testable-core/src/test/java/com/alibaba/testable/core/tool/DemoGrandChild.java create mode 100644 testable-core/src/test/java/com/alibaba/testable/core/tool/DemoParent.java create mode 100644 testable-core/src/test/java/com/alibaba/testable/core/tool/OmniAccessorTest.java 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 b2b0030..0da5430 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 @@ -5,11 +5,6 @@ package com.alibaba.testable.agent.constant; */ public class ConstPool { - public static final String DOT = "."; - public static final String SLASH = "/"; - public static final String DOLLAR = "$"; - public static final String UNDERLINE = "_"; - public static final String FIELD_TARGET_METHOD = "targetMethod"; public static final String FIELD_TARGET_CLASS = "targetClass"; public static final String FIELD_SCOPE = "scope"; diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java index 8ce2d39..3379e32 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java @@ -22,7 +22,8 @@ import java.lang.instrument.ClassFileTransformer; import java.security.ProtectionDomain; import java.util.List; -import static com.alibaba.testable.agent.constant.ConstPool.*; +import static com.alibaba.testable.agent.constant.ConstPool.KOTLIN_POSTFIX_COMPANION; +import static com.alibaba.testable.core.constant.ConstPool.*; import static com.alibaba.testable.agent.util.ClassUtil.toJavaStyleClassName; import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 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 2e42e54..80904e6 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 @@ -11,8 +11,7 @@ import java.util.HashMap; import java.util.Map; import static com.alibaba.testable.agent.constant.ByteCodeConst.*; -import static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX; -import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX; +import static com.alibaba.testable.core.constant.ConstPool.*; import static org.objectweb.asm.Opcodes.*; /** @@ -180,7 +179,7 @@ public class ClassUtil { * @return converted name */ public static String toDotSeparatedName(String name) { - return name.replace(ConstPool.SLASH, ConstPool.DOT); + return name.replace(SLASH, DOT); } /** @@ -189,7 +188,7 @@ public class ClassUtil { * @return converted name */ public static String toSlashSeparatedName(String name) { - return name.replace(ConstPool.DOT, ConstPool.SLASH); + return name.replace(DOT, SLASH); } /** diff --git a/testable-core/src/main/java/com/alibaba/testable/core/constant/ConstPool.java b/testable-core/src/main/java/com/alibaba/testable/core/constant/ConstPool.java index 6c46d03..418f81f 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/constant/ConstPool.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/constant/ConstPool.java @@ -13,4 +13,12 @@ public class ConstPool { public static final String TEST_POSTFIX = "Test"; public static final String MOCK_POSTFIX = "Mock"; + /** + * Common symbols + */ + public static final String DOT = "."; + public static final String SLASH = "/"; + public static final String DOLLAR = "$"; + public static final String UNDERLINE = "_"; + } diff --git a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java new file mode 100644 index 0000000..f40e389 --- /dev/null +++ b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java @@ -0,0 +1,118 @@ +package com.alibaba.testable.core.tool; + +import com.alibaba.testable.core.util.UnnullableMap; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.alibaba.testable.core.constant.ConstPool.SLASH; + +/** + * @author flin + */ +public class OmniAccessor { + + private static final UnnullableMap, List> MEMBER_INDEXES = UnnullableMap.of(new ArrayList()); + private static final String THIS_REF_PREFIX = "this$"; + + /** + * 获取第一个符合搜索路径的成员 + * @param target 目标对象 + * @param queryPath 搜索路径 + * @param clazz 目标成员类型 + * @return 返回目标成员,若不存在则返回null + */ + public static T getFirst(Object target, String queryPath, Class clazz) { + List values = get(target, queryPath, clazz); + return values.isEmpty() ? null : values.get(0); + } + + /** + * 获取所有符合搜索路径的成员 + * @param target 目标对象 + * @param queryPath 搜索路径 + * @param clazz 目标成员类型 + * @return 返回所有匹配的成员 + */ + public static List get(Object target, String queryPath, Class clazz) { + List values = new ArrayList(); + for (String memberPath : MEMBER_INDEXES.getOrElse(target.getClass(), generateMemberIndex(target.getClass()))) { + if (memberPath.matches(toPattern(queryPath))) { + T val = (T)getByPath(target, memberPath); + if (val != null) { + values.add(val); + } + } + } + return values; + } + + /** + * 为符合搜索路径的成员赋值 + * @param target 目标对象 + * @param queryPath 搜索路径 + * @param value 新的值 + */ + public static void set(Object target, String queryPath, Object value) { + for (String memberPath : MEMBER_INDEXES.getOrElse(target.getClass(), generateMemberIndex(target.getClass()))) { + if (memberPath.matches(toPattern(queryPath))) { + Object parent = getByPath(target, toParent(memberPath)); + if (parent != null) { + setByPath(parent, toChild(memberPath), value); + } + } + } + } + + private static List generateMemberIndex(Class clazz) { + return generateMemberIndex("", clazz); + } + + private static List generateMemberIndex(String basePath, Class clazz) { + List fields = getAllFields(clazz); + List paths = new ArrayList(); + for (Field f : fields) { + if (!f.getName().startsWith(THIS_REF_PREFIX)) { + String fullPath = basePath + SLASH + toPath(f); + paths.add(fullPath); + paths.addAll(generateMemberIndex(fullPath, f.getType())); + } + } + return paths; + } + + private static List getAllFields(Class clazz) { + List fields = new ArrayList(Arrays.asList(clazz.getDeclaredFields())); + if (clazz.getSuperclass() != null) { + fields.addAll(getAllFields(clazz.getSuperclass())); + } + return fields; + } + + private static String toPath(Field field) { + return field.getName() + "{" + field.getType().getSimpleName() + "}"; + } + + private static String toPattern(String queryPath) { + return ""; + } + + private static String toChild(String memberPath) { + return memberPath.contains(SLASH) ? memberPath.substring(memberPath.lastIndexOf(SLASH) + 1) : memberPath; + } + + private static String toParent(String memberPath) { + return memberPath.contains(SLASH) ? memberPath.substring(0, memberPath.lastIndexOf(SLASH)) : ""; + } + + private static Object getByPath(Object target, String memberPath) { + return null; + } + + private static void setByPath(Object target, String memberPath, Object value) { + + } + +} diff --git a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java new file mode 100644 index 0000000..bb25736 --- /dev/null +++ b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java @@ -0,0 +1,20 @@ +package com.alibaba.testable.core.tool; + +/** + * @author flin + */ +public class OmniConstructor { + + public static T newInstance(Class clazz) { + return null; + } + + public static void appendInstance(Object target, Class clazz) { + return; + } + + public static void appendInstance(Object target, int count, Class clazz) { + return; + } + +} diff --git a/testable-core/src/main/java/com/alibaba/testable/core/util/UnnullableMap.java b/testable-core/src/main/java/com/alibaba/testable/core/util/UnnullableMap.java index 9b9bf47..ebbb695 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/util/UnnullableMap.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/util/UnnullableMap.java @@ -32,4 +32,14 @@ public class UnnullableMap extends HashMap { } return value; } + + public V getOrElse(Object key, V elseValue) { + V value = super.get(key); + if (value == null) { + value = elseValue; + super.put((K)key, value); + } + return value; + } + } diff --git a/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoChild.java b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoChild.java new file mode 100644 index 0000000..2fdaca2 --- /dev/null +++ b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoChild.java @@ -0,0 +1,17 @@ +package com.alibaba.testable.core.tool; + +public class DemoChild { + + public DemoGrandChild gc1; + + private DemoGrandChild gc2; + + public class SubChild { + private DemoGrandChild gc; + } + + public static class StaticSubChild { + private DemoGrandChild gc; + } + +} diff --git a/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoGrandChild.java b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoGrandChild.java new file mode 100644 index 0000000..d084d7d --- /dev/null +++ b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoGrandChild.java @@ -0,0 +1,4 @@ +package com.alibaba.testable.core.tool; + +public class DemoGrandChild { +} diff --git a/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoParent.java b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoParent.java new file mode 100644 index 0000000..2e3cec2 --- /dev/null +++ b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoParent.java @@ -0,0 +1,13 @@ +package com.alibaba.testable.core.tool; + +public class DemoParent { + + private DemoChild c1; + + public DemoChild c2; + + public DemoChild.SubChild sc; + + public DemoChild.StaticSubChild ssc; + +} diff --git a/testable-core/src/test/java/com/alibaba/testable/core/tool/OmniAccessorTest.java b/testable-core/src/test/java/com/alibaba/testable/core/tool/OmniAccessorTest.java new file mode 100644 index 0000000..f63b4f1 --- /dev/null +++ b/testable-core/src/test/java/com/alibaba/testable/core/tool/OmniAccessorTest.java @@ -0,0 +1,61 @@ +package com.alibaba.testable.core.tool; + +import com.alibaba.testable.core.accessor.PrivateAccessor; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class OmniAccessorTest { + + @Test + void should_generate_member_index() { + List index = PrivateAccessor.invokeStatic(OmniAccessor.class, "generateMemberIndex", DemoParent.class); + assertEquals(10, index.size()); + assertEquals("/c1{DemoChild}", index.get(0)); + assertEquals("/c1{DemoChild}/gc1{DemoGrandChild}", index.get(1)); + assertEquals("/c1{DemoChild}/gc2{DemoGrandChild}", index.get(2)); + assertEquals("/c2{DemoChild}", index.get(3)); + assertEquals("/c2{DemoChild}/gc1{DemoGrandChild}", index.get(4)); + assertEquals("/c2{DemoChild}/gc2{DemoGrandChild}", index.get(5)); + assertEquals("/sc{SubChild}", index.get(6)); + assertEquals("/sc{SubChild}/gc{DemoGrandChild}", index.get(7)); + assertEquals("/ssc{StaticSubChild}", index.get(8)); + assertEquals("/ssc{StaticSubChild}/gc{DemoGrandChild}", index.get(9)); + } + + @Test + void should_to_pattern() { + + } + + @Test + void should_to_parent() { + assertEquals("", PrivateAccessor.invokeStatic(OmniAccessor.class, "toParent", "")); + assertEquals("", PrivateAccessor.invokeStatic(OmniAccessor.class, "toParent", "abc")); + assertEquals("abc", PrivateAccessor.invokeStatic(OmniAccessor.class, "toParent", "abc/xyz")); + assertEquals("abc/def", PrivateAccessor.invokeStatic(OmniAccessor.class, "toParent", "abc/def/xyz")); + assertEquals("/abc/def", PrivateAccessor.invokeStatic(OmniAccessor.class, "toParent", "/abc/def/xyz")); + } + + @Test + void should_to_child() { + assertEquals("", PrivateAccessor.invokeStatic(OmniAccessor.class, "toChild", "")); + assertEquals("abc", PrivateAccessor.invokeStatic(OmniAccessor.class, "toChild", "abc")); + assertEquals("xyz", PrivateAccessor.invokeStatic(OmniAccessor.class, "toChild", "abc/xyz")); + assertEquals("xyz", PrivateAccessor.invokeStatic(OmniAccessor.class, "toChild", "abc/def/xyz")); + assertEquals("xyz", PrivateAccessor.invokeStatic(OmniAccessor.class, "toChild", "/abc/def/xyz")); + } + + @Test + void should_get_by_path() { + + } + + @Test + void should_set_by_path() { + + } + +}