mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-02-03 16:20:54 +08:00
add verbose log for omni constructor
This commit is contained in:
parent
b89daa22c3
commit
a67619d574
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user