diff --git a/agent/src/main/java/com/alibaba/testable/constant/Const.java b/agent/src/main/java/com/alibaba/testable/constant/Const.java index 9ab1b5c..f75f880 100644 --- a/agent/src/main/java/com/alibaba/testable/constant/Const.java +++ b/agent/src/main/java/com/alibaba/testable/constant/Const.java @@ -1,5 +1,8 @@ package com.alibaba.testable.constant; +import java.util.ArrayList; +import java.util.List; + /** * @author flin */ @@ -8,4 +11,9 @@ public class Const { public static final String DOT = "."; public static final String SLASH = "/"; + public static final List SYS_CLASSES = new ArrayList(); + static { + SYS_CLASSES.add("java/lang/StringBuilder"); + } + } diff --git a/agent/src/main/java/com/alibaba/testable/model/TravelStatus.java b/agent/src/main/java/com/alibaba/testable/model/TravelStatus.java new file mode 100644 index 0000000..2f55ddb --- /dev/null +++ b/agent/src/main/java/com/alibaba/testable/model/TravelStatus.java @@ -0,0 +1,22 @@ +package com.alibaba.testable.model; + +/** + * @author flin + */ + +public enum TravelStatus { + + /** + * Initialized + */ + INIT, + /** + * Processing member method replacement + */ + MEM_REP, + /** + * Processing member operation replacement + */ + NEW_REP + +} diff --git a/agent/src/main/java/com/alibaba/testable/transformer/TestableClassTransformer.java b/agent/src/main/java/com/alibaba/testable/transformer/TestableClassTransformer.java index 77acb45..f214eb5 100644 --- a/agent/src/main/java/com/alibaba/testable/transformer/TestableClassTransformer.java +++ b/agent/src/main/java/com/alibaba/testable/transformer/TestableClassTransformer.java @@ -1,11 +1,17 @@ package com.alibaba.testable.transformer; +import com.alibaba.testable.model.TravelStatus; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static com.alibaba.testable.constant.Const.SYS_CLASSES; /** * @author flin @@ -27,7 +33,63 @@ public class TestableClassTransformer implements Opcodes { } private void transform() { + List methodNames = new ArrayList(); + for (MethodNode m : cn.methods) { + if (!"".equals(m.name)) { + methodNames.add(m.name); + } + } + for (MethodNode m : cn.methods) { + transformMethod(m, methodNames); + } + } + private void transformMethod(MethodNode mn, List methodNames) { + AbstractInsnNode[] instructions = mn.instructions.toArray(); + TravelStatus status = TravelStatus.INIT; + String target = ""; + int rangeStart = 0; + for (int i = 0; i < instructions.length; i++) { + if (instructions[i].getOpcode() == Opcodes.NEW) { + TypeInsnNode node = (TypeInsnNode)instructions[i]; + if (!SYS_CLASSES.contains(node.desc) && node.desc.equals("com/alibaba/testable/BlackBox")) { + target = node.desc; + status = TravelStatus.NEW_REP; + rangeStart = i; + } + } + 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 && "".equals(node.name) && target.equals(node.owner)) { + replaceNewOps(mn, instructions, rangeStart, i); + } + } + } + } + + private void replaceNewOps(MethodNode mn, AbstractInsnNode[] instructions, int start, int end) { + + 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; } } diff --git a/agent/src/main/java/com/alibaba/testable/transformer/TestableFileTransformer.java b/agent/src/main/java/com/alibaba/testable/transformer/TestableFileTransformer.java index 147a8da..47e8197 100644 --- a/agent/src/main/java/com/alibaba/testable/transformer/TestableFileTransformer.java +++ b/agent/src/main/java/com/alibaba/testable/transformer/TestableFileTransformer.java @@ -23,8 +23,8 @@ public class TestableFileTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { - if (isSystemClass(loader, className)) { - // Ignore system class + if (isSystemClass(loader, className) || loadedClassNames.contains(className)) { + // Ignore system class and duplicate class return null; } @@ -36,6 +36,7 @@ public class TestableFileTransformer implements ClassFileTransformer { } try { + loadedClassNames.add(className); return new TestableClassTransformer(className).getBytes(); } catch (IOException e) { return null;