consider return type when calculating stack change

This commit is contained in:
金戟 2020-11-16 23:13:45 +08:00
parent 43cd4aeeec
commit 7d2dd83410
2 changed files with 14 additions and 10 deletions

View File

@ -122,21 +122,17 @@ public class SourceClassHandler extends BaseClassHandler {
for (int i = rangeEnd - 1; i >= 0; i--) { for (int i = rangeEnd - 1; i >= 0; i--) {
switch (instructions[i].getOpcode()) { switch (instructions[i].getOpcode()) {
case Opcodes.INVOKESPECIAL: 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)) { 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++; stackLevel++;
} }
break; break;
case Opcodes.INVOKESTATIC: case Opcodes.INVOKESTATIC:
case Opcodes.INVOKEDYNAMIC: case Opcodes.INVOKEDYNAMIC:
// static and dynamic invoke implicitly return 1 value stackLevel += stackEffectOfInvocation(instructions[i]);
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();
break; break;
case -1: case -1:
// reach LineNumberNode or LabelNode // reach LineNumberNode or LabelNode
@ -151,6 +147,11 @@ public class SourceClassHandler extends BaseClassHandler {
return -1; 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, private AbstractInsnNode[] replaceNewOps(ClassNode cn, MethodNode mn, String newOperatorInjectMethodName,
AbstractInsnNode[] instructions, int start, int end) { AbstractInsnNode[] instructions, int start, int end) {
LogUtil.debug(" Using %s mock new operation in %s", newOperatorInjectMethodName, mn.name); LogUtil.debug(" Using %s mock new operation in %s", newOperatorInjectMethodName, mn.name);

View File

@ -26,6 +26,7 @@ public class ClassUtil {
public static final byte TYPE_CLASS = 'L'; public static final byte TYPE_CLASS = 'L';
public static final byte TYPE_SHORT = 'S'; public static final byte TYPE_SHORT = 'S';
public static final byte TYPE_BOOL = 'Z'; public static final byte TYPE_BOOL = 'Z';
public static final byte TYPE_VOID = 'V';
private static final byte PARAM_END = ')'; private static final byte PARAM_END = ')';
private static final byte CLASS_END = ';'; private static final byte CLASS_END = ';';
private static final byte TYPE_ARRAY = '['; 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_LONG = "java/lang/Long";
private static final String CLASS_SHORT = "java/lang/Short"; private static final String CLASS_SHORT = "java/lang/Short";
private static final String CLASS_BOOLEAN = "java/lang/Boolean"; private static final String CLASS_BOOLEAN = "java/lang/Boolean";
private static final String EMPTY = "";
private static final String METHOD_VALUE_OF = "valueOf"; private static final String METHOD_VALUE_OF = "valueOf";
private final static String JOINER = "::"; private final static String JOINER = "::";
@ -56,6 +58,7 @@ public class ClassUtil {
TYPE_MAPPING.put(TYPE_LONG, CLASS_LONG); TYPE_MAPPING.put(TYPE_LONG, CLASS_LONG);
TYPE_MAPPING.put(TYPE_SHORT, CLASS_SHORT); TYPE_MAPPING.put(TYPE_SHORT, CLASS_SHORT);
TYPE_MAPPING.put(TYPE_BOOL, CLASS_BOOLEAN); 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)) { } else if (TYPE_MAPPING.containsKey((byte)typeChar)) {
return TYPE_MAPPING.get((byte)typeChar); return TYPE_MAPPING.get((byte)typeChar);
} else { } else {
return ""; return EMPTY;
} }
} }