mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-03-26 12:05:04 +08:00
implement new operation replacement
This commit is contained in:
parent
6f292b6334
commit
044f201492
@ -18,6 +18,12 @@
|
||||
<artifactId>asm-tree</artifactId>
|
||||
<version>${asm-lib-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.6.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.alibaba.testable.transformer;
|
||||
|
||||
import com.alibaba.testable.model.TravelStatus;
|
||||
import com.alibaba.testable.util.ClassUtil;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
@ -18,6 +19,8 @@ import static com.alibaba.testable.constant.Const.SYS_CLASSES;
|
||||
*/
|
||||
public class TestableClassTransformer implements Opcodes {
|
||||
|
||||
private static final String CONSTRUCTOR = "<init>";
|
||||
private static final String TESTABLE_NE = "testable_internal/n/e";
|
||||
private final ClassNode cn = new ClassNode();
|
||||
|
||||
public TestableClassTransformer(String className) throws IOException {
|
||||
@ -35,7 +38,7 @@ public class TestableClassTransformer implements Opcodes {
|
||||
private void transform() {
|
||||
List<String> methodNames = new ArrayList<String>();
|
||||
for (MethodNode m : cn.methods) {
|
||||
if (!"<init>".equals(m.name)) {
|
||||
if (!CONSTRUCTOR.equals(m.name)) {
|
||||
methodNames.add(m.name);
|
||||
}
|
||||
}
|
||||
@ -49,47 +52,42 @@ public class TestableClassTransformer implements Opcodes {
|
||||
TravelStatus status = TravelStatus.INIT;
|
||||
String target = "";
|
||||
int rangeStart = 0;
|
||||
for (int i = 0; i < instructions.length; i++) {
|
||||
int i = 0;
|
||||
do {
|
||||
if (instructions[i].getOpcode() == Opcodes.NEW) {
|
||||
TypeInsnNode node = (TypeInsnNode)instructions[i];
|
||||
if (!SYS_CLASSES.contains(node.desc) && node.desc.equals("com/alibaba/testable/BlackBox")) {
|
||||
if (!SYS_CLASSES.contains(node.desc)) {
|
||||
target = node.desc;
|
||||
status = TravelStatus.NEW_REP;
|
||||
rangeStart = i;
|
||||
}
|
||||
}
|
||||
if (instructions[i].getOpcode() == Opcodes.INVOKESPECIAL) {
|
||||
} else if (instructions[i].getOpcode() == Opcodes.INVOKESPECIAL) {
|
||||
MethodInsnNode node = (MethodInsnNode)instructions[i];
|
||||
if (methodNames.contains(node.name) && cn.name.equals(node.owner)) {
|
||||
status = TravelStatus.MEM_REP;
|
||||
}
|
||||
if (TravelStatus.NEW_REP == status && "<init>".equals(node.name) && target.equals(node.owner)) {
|
||||
replaceNewOps(mn, instructions, rangeStart, i);
|
||||
} else if (TravelStatus.NEW_REP == status && CONSTRUCTOR.equals(node.name) && target.equals(node.owner)) {
|
||||
instructions = replaceNewOps(mn, instructions, rangeStart, i);
|
||||
i = rangeStart;
|
||||
status = TravelStatus.INIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
i++;
|
||||
} while (i < instructions.length);
|
||||
}
|
||||
|
||||
private void replaceNewOps(MethodNode mn, AbstractInsnNode[] instructions, int start, int end) {
|
||||
|
||||
private AbstractInsnNode[] replaceNewOps(MethodNode mn, AbstractInsnNode[] instructions, int start, int end) {
|
||||
String classType = ((TypeInsnNode)instructions[start]).desc;
|
||||
String paramTypes = ((MethodInsnNode)instructions[end]).desc;
|
||||
mn.instructions.insertBefore(instructions[start], new LdcInsnNode(Type.getType("L" + classType + ";")));
|
||||
InsnList il = new InsnList();
|
||||
il.add(new LdcInsnNode(Type.getType("Lcom/alibaba/testable/BlackBox;")));
|
||||
il.add(new InsnNode(ICONST_1));
|
||||
il.add(new TypeInsnNode(ANEWARRAY, "java/lang/Object"));
|
||||
il.add(new InsnNode(DUP));
|
||||
il.add(new InsnNode(ICONST_0));
|
||||
il.add(new LdcInsnNode("something"));
|
||||
il.add(new InsnNode(AASTORE));
|
||||
il.add(new MethodInsnNode(INVOKESTATIC, "testable_internal/n/e",
|
||||
"w", "(Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/Object;", false));
|
||||
il.add(new TypeInsnNode(CHECKCAST, "com/alibaba/testable/BlackBox"));
|
||||
mn.instructions.insert(instructions[end], il);
|
||||
|
||||
for (int z = start; z <= end; z++) {
|
||||
mn.instructions.remove(instructions[z]);
|
||||
}
|
||||
|
||||
mn.maxStack += 4;
|
||||
il.add(new MethodInsnNode(INVOKESTATIC, TESTABLE_NE, "w", ClassUtil.generateTargetDesc(paramTypes), false));
|
||||
il.add(new TypeInsnNode(CHECKCAST, classType));
|
||||
mn.instructions.insertBefore(instructions[end], il);
|
||||
mn.instructions.remove(instructions[start]);
|
||||
mn.instructions.remove(instructions[start + 1]);
|
||||
mn.instructions.remove(instructions[end]);
|
||||
mn.maxStack += 1;
|
||||
return mn.instructions.toArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,4 +30,34 @@ public class ClassUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static String generateTargetDesc(String paramTypes) {
|
||||
int paramCount = 0;
|
||||
boolean travelingClass = false;
|
||||
for (byte b : paramTypes.getBytes()) {
|
||||
if (travelingClass) {
|
||||
if (b == ';') {
|
||||
travelingClass = false;
|
||||
}
|
||||
} else {
|
||||
if (b == 'B' || b == 'C' || b == 'D' || b == 'F' || b == 'I' || b == 'J' || b == 'S' || b == 'Z') {
|
||||
paramCount++;
|
||||
} else if (b == 'L') {
|
||||
travelingClass = true;
|
||||
paramCount++;
|
||||
} else if (b == ')') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "(Ljava/lang/Class;" + repeat("Ljava/lang/Object;", paramCount) + ")Ljava/lang/Object;";
|
||||
}
|
||||
|
||||
private static String repeat(String text, int times) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < times; i++) {
|
||||
sb.append(text);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package com.alibaba.testable.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ClassUtilTest {
|
||||
|
||||
@Test
|
||||
void should_able_to_generate_target_desc() {
|
||||
assertEquals("(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
ClassUtil.generateTargetDesc("(Ljava/lang/String;)V"));
|
||||
assertEquals("(Ljava/lang/Class;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
ClassUtil.generateTargetDesc("(Ljava/lang/String;IDLjava/lang/String;ZLjava/net/URL;)V"));
|
||||
assertEquals("(Ljava/lang/Class;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
ClassUtil.generateTargetDesc("(ZLjava/lang/String;IJFDCSBZ)V"));
|
||||
}
|
||||
|
||||
}
|
@ -41,10 +41,62 @@ public final class e {
|
||||
pf.add(np);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct) {
|
||||
return wi(ct);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1) {
|
||||
return wi(ct, a1);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2) {
|
||||
return wi(ct, a1, a2);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3) {
|
||||
return wi(ct, a1, a2, a3);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3, Object a4) {
|
||||
return wi(ct, a1, a2, a3, a4);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3, Object a4, Object a5) {
|
||||
return wi(ct, a1, a2, a3, a4, a5);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) {
|
||||
return wi(ct, a1, a2, a3, a4, a5, a6);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
|
||||
return wi(ct, a1, a2, a3, a4, a5, a6, a7);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) {
|
||||
return wi(ct, a1, a2, a3, a4, a5, a6, a7, a8);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) {
|
||||
return wi(ct, a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9, Object a10) {
|
||||
return wi(ct, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9, Object a10, Object a11) {
|
||||
return wi(ct, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
|
||||
}
|
||||
|
||||
public static <T> T w(Class<T> ct, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9, Object a10, Object a11, Object a12) {
|
||||
return wi(ct, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
|
||||
}
|
||||
|
||||
/**
|
||||
* substitution entry for new
|
||||
*/
|
||||
public static <T> T w(Class<T> ct, Object... as) {
|
||||
public static <T> T wi(Class<T> ct, Object... as) {
|
||||
Class[] cs = TypeUtil.gcs(as);
|
||||
if (!pw.isEmpty()) {
|
||||
try {
|
||||
|
Loading…
Reference in New Issue
Block a user