diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java index e54d7c7..fa72454 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java @@ -122,21 +122,17 @@ public class SourceClassHandler extends BaseClassHandler { for (int i = rangeEnd - 1; i >= 0; i--) { switch (instructions[i].getOpcode()) { case Opcodes.INVOKESPECIAL: - stackLevel += ClassUtil.getParameterTypes(((MethodInsnNode)instructions[i]).desc).size(); + case Opcodes.INVOKEVIRTUAL: + case Opcodes.INVOKEINTERFACE: + stackLevel += stackEffectOfInvocation(instructions[i]) + 1; if (((MethodInsnNode)instructions[i]).name.equals(ConstPool.CONSTRUCTOR)) { - // constructor implicitly eat 2 more stack and return 1 value + // constructor must be INVOKESPECIAL and implicitly pop 1 more stack stackLevel++; } break; case Opcodes.INVOKESTATIC: case Opcodes.INVOKEDYNAMIC: - // static and dynamic invoke implicitly return 1 value - stackLevel += (ClassUtil.getParameterTypes(((MethodInsnNode)instructions[i]).desc).size() - 1); - break; - case Opcodes.INVOKEVIRTUAL: - case Opcodes.INVOKEINTERFACE: - // virtual and interface invoke implicitly eat 1 more stack and return 1 value, deuce - stackLevel += ClassUtil.getParameterTypes(((MethodInsnNode)instructions[i]).desc).size(); + stackLevel += stackEffectOfInvocation(instructions[i]); break; case -1: // reach LineNumberNode or LabelNode @@ -151,6 +147,11 @@ public class SourceClassHandler extends BaseClassHandler { return -1; } + private int stackEffectOfInvocation(AbstractInsnNode instruction) { + String desc = ((MethodInsnNode)instruction).desc; + return ClassUtil.getParameterTypes(desc).size() - (ClassUtil.getReturnType(desc).isEmpty() ? 0 : 1); + } + private AbstractInsnNode[] replaceNewOps(ClassNode cn, MethodNode mn, String newOperatorInjectMethodName, AbstractInsnNode[] instructions, int start, int end) { LogUtil.debug(" Using %s mock new operation in %s", newOperatorInjectMethodName, mn.name); diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/util/ClassUtil.java b/testable-agent/src/main/java/com/alibaba/testable/agent/util/ClassUtil.java index f8e311f..5b1cd48 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/util/ClassUtil.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/util/ClassUtil.java @@ -26,6 +26,7 @@ public class ClassUtil { public static final byte TYPE_CLASS = 'L'; public static final byte TYPE_SHORT = 'S'; public static final byte TYPE_BOOL = 'Z'; + public static final byte TYPE_VOID = 'V'; private static final byte PARAM_END = ')'; private static final byte CLASS_END = ';'; private static final byte TYPE_ARRAY = '['; @@ -39,6 +40,7 @@ public class ClassUtil { private static final String CLASS_LONG = "java/lang/Long"; private static final String CLASS_SHORT = "java/lang/Short"; private static final String CLASS_BOOLEAN = "java/lang/Boolean"; + private static final String EMPTY = ""; private static final String METHOD_VALUE_OF = "valueOf"; private final static String JOINER = "::"; @@ -56,6 +58,7 @@ public class ClassUtil { TYPE_MAPPING.put(TYPE_LONG, CLASS_LONG); TYPE_MAPPING.put(TYPE_SHORT, CLASS_SHORT); TYPE_MAPPING.put(TYPE_BOOL, CLASS_BOOLEAN); + TYPE_MAPPING.put(TYPE_VOID, EMPTY); } /** @@ -159,7 +162,7 @@ public class ClassUtil { } else if (TYPE_MAPPING.containsKey((byte)typeChar)) { return TYPE_MAPPING.get((byte)typeChar); } else { - return ""; + return EMPTY; } }