add verbose log for omni constructor

This commit is contained in:
金戟 2021-04-23 20:34:15 +08:00
parent b89daa22c3
commit a67619d574
4 changed files with 51 additions and 23 deletions

View File

@ -45,12 +45,11 @@ public class TestableClassTransformer implements ClassFileTransformer {
@Override @Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classFileBuffer) { ProtectionDomain protectionDomain, byte[] classFileBuffer) {
// className is in slash-separated format
if (isSystemClass(className)) { if (isSystemClass(className)) {
// Ignore system class and reloaded class // Ignore system class and reloaded class
return null; return null;
} }
// className is in slash-separated format
LogUtil.verbose("Handle class: " + className);
byte[] bytes = shouldOmniEnhance(className) ? new OmniClassHandler().getBytes(classFileBuffer) : classFileBuffer; byte[] bytes = shouldOmniEnhance(className) ? new OmniClassHandler().getBytes(classFileBuffer) : classFileBuffer;
ClassNode cn = ClassUtil.getClassNode(className); ClassNode cn = ClassUtil.getClassNode(className);
if (cn != null) { if (cn != null) {

View File

@ -2,7 +2,7 @@ package com.alibaba.testable.core.tool;
import com.alibaba.testable.core.exception.ClassConstructionException; import com.alibaba.testable.core.exception.ClassConstructionException;
import com.alibaba.testable.core.model.TestableNull; import com.alibaba.testable.core.model.TestableNull;
import com.alibaba.testable.core.util.CollectionUtil; import com.alibaba.testable.core.util.LogUtil;
import com.alibaba.testable.core.util.TypeUtil; import com.alibaba.testable.core.util.TypeUtil;
import javax.lang.model.type.NullType; import javax.lang.model.type.NullType;
@ -17,6 +17,7 @@ import static com.alibaba.testable.core.constant.ConstPool.DOLLAR;
public class OmniConstructor { public class OmniConstructor {
private static final int INITIAL_CAPACITY = 6; private static final int INITIAL_CAPACITY = 6;
private static final int ZERO = 0;
private OmniConstructor() {} private OmniConstructor() {}
@ -27,7 +28,7 @@ public class OmniConstructor {
* @return 返回新创建的对象 * @return 返回新创建的对象
*/ */
public static <T> T newInstance(Class<T> clazz) { public static <T> T newInstance(Class<T> clazz) {
return handleCircleReference(newInstance(clazz, new HashSet<Class<?>>(INITIAL_CAPACITY))); return handleCircleReference(newInstance(clazz, new HashSet<Class<?>>(INITIAL_CAPACITY), ZERO));
} }
/** /**
@ -38,10 +39,11 @@ public class OmniConstructor {
* @return 返回新创建的对象数组 * @return 返回新创建的对象数组
*/ */
public static <T> T[] newArray(Class<T> clazz, int size) { public static <T> T[] newArray(Class<T> clazz, int size) {
return (T[])handleCircleReference(newArray(clazz, size, new HashSet<Class<?>>(INITIAL_CAPACITY))); return (T[])handleCircleReference(newArray(clazz, size, new HashSet<Class<?>>(INITIAL_CAPACITY), ZERO));
} }
private static <T> T newInstance(Class<T> clazz, Set<Class<?>> classPool) { private static <T> T newInstance(Class<T> clazz, Set<Class<?>> classPool, int level) {
LogUtil.verbose(level, "creating %s", clazz.getSimpleName());
if (classPool.contains(clazz)) { if (classPool.contains(clazz)) {
return null; return null;
} }
@ -50,7 +52,7 @@ public class OmniConstructor {
if (clazz.isPrimitive()) { if (clazz.isPrimitive()) {
return newPrimitive(clazz); return newPrimitive(clazz);
} else if (clazz.isArray()) { } else if (clazz.isArray()) {
return (T)newArray(clazz.getComponentType(), 0, classPool); return (T)newArray(clazz.getComponentType(), 0, classPool, level);
} else if (clazz.isEnum()) { } else if (clazz.isEnum()) {
return newEnum(clazz); return newEnum(clazz);
} else if (clazz.isInterface()) { } else if (clazz.isInterface()) {
@ -58,7 +60,7 @@ public class OmniConstructor {
} else if (Modifier.isAbstract(clazz.getModifiers())) { } else if (Modifier.isAbstract(clazz.getModifiers())) {
return newAbstractClass(clazz); return newAbstractClass(clazz);
} }
return newObject(clazz, classPool); return newObject(clazz, classPool, level);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
throw new ClassConstructionException("Failed to find constructor", e); throw new ClassConstructionException("Failed to find constructor", e);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
@ -74,26 +76,26 @@ public class OmniConstructor {
} }
} }
private static <T> T newObject(Class<T> clazz, Set<Class<?>> classPool) private static <T> T newObject(Class<T> clazz, Set<Class<?>> classPool, int level)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Object ins = createInstance(getBestConstructor(clazz), classPool); Object ins = createInstance(getBestConstructor(clazz), classPool, level);
if (!TypeUtil.isBasicType(clazz)) { if (!TypeUtil.isBasicType(clazz)) {
for (Field f : TypeUtil.getAllFields(clazz)) { for (Field f : TypeUtil.getAllFields(clazz)) {
f.setAccessible(true); f.setAccessible(true);
// skip "$jacocoData" field added by jacoco // skip "$jacocoData" field added by jacoco
if (f.get(ins) == null && !f.getName().startsWith(DOLLAR)) { if (f.get(ins) == null && !f.getName().startsWith(DOLLAR)) {
f.set(ins, newInstance(f.getType(), classPool)); f.set(ins, newInstance(f.getType(), classPool, level + 1));
} }
} }
} }
return (T)ins; return (T)ins;
} }
private static Object newArray(Class<?> clazz, int size, Set<Class<?>> classPool) { private static Object newArray(Class<?> clazz, int size, Set<Class<?>> classPool, int level) {
// primary[] cannot be cast to Object[], have to use Object instead of T[] // primary[] cannot be cast to Object[], have to use Object instead of T[]
Object array = Array.newInstance(clazz, size); Object array = Array.newInstance(clazz, size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
Array.set(array, i, newInstance(clazz, classPool)); Array.set(array, i, newInstance(clazz, classPool, level + 1));
} }
return array; return array;
} }
@ -144,10 +146,10 @@ public class OmniConstructor {
try { try {
if (instance.getClass().isArray()) { if (instance.getClass().isArray()) {
for (int i = 0; i < Array.getLength(instance); i++) { for (int i = 0; i < Array.getLength(instance); i++) {
handleCircleReference(Array.get(instance, i), new HashMap<Class<?>, Object>(INITIAL_CAPACITY)); handleCircleReference(Array.get(instance, i), new HashMap<Class<?>, Object>(INITIAL_CAPACITY), ZERO);
} }
} else { } else {
handleCircleReference(instance, new HashMap<Class<?>, Object>(INITIAL_CAPACITY)); handleCircleReference(instance, new HashMap<Class<?>, Object>(INITIAL_CAPACITY), ZERO);
} }
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new ClassConstructionException("Failed to access field", e); throw new ClassConstructionException("Failed to access field", e);
@ -155,11 +157,13 @@ public class OmniConstructor {
return instance; return instance;
} }
private static <T> void handleCircleReference(T instance, Map<Class<?>, Object> classPool) private static <T> void handleCircleReference(T instance, Map<Class<?>, Object> classPool, int level)
throws IllegalAccessException { throws IllegalAccessException {
if (instance == null) { if (instance == null) {
// don't travel null object
return; return;
} }
LogUtil.verbose(level, "verifying %s", instance.getClass().getSimpleName());
classPool.put(instance.getClass(), instance); classPool.put(instance.getClass(), instance);
for (Field f : TypeUtil.getAllFields(instance.getClass())) { for (Field f : TypeUtil.getAllFields(instance.getClass())) {
f.setAccessible(true); f.setAccessible(true);
@ -168,21 +172,21 @@ public class OmniConstructor {
if (fieldType.isArray()) { if (fieldType.isArray()) {
if (fieldIns != null) { if (fieldIns != null) {
for (int i = 0; i < Array.getLength(fieldIns); i++) { for (int i = 0; i < Array.getLength(fieldIns); i++) {
handleCircleReference(Array.get(fieldIns, i), classPool); handleCircleReference(Array.get(fieldIns, i), classPool, level + 1);
} }
} }
} else if (!fieldType.isPrimitive() && !TypeUtil.isBasicType(fieldType)) { } else if (!fieldType.isPrimitive() && !TypeUtil.isBasicType(fieldType)) {
if (fieldIns == null && classPool.containsKey(fieldType)) { if (fieldIns == null && classPool.containsKey(fieldType)) {
f.set(instance, classPool.get(fieldType)); f.set(instance, classPool.get(fieldType));
} else if (!classPool.containsKey(fieldType)) { } else if (!classPool.containsKey(fieldType)) {
handleCircleReference(fieldIns, classPool); handleCircleReference(fieldIns, classPool, level + 1);
} }
} }
} }
classPool.remove(instance.getClass()); classPool.remove(instance.getClass());
} }
private static Object createInstance(Constructor<?> constructor, Set<Class<?>> classPool) private static Object createInstance(Constructor<?> constructor, Set<Class<?>> classPool, int level)
throws InstantiationException, IllegalAccessException, InvocationTargetException { throws InstantiationException, IllegalAccessException, InvocationTargetException {
constructor.setAccessible(true); constructor.setAccessible(true);
Class<?>[] types = constructor.getParameterTypes(); Class<?>[] types = constructor.getParameterTypes();
@ -191,7 +195,7 @@ public class OmniConstructor {
} else { } else {
Object[] args = new Object[types.length]; Object[] args = new Object[types.length];
for (int i = 0; i < types.length; i++) { for (int i = 0; i < types.length; i++) {
args[i] = newInstance(types[i], classPool); args[i] = newInstance(types[i], classPool, level + 1);
} }
return constructor.newInstance(args); return constructor.newInstance(args);
} }

View File

@ -17,8 +17,16 @@ public class LogUtil {
private static FileOutputStream logFileStream = null; private static FileOutputStream logFileStream = null;
public static void verbose(String msg, Object... args) { public static void verbose(String msg, Object... args) {
verbose(0, msg, args);
}
public static void diagnose(String msg, Object... args) {
diagnose(0, msg, args);
}
public static void verbose(int indent, String msg, Object... args) {
if (isVerboseEnabled()) { if (isVerboseEnabled()) {
String text = String.format(msg + "\n", args); String text = String.format(space(indent) + msg + "\n", args);
System.out.print("[VERBOSE] "); System.out.print("[VERBOSE] ");
System.out.print(text); System.out.print(text);
write("[TIP] "); write("[TIP] ");
@ -26,8 +34,8 @@ public class LogUtil {
} }
} }
public static void diagnose(String msg, Object... args) { public static void diagnose(int indent, String msg, Object... args) {
String text = String.format(msg + "\n", args); String text = String.format(space(indent) + msg + "\n", args);
if (currentLogLevel.level >= LogLevel.ENABLE.level) { if (currentLogLevel.level >= LogLevel.ENABLE.level) {
System.out.print("[DIAGNOSE] "); System.out.print("[DIAGNOSE] ");
System.out.print(text); System.out.print(text);
@ -93,6 +101,10 @@ public class LogUtil {
} }
} }
private static String space(int indent) {
return StringUtil.repeat(" ", indent);
}
private static void write(String text) { private static void write(String text) {
try { try {
if (logFileStream != null) { if (logFileStream != null) {

View File

@ -0,0 +1,13 @@
package com.alibaba.testable.core.util;
public class StringUtil {
public static String repeat(String str, int n) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.append(str);
}
return sb.toString();
}
}