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 // it's a member or static method and an inject method for it exist
int rangeStart = getMemberMethodStart(instructions, i); int rangeStart = getMemberMethodStart(instructions, i);
if (rangeStart >= 0) { if (rangeStart >= 0) {
handleFrameStackChange(mn, mockMethod, rangeStart, i);
instructions = replaceMemberCallOps(mn, mockMethod, instructions = replaceMemberCallOps(mn, mockMethod,
instructions, node.owner, node.getOpcode(), rangeStart, i); instructions, node.owner, node.getOpcode(), rangeStart, i);
i = rangeStart; i = rangeStart;
@ -249,6 +250,17 @@ public class SourceClassHandler extends BaseClassHandler {
return mn.instructions.toArray(); 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) { private boolean isCompanionMethod(String ownerClass, int opcode) {
return Opcodes.INVOKEVIRTUAL == opcode && ClassUtil.isCompanionClassName(ownerClass); return Opcodes.INVOKEVIRTUAL == opcode && ClassUtil.isCompanionClassName(ownerClass);
} }

View File

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

View File

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