should handle frame byte code when mock substitutions

This commit is contained in:
金戟 2021-02-24 14:31:50 +08:00
parent d49f526376
commit 190cf4cc3c
3 changed files with 30 additions and 6 deletions

View File

@ -84,6 +84,7 @@ public class SourceClassHandler extends BaseClassHandler {
// it's a member or static method and an inject method for it exist
int rangeStart = getMemberMethodStart(instructions, i);
if (rangeStart >= 0) {
handleFrameStackChange(mn, mockMethod, rangeStart, i);
instructions = replaceMemberCallOps(mn, mockMethod,
instructions, node.owner, node.getOpcode(), rangeStart, i);
i = rangeStart;
@ -249,6 +250,17 @@ public class SourceClassHandler extends BaseClassHandler {
return mn.instructions.toArray();
}
private void handleFrameStackChange(MethodNode mn, MethodInfo mockMethod, int start, int end) {
AbstractInsnNode curInsn = mn.instructions.get(start);
AbstractInsnNode endInsn = mn.instructions.get(end);
do {
if (curInsn instanceof FrameNode && ((FrameNode)curInsn).type == F_FULL) {
((FrameNode)curInsn).stack.add(0, mockMethod.getMockClass());
}
curInsn = curInsn.getNext();
} while (!curInsn.equals(endInsn));
}
private boolean isCompanionMethod(String ownerClass, int opcode) {
return Opcodes.INVOKEVIRTUAL == opcode && ClassUtil.isCompanionClassName(ownerClass);
}

View File

@ -17,6 +17,10 @@ public class MethodInfo {
* parameter and return value of the source method
*/
private final String desc;
/**
* name of the class where this mock method defined (in slash-separate format)
*/
private final String mockClass;
/**
* name of the mock method
*/
@ -30,10 +34,11 @@ public class MethodInfo {
*/
private final boolean isStatic;
public MethodInfo(String clazz, String name, String desc, String mockName, String mockDesc, boolean isStatic) {
public MethodInfo(String clazz, String name, String desc, String mockClass, String mockName, String mockDesc, boolean isStatic) {
this.clazz = clazz;
this.name = name;
this.desc = desc;
this.mockClass = mockClass;
this.mockName = mockName;
this.mockDesc = mockDesc;
this.isStatic = isStatic;
@ -51,6 +56,10 @@ public class MethodInfo {
return desc;
}
public String getMockClass() {
return mockClass;
}
public String getMockName() {
return mockName;
}
@ -74,6 +83,7 @@ public class MethodInfo {
if (!clazz.equals(that.clazz)) { return false; }
if (!name.equals(that.name)) { return false; }
if (!desc.equals(that.desc)) { return false; }
if (!mockClass.equals(that.mockClass)) { return false; }
if (!mockName.equals(that.mockName)) { return false; }
return mockDesc.equals(that.mockDesc);
}
@ -83,6 +93,7 @@ public class MethodInfo {
int result = clazz.hashCode();
result = 31 * result + name.hashCode();
result = 31 * result + desc.hashCode();
result = 31 * result + mockClass.hashCode();
result = 31 * result + mockName.hashCode();
result = 31 * result + mockDesc.hashCode();
result = 31 * result + (isStatic ? 1 : 0);

View File

@ -96,7 +96,7 @@ public class MockClassParser {
if (CONSTRUCTOR.equals(targetMethod)) {
addMockConstructor(methodInfos, cn, mn);
} else {
MethodInfo mi = getMethodInfo(mn, an, targetMethod);
MethodInfo mi = getMethodInfo(cn, mn, an, targetMethod);
if (mi != null) {
methodInfos.add(mi);
}
@ -112,7 +112,7 @@ public class MockClassParser {
return type == null ? MethodUtil.removeFirstParameter(mn.desc) : mn.desc;
}
private MethodInfo getMethodInfo(MethodNode mn, AnnotationNode an, String targetMethod) {
private MethodInfo getMethodInfo(ClassNode cn, MethodNode mn, AnnotationNode an, String targetMethod) {
Type targetType = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_CLASS, null, Type.class);
boolean isStatic = isStatic(mn);
if (targetType == null) {
@ -121,18 +121,19 @@ public class MockClassParser {
if (methodDescPair == null) {
return null;
}
return new MethodInfo(methodDescPair.left, targetMethod, methodDescPair.right, mn.name, mn.desc, isStatic);
return new MethodInfo(methodDescPair.left, targetMethod, methodDescPair.right, cn.name, mn.name, mn.desc,
isStatic);
} else {
// "targetClass" found, use it as target class type
String slashSeparatedName = ClassUtil.toSlashSeparatedName(targetType.getClassName());
return new MethodInfo(slashSeparatedName, targetMethod, mn.desc, mn.name,
return new MethodInfo(slashSeparatedName, targetMethod, mn.desc, cn.name, mn.name,
MethodUtil.addParameterAtBegin(mn.desc, ClassUtil.toByteCodeClassName(slashSeparatedName)), isStatic);
}
}
private void addMockConstructor(List<MethodInfo> methodInfos, ClassNode cn, MethodNode mn) {
String sourceClassName = ClassUtil.getSourceClassName(cn.name);
methodInfos.add(new MethodInfo(sourceClassName, CONSTRUCTOR, mn.desc, mn.name, mn.desc, isStatic(mn)));
methodInfos.add(new MethodInfo(sourceClassName, CONSTRUCTOR, mn.desc, cn.name, mn.name, mn.desc, isStatic(mn)));
}
/**