mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-01 07:50:22 +08:00
use asm tree api
This commit is contained in:
parent
612173f193
commit
a1ee894faf
@ -8,11 +8,15 @@
|
||||
<version>0.0.2-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<asm-lib-version>8.0.1</asm-lib-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>8.0.1</version>
|
||||
<artifactId>asm-tree</artifactId>
|
||||
<version>${asm-lib-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
11
agent/src/main/java/com/alibaba/testable/constant/Const.java
Normal file
11
agent/src/main/java/com/alibaba/testable/constant/Const.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.alibaba.testable.constant;
|
||||
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class Const {
|
||||
|
||||
public static final String DOT = ".";
|
||||
public static final String SLASH = "/";
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.alibaba.testable.transformer;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class TestableClassTransformer implements Opcodes {
|
||||
|
||||
private final ClassNode cn = new ClassNode();
|
||||
|
||||
public TestableClassTransformer(String className) throws IOException {
|
||||
ClassReader cr = new ClassReader(className);
|
||||
cr.accept(cn, 0);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
transform();
|
||||
ClassWriter cw = new ClassWriter( 0);
|
||||
cn.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private void transform() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1,13 @@
|
||||
package com.alibaba.testable.transformer;
|
||||
|
||||
import com.alibaba.testable.visitor.MethodRecordVisitor;
|
||||
import com.alibaba.testable.visitor.TestableVisitor;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import com.alibaba.testable.util.ClassUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.io.IOException;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.net.URLClassLoader;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -17,55 +16,40 @@ import java.util.Set;
|
||||
public class TestableFileTransformer implements ClassFileTransformer {
|
||||
|
||||
private static final String ENABLE_TESTABLE = "com.alibaba.testable.annotation.EnableTestable";
|
||||
private static final String DOT = ".";
|
||||
private static final String SLASH = "/";
|
||||
private static final String ENABLE_TESTABLE_INJECT = "com.alibaba.testable.annotation.EnableTestableInject";
|
||||
private static final String TEST_POSTFIX = "Test";
|
||||
|
||||
private static final Set<String> loadedClassNames = new HashSet<String>();
|
||||
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||
ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
||||
if (null == loader || null == className) {
|
||||
if (isSystemClass(loader, className)) {
|
||||
// Ignore system class
|
||||
return null;
|
||||
}
|
||||
String dotClassName = className.replace(SLASH, DOT);
|
||||
loadedClassNames.add(dotClassName);
|
||||
MethodRecordVisitor methodRecordVisitor = getMemberMethods(classfileBuffer, checkTestClass(dotClassName));
|
||||
if (!methodRecordVisitor.isNeedTransform()) {
|
||||
|
||||
List<String> annotations = ClassUtil.getAnnotations(className);
|
||||
List<String> testAnnotations = ClassUtil.getAnnotations(className + TEST_POSTFIX);
|
||||
if (!isNeedTransform(annotations, testAnnotations)) {
|
||||
// Neither EnableTestable on test class, nor EnableTestableInject on source class
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassReader reader = new ClassReader(classfileBuffer);
|
||||
ClassWriter writer = new ClassWriter(reader, 0);
|
||||
reader.accept(new TestableVisitor(writer, methodRecordVisitor.getMethods()), 0);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
private boolean checkTestClass(String dotClassName) {
|
||||
String testClassName = dotClassName + TEST_POSTFIX;
|
||||
if (loadedClassNames.contains(testClassName)) {
|
||||
try {
|
||||
Class<?> testClazz = Class.forName(testClassName);
|
||||
for (Annotation a : testClazz.getAnnotations()) {
|
||||
if (a.annotationType().getName().equals(ENABLE_TESTABLE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return new TestableClassTransformer(className).getBytes();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MethodRecordVisitor getMemberMethods(byte[] classfileBuffer, boolean needTransform) {
|
||||
ClassReader reader = new ClassReader(classfileBuffer);
|
||||
ClassWriter writer = new ClassWriter(reader, 0);
|
||||
MethodRecordVisitor visitor = new MethodRecordVisitor(writer, needTransform);
|
||||
reader.accept(visitor, 0);
|
||||
return visitor;
|
||||
private boolean isSystemClass(ClassLoader loader, String className) {
|
||||
return !(loader instanceof URLClassLoader) || null == className || className.startsWith("jdk/");
|
||||
}
|
||||
|
||||
private boolean isNeedTransform(List<String> annotations, List<String> testAnnotations) {
|
||||
return annotations != null &&
|
||||
(annotations.contains(ENABLE_TESTABLE_INJECT) ||
|
||||
(testAnnotations != null && testAnnotations.contains(ENABLE_TESTABLE)));
|
||||
}
|
||||
|
||||
}
|
||||
|
33
agent/src/main/java/com/alibaba/testable/util/ClassUtil.java
Normal file
33
agent/src/main/java/com/alibaba/testable/util/ClassUtil.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.alibaba.testable.util;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.AnnotationNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.alibaba.testable.constant.Const.*;
|
||||
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class ClassUtil {
|
||||
|
||||
public static List<String> getAnnotations(String className) {
|
||||
try {
|
||||
List<String> annotations = new ArrayList<String>();
|
||||
ClassNode cn = new ClassNode();
|
||||
new ClassReader(className).accept(cn, 0);
|
||||
for (AnnotationNode an : cn.visibleAnnotations) {
|
||||
String annotationName = an.desc.replace(SLASH, DOT).substring(1, an.desc.length() - 1);
|
||||
annotations.add(annotationName);
|
||||
}
|
||||
return annotations;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package com.alibaba.testable.visitor;
|
||||
|
||||
import com.alibaba.testable.model.MethodInfo;
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MethodRecordVisitor extends ClassVisitor {
|
||||
|
||||
/**
|
||||
* Member methods
|
||||
*/
|
||||
private final List<MethodInfo> methods = new ArrayList<MethodInfo>();
|
||||
private boolean needTransform;
|
||||
|
||||
private static final String ENABLE_TESTABLE_INJECT = "Lcom/alibaba/testable/annotation/EnableTestableInject;";
|
||||
|
||||
public List<MethodInfo> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public boolean isNeedTransform() {
|
||||
return needTransform;
|
||||
}
|
||||
|
||||
public MethodRecordVisitor(ClassWriter cw, boolean needTransform) {
|
||||
super(Opcodes.ASM8, cw);
|
||||
this.needTransform = needTransform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||
if (descriptor.equals(ENABLE_TESTABLE_INJECT)) {
|
||||
needTransform = true;
|
||||
}
|
||||
return super.visitAnnotation(descriptor, visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
methods.add(new MethodInfo(access, name, desc, signature, exceptions));
|
||||
return super.visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user