implement new operation replacement

This commit is contained in:
金戟 2020-07-21 23:23:56 +08:00
parent 6f292b6334
commit 044f201492
5 changed files with 134 additions and 29 deletions

View File

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

View File

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

View File

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

View File

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

View File

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