generate member index of specified class

This commit is contained in:
金戟 2021-03-15 23:51:21 +08:00
parent dce1accafb
commit bdd16c0948
11 changed files with 256 additions and 10 deletions

View File

@ -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";

View File

@ -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;

View File

@ -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);
}
/**

View File

@ -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 = "_";
}

View File

@ -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<Class<?>, List<String>> MEMBER_INDEXES = UnnullableMap.of(new ArrayList<String>());
private static final String THIS_REF_PREFIX = "this$";
/**
* 获取第一个符合搜索路径的成员
* @param target 目标对象
* @param queryPath 搜索路径
* @param clazz 目标成员类型
* @return 返回目标成员若不存在则返回null
*/
public static <T> T getFirst(Object target, String queryPath, Class<T> clazz) {
List<T> values = get(target, queryPath, clazz);
return values.isEmpty() ? null : values.get(0);
}
/**
* 获取所有符合搜索路径的成员
* @param target 目标对象
* @param queryPath 搜索路径
* @param clazz 目标成员类型
* @return 返回所有匹配的成员
*/
public static <T> List<T> get(Object target, String queryPath, Class<T> clazz) {
List<T> values = new ArrayList<T>();
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<String> generateMemberIndex(Class<?> clazz) {
return generateMemberIndex("", clazz);
}
private static List<String> generateMemberIndex(String basePath, Class<?> clazz) {
List<Field> fields = getAllFields(clazz);
List<String> paths = new ArrayList<String>();
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<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<Field>(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) {
}
}

View File

@ -0,0 +1,20 @@
package com.alibaba.testable.core.tool;
/**
* @author flin
*/
public class OmniConstructor {
public static <T> T newInstance(Class<T> clazz) {
return null;
}
public static <T> void appendInstance(Object target, Class<T> clazz) {
return;
}
public static <T> void appendInstance(Object target, int count, Class<T> clazz) {
return;
}
}

View File

@ -32,4 +32,14 @@ public class UnnullableMap<K, V> extends HashMap<K, V> {
}
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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,4 @@
package com.alibaba.testable.core.tool;
public class DemoGrandChild {
}

View File

@ -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;
}

View File

@ -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<String> 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.<String>invokeStatic(OmniAccessor.class, "toParent", ""));
assertEquals("", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toParent", "abc"));
assertEquals("abc", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toParent", "abc/xyz"));
assertEquals("abc/def", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toParent", "abc/def/xyz"));
assertEquals("/abc/def", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toParent", "/abc/def/xyz"));
}
@Test
void should_to_child() {
assertEquals("", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", ""));
assertEquals("abc", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "abc"));
assertEquals("xyz", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "abc/xyz"));
assertEquals("xyz", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "abc/def/xyz"));
assertEquals("xyz", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "/abc/def/xyz"));
}
@Test
void should_get_by_path() {
}
@Test
void should_set_by_path() {
}
}