mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-27 12:51:00 +08:00
refactor and reduce stack size
This commit is contained in:
parent
bdd99577c4
commit
65cc631d39
@ -0,0 +1,19 @@
|
|||||||
|
package com.alibaba.testable.agent.constant;
|
||||||
|
|
||||||
|
public class ByteCodeConst {
|
||||||
|
|
||||||
|
public static final byte TYPE_BYTE = 'B';
|
||||||
|
public static final byte TYPE_CHAR = 'C';
|
||||||
|
public static final byte TYPE_DOUBLE = 'D';
|
||||||
|
public static final byte TYPE_FLOAT = 'F';
|
||||||
|
public static final byte TYPE_INT = 'I';
|
||||||
|
public static final byte TYPE_LONG = 'J';
|
||||||
|
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';
|
||||||
|
public static final byte PARAM_END = ')';
|
||||||
|
public static final byte CLASS_END = ';';
|
||||||
|
public static final byte TYPE_ARRAY = '[';
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.alibaba.testable.agent.handler;
|
package com.alibaba.testable.agent.handler;
|
||||||
|
|
||||||
|
import com.alibaba.testable.agent.constant.ByteCodeConst;
|
||||||
import com.alibaba.testable.agent.constant.ConstPool;
|
import com.alibaba.testable.agent.constant.ConstPool;
|
||||||
import com.alibaba.testable.agent.tool.ImmutablePair;
|
import com.alibaba.testable.agent.tool.ImmutablePair;
|
||||||
import com.alibaba.testable.agent.util.AnnotationUtil;
|
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||||
@ -92,7 +93,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
if (targetClassName != null) {
|
if (targetClassName != null) {
|
||||||
// must get label before method description changed
|
// must get label before method description changed
|
||||||
ImmutablePair<LabelNode, LabelNode> labels = getStartAndEndLabel(mn);
|
ImmutablePair<LabelNode, LabelNode> labels = getStartAndEndLabel(mn);
|
||||||
mn.desc = ClassUtil.addParameterAtBegin(mn.desc, targetClassName);
|
mn.desc = MethodUtil.addParameterAtBegin(mn.desc, targetClassName);
|
||||||
int parameterOffset = MethodUtil.isStaticMethod(mn) ? 0 : 1;
|
int parameterOffset = MethodUtil.isStaticMethod(mn) ? 0 : 1;
|
||||||
mn.localVariables.add(parameterOffset, new LocalVariableNode("__self", targetClassName, null,
|
mn.localVariables.add(parameterOffset, new LocalVariableNode("__self", targetClassName, null,
|
||||||
labels.left, labels.right, parameterOffset));
|
labels.left, labels.right, parameterOffset));
|
||||||
@ -121,7 +122,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ClassUtil.extractParameters(mn.desc).isEmpty()) {
|
if (MethodUtil.extractParameters(mn.desc).isEmpty()) {
|
||||||
// for method without parameter, should manually add a ending label
|
// for method without parameter, should manually add a ending label
|
||||||
endLabel = new LabelNode(new Label());
|
endLabel = new LabelNode(new Label());
|
||||||
mn.instructions.add(endLabel);
|
mn.instructions.add(endLabel);
|
||||||
@ -158,14 +159,13 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
|
|
||||||
private InsnList invokeOriginalMethod(MethodNode mn) {
|
private InsnList invokeOriginalMethod(MethodNode mn) {
|
||||||
InsnList il = new InsnList();
|
InsnList il = new InsnList();
|
||||||
mn.maxStack += 3;
|
|
||||||
ImmutablePair<Type, String> target = getTargetClassAndMethodName(mn);
|
ImmutablePair<Type, String> target = getTargetClassAndMethodName(mn);
|
||||||
il.add(new LdcInsnNode(target.left));
|
il.add(new LdcInsnNode(target.left));
|
||||||
il.add(new LdcInsnNode(target.right));
|
il.add(new LdcInsnNode(target.right));
|
||||||
il.add(duplicateParameters(mn));
|
il.add(duplicateParameters(mn));
|
||||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_MOCK_ASSOCIATION_UTIL, METHOD_INVOKE_ORIGIN,
|
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_MOCK_ASSOCIATION_UTIL, METHOD_INVOKE_ORIGIN,
|
||||||
SIGNATURE_INVOKE_ORIGIN, false));
|
SIGNATURE_INVOKE_ORIGIN, false));
|
||||||
String returnType = ClassUtil.getReturnType(mn.desc);
|
String returnType = MethodUtil.getReturnType(mn.desc);
|
||||||
if (VOID_RES.equals(returnType)) {
|
if (VOID_RES.equals(returnType)) {
|
||||||
il.add(new InsnNode(POP));
|
il.add(new InsnNode(POP));
|
||||||
il.add(new InsnNode(RETURN));
|
il.add(new InsnNode(RETURN));
|
||||||
@ -197,9 +197,9 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (methodName.equals(CONSTRUCTOR)) {
|
if (methodName.equals(CONSTRUCTOR)) {
|
||||||
className = Type.getType(ClassUtil.getReturnType(mn.desc));
|
className = Type.getType(MethodUtil.getReturnType(mn.desc));
|
||||||
} else {
|
} else {
|
||||||
className = Type.getType(ClassUtil.getFirstParameter(mn.desc));
|
className = Type.getType(MethodUtil.getFirstParameter(mn.desc));
|
||||||
}
|
}
|
||||||
return ImmutablePair.of(className, methodName);
|
return ImmutablePair.of(className, methodName);
|
||||||
}
|
}
|
||||||
@ -233,7 +233,6 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
|
|
||||||
private void injectInvokeRecorder(MethodNode mn) {
|
private void injectInvokeRecorder(MethodNode mn) {
|
||||||
InsnList il = new InsnList();
|
InsnList il = new InsnList();
|
||||||
mn.maxStack += 2;
|
|
||||||
il.add(duplicateParameters(mn));
|
il.add(duplicateParameters(mn));
|
||||||
if (isMockForConstructor(mn)) {
|
if (isMockForConstructor(mn)) {
|
||||||
il.add(new InsnNode(ICONST_1));
|
il.add(new InsnNode(ICONST_1));
|
||||||
@ -243,17 +242,17 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_INVOKE_RECORD_UTIL, METHOD_RECORD_MOCK_INVOKE,
|
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_INVOKE_RECORD_UTIL, METHOD_RECORD_MOCK_INVOKE,
|
||||||
SIGNATURE_RECORDER_METHOD_INVOKE, false));
|
SIGNATURE_RECORDER_METHOD_INVOKE, false));
|
||||||
mn.instructions.insertBefore(mn.instructions.getFirst(), il);
|
mn.instructions.insertBefore(mn.instructions.getFirst(), il);
|
||||||
|
mn.maxStack += (2 + MethodUtil.getParameterTypes(mn.desc).size() * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InsnList duplicateParameters(MethodNode mn) {
|
private InsnList duplicateParameters(MethodNode mn) {
|
||||||
InsnList il = new InsnList();
|
InsnList il = new InsnList();
|
||||||
List<Byte> types = ClassUtil.getParameterTypes(mn.desc);
|
List<Byte> types = MethodUtil.getParameterTypes(mn.desc);
|
||||||
int size = types.size();
|
int size = types.size();
|
||||||
il.add(getIntInsn(size));
|
il.add(getIntInsn(size));
|
||||||
il.add(new TypeInsnNode(ANEWARRAY, ClassUtil.CLASS_OBJECT));
|
il.add(new TypeInsnNode(ANEWARRAY, ClassUtil.CLASS_OBJECT));
|
||||||
int parameterOffset = MethodUtil.isStaticMethod(mn) ? 0 : 1;
|
int parameterOffset = MethodUtil.isStaticMethod(mn) ? 0 : 1;
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
mn.maxStack += 3;
|
|
||||||
il.add(new InsnNode(DUP));
|
il.add(new InsnNode(DUP));
|
||||||
il.add(getIntInsn(i));
|
il.add(getIntInsn(i));
|
||||||
ImmutablePair<Integer, Integer> code = getLoadParameterByteCode(types.get(i));
|
ImmutablePair<Integer, Integer> code = getLoadParameterByteCode(types.get(i));
|
||||||
@ -286,17 +285,17 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
|
|
||||||
private static ImmutablePair<Integer, Integer> getLoadParameterByteCode(Byte type) {
|
private static ImmutablePair<Integer, Integer> getLoadParameterByteCode(Byte type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ClassUtil.TYPE_BYTE:
|
case ByteCodeConst.TYPE_BYTE:
|
||||||
case ClassUtil.TYPE_CHAR:
|
case ByteCodeConst.TYPE_CHAR:
|
||||||
case ClassUtil.TYPE_SHORT:
|
case ByteCodeConst.TYPE_SHORT:
|
||||||
case ClassUtil.TYPE_INT:
|
case ByteCodeConst.TYPE_INT:
|
||||||
case ClassUtil.TYPE_BOOL:
|
case ByteCodeConst.TYPE_BOOL:
|
||||||
return ImmutablePair.of(ILOAD, 1);
|
return ImmutablePair.of(ILOAD, 1);
|
||||||
case ClassUtil.TYPE_DOUBLE:
|
case ByteCodeConst.TYPE_DOUBLE:
|
||||||
return ImmutablePair.of(DLOAD, 2);
|
return ImmutablePair.of(DLOAD, 2);
|
||||||
case ClassUtil.TYPE_FLOAT:
|
case ByteCodeConst.TYPE_FLOAT:
|
||||||
return ImmutablePair.of(FLOAD, 1);
|
return ImmutablePair.of(FLOAD, 1);
|
||||||
case ClassUtil.TYPE_LONG:
|
case ByteCodeConst.TYPE_LONG:
|
||||||
return ImmutablePair.of(LLOAD, 2);
|
return ImmutablePair.of(LLOAD, 2);
|
||||||
default:
|
default:
|
||||||
return ImmutablePair.of(ALOAD, 1);
|
return ImmutablePair.of(ALOAD, 1);
|
||||||
|
@ -4,6 +4,7 @@ import com.alibaba.testable.agent.model.MethodInfo;
|
|||||||
import com.alibaba.testable.agent.model.ModifiedInsnNodes;
|
import com.alibaba.testable.agent.model.ModifiedInsnNodes;
|
||||||
import com.alibaba.testable.agent.util.BytecodeUtil;
|
import com.alibaba.testable.agent.util.BytecodeUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
|
import com.alibaba.testable.agent.util.MethodUtil;
|
||||||
import com.alibaba.testable.core.util.LogUtil;
|
import com.alibaba.testable.core.util.LogUtil;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
@ -115,7 +116,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
String nodeOwner = ClassUtil.fitCompanionClassName(node.owner);
|
String nodeOwner = ClassUtil.fitCompanionClassName(node.owner);
|
||||||
String nodeName = ClassUtil.fitKotlinAccessorName(node.name);
|
String nodeName = ClassUtil.fitKotlinAccessorName(node.name);
|
||||||
// Kotlin accessor method will append a extra type parameter
|
// Kotlin accessor method will append a extra type parameter
|
||||||
String nodeDesc = nodeName.equals(node.name) ? node.desc : ClassUtil.removeFirstParameter(node.desc);
|
String nodeDesc = nodeName.equals(node.name) ? node.desc : MethodUtil.removeFirstParameter(node.desc);
|
||||||
if (m.getClazz().equals(nodeOwner) && m.getName().equals(nodeName) && m.getDesc().equals(nodeDesc)) {
|
if (m.getClazz().equals(nodeOwner) && m.getName().equals(nodeName) && m.getDesc().equals(nodeDesc)) {
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
@ -161,7 +162,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getInitialStackLevel(MethodInsnNode instruction) {
|
private int getInitialStackLevel(MethodInsnNode instruction) {
|
||||||
int stackLevel = ClassUtil.getParameterTypes((instruction).desc).size();
|
int stackLevel = MethodUtil.getParameterTypes((instruction).desc).size();
|
||||||
switch (instruction.getOpcode()) {
|
switch (instruction.getOpcode()) {
|
||||||
case Opcodes.INVOKESPECIAL:
|
case Opcodes.INVOKESPECIAL:
|
||||||
case Opcodes.INVOKEVIRTUAL:
|
case Opcodes.INVOKEVIRTUAL:
|
||||||
@ -194,7 +195,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int stackEffectOfInvocation(String desc) {
|
private int stackEffectOfInvocation(String desc) {
|
||||||
return ClassUtil.getParameterTypes(desc).size() - (ClassUtil.getReturnType(desc).equals(VOID_RES) ? 0 : 1);
|
return MethodUtil.getParameterTypes(desc).size() - (MethodUtil.getReturnType(desc).equals(VOID_RES) ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModifiedInsnNodes replaceNewOps(MethodNode mn, MethodInfo newOperatorInjectMethod,
|
private ModifiedInsnNodes replaceNewOps(MethodNode mn, MethodInfo newOperatorInjectMethod,
|
||||||
|
@ -6,6 +6,7 @@ import com.alibaba.testable.agent.tool.ImmutablePair;
|
|||||||
import com.alibaba.testable.agent.util.AnnotationUtil;
|
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
import com.alibaba.testable.agent.util.DiagnoseUtil;
|
import com.alibaba.testable.agent.util.DiagnoseUtil;
|
||||||
|
import com.alibaba.testable.agent.util.MethodUtil;
|
||||||
import com.alibaba.testable.core.util.LogUtil;
|
import com.alibaba.testable.core.util.LogUtil;
|
||||||
import com.alibaba.testable.core.util.MockAssociationUtil;
|
import com.alibaba.testable.core.util.MockAssociationUtil;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
@ -92,7 +93,7 @@ public class MockClassParser {
|
|||||||
String fullClassName = toDotSeparateFullClassName(an.desc);
|
String fullClassName = toDotSeparateFullClassName(an.desc);
|
||||||
if (fullClassName.equals(ConstPool.MOCK_CONSTRUCTOR)) {
|
if (fullClassName.equals(ConstPool.MOCK_CONSTRUCTOR)) {
|
||||||
LogUtil.verbose(" Mock constructor \"%s\" as \"(%s)V\" for \"%s\"", mn.name,
|
LogUtil.verbose(" Mock constructor \"%s\" as \"(%s)V\" for \"%s\"", mn.name,
|
||||||
ClassUtil.extractParameters(mn.desc), ClassUtil.getReturnType(mn.desc));
|
MethodUtil.extractParameters(mn.desc), MethodUtil.getReturnType(mn.desc));
|
||||||
addMockConstructor(methodInfos, cn, mn);
|
addMockConstructor(methodInfos, cn, mn);
|
||||||
} else if (fullClassName.equals(ConstPool.MOCK_METHOD)) {
|
} else if (fullClassName.equals(ConstPool.MOCK_METHOD)) {
|
||||||
LogUtil.verbose(" Mock method \"%s\" as \"%s\"", mn.name, getTargetMethodDesc(mn, an));
|
LogUtil.verbose(" Mock method \"%s\" as \"%s\"", mn.name, getTargetMethodDesc(mn, an));
|
||||||
@ -114,7 +115,7 @@ public class MockClassParser {
|
|||||||
private String getTargetMethodDesc(MethodNode mn, AnnotationNode mockMethodAnnotation) {
|
private String getTargetMethodDesc(MethodNode mn, AnnotationNode mockMethodAnnotation) {
|
||||||
Type type = AnnotationUtil.getAnnotationParameter(mockMethodAnnotation, ConstPool.FIELD_TARGET_CLASS,
|
Type type = AnnotationUtil.getAnnotationParameter(mockMethodAnnotation, ConstPool.FIELD_TARGET_CLASS,
|
||||||
null, Type.class);
|
null, Type.class);
|
||||||
return type == null ? ClassUtil.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(MethodNode mn, AnnotationNode an, String targetMethod) {
|
||||||
@ -131,7 +132,7 @@ public class MockClassParser {
|
|||||||
// "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, mn.name,
|
||||||
ClassUtil.addParameterAtBegin(mn.desc, ClassUtil.toByteCodeClassName(slashSeparatedName)), isStatic);
|
MethodUtil.addParameterAtBegin(mn.desc, ClassUtil.toByteCodeClassName(slashSeparatedName)), isStatic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,10 @@ import org.objectweb.asm.tree.ClassNode;
|
|||||||
import org.objectweb.asm.tree.MethodInsnNode;
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.alibaba.testable.agent.constant.ByteCodeConst.*;
|
||||||
import static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX;
|
import static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX;
|
||||||
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
|
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
|
||||||
import static org.objectweb.asm.Opcodes.*;
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
@ -21,20 +20,6 @@ import static org.objectweb.asm.Opcodes.*;
|
|||||||
*/
|
*/
|
||||||
public class ClassUtil {
|
public class ClassUtil {
|
||||||
|
|
||||||
public static final byte TYPE_BYTE = 'B';
|
|
||||||
public static final byte TYPE_CHAR = 'C';
|
|
||||||
public static final byte TYPE_DOUBLE = 'D';
|
|
||||||
public static final byte TYPE_FLOAT = 'F';
|
|
||||||
public static final byte TYPE_INT = 'I';
|
|
||||||
public static final byte TYPE_LONG = 'J';
|
|
||||||
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 = '[';
|
|
||||||
|
|
||||||
public static final String CLASS_OBJECT = "java/lang/Object";
|
public static final String CLASS_OBJECT = "java/lang/Object";
|
||||||
private static final String CLASS_BYTE = "java/lang/Byte";
|
private static final String CLASS_BYTE = "java/lang/Byte";
|
||||||
private static final String CLASS_CHARACTER = "java/lang/Character";
|
private static final String CLASS_CHARACTER = "java/lang/Character";
|
||||||
@ -150,69 +135,6 @@ public class ClassUtil {
|
|||||||
return testClassName.substring(0, testClassName.length() - TEST_POSTFIX.length());
|
return testClassName.substring(0, testClassName.length() - TEST_POSTFIX.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* parse method desc, fetch parameter types
|
|
||||||
* @param desc method description
|
|
||||||
* @return list of parameter types
|
|
||||||
*/
|
|
||||||
public static List<Byte> getParameterTypes(String desc) {
|
|
||||||
List<Byte> parameterTypes = new ArrayList<Byte>();
|
|
||||||
boolean travelingClass = false;
|
|
||||||
boolean travelingArray = false;
|
|
||||||
for (byte b : desc.getBytes()) {
|
|
||||||
if (travelingClass) {
|
|
||||||
if (b == CLASS_END) {
|
|
||||||
travelingClass = false;
|
|
||||||
travelingArray = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isPrimaryType(b)) {
|
|
||||||
// should treat primary array as class (issue-48)
|
|
||||||
parameterTypes.add(travelingArray ? TYPE_CLASS : b);
|
|
||||||
travelingArray = false;
|
|
||||||
} else if (b == TYPE_CLASS) {
|
|
||||||
travelingClass = true;
|
|
||||||
parameterTypes.add(b);
|
|
||||||
} else if (b == TYPE_ARRAY) {
|
|
||||||
travelingArray = true;
|
|
||||||
} else if (b == PARAM_END) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parameterTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* extract parameter part of method desc
|
|
||||||
* @param desc method description
|
|
||||||
* @return parameter value
|
|
||||||
*/
|
|
||||||
public static String extractParameters(String desc) {
|
|
||||||
int returnTypeEdge = desc.lastIndexOf(PARAM_END);
|
|
||||||
return desc.substring(1, returnTypeEdge);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* parse method desc, fetch return value types
|
|
||||||
* @param desc method description
|
|
||||||
* @return types of return value
|
|
||||||
*/
|
|
||||||
public static String getReturnType(String desc) {
|
|
||||||
int returnTypeEdge = desc.lastIndexOf(PARAM_END);
|
|
||||||
return desc.substring(returnTypeEdge + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* parse method desc, fetch first parameter type
|
|
||||||
* @param desc method description
|
|
||||||
* @return types of first parameter
|
|
||||||
*/
|
|
||||||
public static String getFirstParameter(String desc) {
|
|
||||||
int typeEdge = desc.indexOf(CLASS_END);
|
|
||||||
return desc.substring(1, typeEdge + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get wrapper class of specified private type
|
* get wrapper class of specified private type
|
||||||
* @param primaryType byte code of private type
|
* @param primaryType byte code of private type
|
||||||
@ -279,15 +201,6 @@ public class ClassUtil {
|
|||||||
return (char)TYPE_CLASS + toSlashSeparatedName(className) + (char)CLASS_END;
|
return (char)TYPE_CLASS + toSlashSeparatedName(className) + (char)CLASS_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* convert byte code class name to slash separated human readable name
|
|
||||||
* @param className original name
|
|
||||||
* @return converted name
|
|
||||||
*/
|
|
||||||
public static String toSlashSeparateFullClassName(String className) {
|
|
||||||
return className.substring(1, className.length() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert byte code class name to dot separated human readable name
|
* convert byte code class name to dot separated human readable name
|
||||||
* @param className original name
|
* @param className original name
|
||||||
@ -297,25 +210,6 @@ public class ClassUtil {
|
|||||||
return toDotSeparatedName(className).substring(1, className.length() - 1);
|
return toDotSeparatedName(className).substring(1, className.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* remove first parameter from method descriptor
|
|
||||||
* @param desc original descriptor
|
|
||||||
* @return descriptor without first parameter
|
|
||||||
*/
|
|
||||||
public static String removeFirstParameter(String desc) {
|
|
||||||
return "(" + desc.substring(desc.indexOf(";") + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* add extra parameter to the beginning of method descriptor
|
|
||||||
* @param desc original descriptor
|
|
||||||
* @param type byte code class name
|
|
||||||
* @return descriptor with specified parameter at begin
|
|
||||||
*/
|
|
||||||
public static String addParameterAtBegin(String desc, String type) {
|
|
||||||
return "(" + type + desc.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read class from current context
|
* Read class from current context
|
||||||
* @param className class name
|
* @param className class name
|
||||||
@ -335,8 +229,4 @@ public class ClassUtil {
|
|||||||
return "(" + (char)type.byteValue() + ")L" + objectType + ";";
|
return "(" + (char)type.byteValue() + ")L" + objectType + ";";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isPrimaryType(byte b) {
|
|
||||||
return b == TYPE_BYTE || b == TYPE_CHAR || b == TYPE_DOUBLE || b == TYPE_FLOAT
|
|
||||||
|| b == TYPE_INT || b == TYPE_LONG || b == TYPE_SHORT || b == TYPE_BOOL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,107 @@ package com.alibaba.testable.agent.util;
|
|||||||
|
|
||||||
import org.objectweb.asm.tree.MethodNode;
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.alibaba.testable.agent.constant.ByteCodeConst.*;
|
||||||
import static org.objectweb.asm.Opcodes.ACC_STATIC;
|
import static org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||||
|
|
||||||
public class MethodUtil {
|
public class MethodUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Judge whether a method is static
|
||||||
|
* @param mn method to check
|
||||||
|
* @return is static or not
|
||||||
|
*/
|
||||||
public static boolean isStaticMethod(MethodNode mn) {
|
public static boolean isStaticMethod(MethodNode mn) {
|
||||||
return (mn.access & ACC_STATIC) != 0;
|
return (mn.access & ACC_STATIC) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse method desc, fetch parameter types
|
||||||
|
* @param desc method description
|
||||||
|
* @return list of parameter types
|
||||||
|
*/
|
||||||
|
public static List<Byte> getParameterTypes(String desc) {
|
||||||
|
List<Byte> parameterTypes = new ArrayList<Byte>();
|
||||||
|
boolean travelingClass = false;
|
||||||
|
boolean travelingArray = false;
|
||||||
|
for (byte b : desc.getBytes()) {
|
||||||
|
if (travelingClass) {
|
||||||
|
if (b == CLASS_END) {
|
||||||
|
travelingClass = false;
|
||||||
|
travelingArray = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isPrimaryType(b)) {
|
||||||
|
// should treat primary array as class (issue-48)
|
||||||
|
parameterTypes.add(travelingArray ? TYPE_CLASS : b);
|
||||||
|
travelingArray = false;
|
||||||
|
} else if (b == TYPE_CLASS) {
|
||||||
|
travelingClass = true;
|
||||||
|
parameterTypes.add(b);
|
||||||
|
} else if (b == TYPE_ARRAY) {
|
||||||
|
travelingArray = true;
|
||||||
|
} else if (b == PARAM_END) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parameterTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extract parameter part of method desc
|
||||||
|
* @param desc method description
|
||||||
|
* @return parameter value
|
||||||
|
*/
|
||||||
|
public static String extractParameters(String desc) {
|
||||||
|
int returnTypeEdge = desc.lastIndexOf(PARAM_END);
|
||||||
|
return desc.substring(1, returnTypeEdge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse method desc, fetch return value types
|
||||||
|
* @param desc method description
|
||||||
|
* @return types of return value
|
||||||
|
*/
|
||||||
|
public static String getReturnType(String desc) {
|
||||||
|
int returnTypeEdge = desc.lastIndexOf(PARAM_END);
|
||||||
|
return desc.substring(returnTypeEdge + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse method desc, fetch first parameter type
|
||||||
|
* @param desc method description
|
||||||
|
* @return types of first parameter
|
||||||
|
*/
|
||||||
|
public static String getFirstParameter(String desc) {
|
||||||
|
int typeEdge = desc.indexOf(CLASS_END);
|
||||||
|
return desc.substring(1, typeEdge + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove first parameter from method descriptor
|
||||||
|
* @param desc original descriptor
|
||||||
|
* @return descriptor without first parameter
|
||||||
|
*/
|
||||||
|
public static String removeFirstParameter(String desc) {
|
||||||
|
return "(" + desc.substring(desc.indexOf(";") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add extra parameter to the beginning of method descriptor
|
||||||
|
* @param desc original descriptor
|
||||||
|
* @param type byte code class name
|
||||||
|
* @return descriptor with specified parameter at begin
|
||||||
|
*/
|
||||||
|
public static String addParameterAtBegin(String desc, String type) {
|
||||||
|
return "(" + type + desc.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPrimaryType(byte b) {
|
||||||
|
return b == TYPE_BYTE || b == TYPE_CHAR || b == TYPE_DOUBLE || b == TYPE_FLOAT
|
||||||
|
|| b == TYPE_INT || b == TYPE_LONG || b == TYPE_SHORT || b == TYPE_BOOL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,39 +2,10 @@ package com.alibaba.testable.agent.util;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
class ClassUtilTest {
|
class ClassUtilTest {
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_able_to_get_parameter_count() {
|
|
||||||
assertEquals(0, ClassUtil.getParameterTypes("()V").size());
|
|
||||||
assertEquals(1, ClassUtil.getParameterTypes("(Ljava/lang/String;)V").size());
|
|
||||||
assertEquals(6, ClassUtil.getParameterTypes("(Ljava/lang/String;IDLjava/lang/String;ZLjava/net/URL;)V").size());
|
|
||||||
assertEquals(10, ClassUtil.getParameterTypes("(ZLjava/lang/String;IJFDCSBZ)V").size());
|
|
||||||
assertEquals(3, ClassUtil.getParameterTypes("(Ljava/lang/String;[I[Ljava/lang/String;)V").size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_able_to_extract_parameter() {
|
|
||||||
assertEquals("", ClassUtil.extractParameters("()I"));
|
|
||||||
assertEquals("Ljava/lang/String;", ClassUtil.extractParameters("(Ljava/lang/String;)I"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_able_to_get_return_type() {
|
|
||||||
assertEquals("V", ClassUtil.getReturnType("(Ljava/lang/String;)V"));
|
|
||||||
assertEquals("I", ClassUtil.getReturnType("(Ljava/lang/String;)I"));
|
|
||||||
assertEquals("[I", ClassUtil.getReturnType("(Ljava/lang/String;)[I"));
|
|
||||||
assertEquals("Ljava/lang/String;", ClassUtil.getReturnType("(Ljava/lang/String;)Ljava/lang/String;"));
|
|
||||||
assertEquals("[Ljava/lang/String;", ClassUtil.getReturnType("(Ljava/lang/String;)[Ljava/lang/String;"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_able_to_get_first_parameter() {
|
|
||||||
assertEquals("Ljava/lang/String;", ClassUtil.getFirstParameter("(Ljava/lang/String;Ljava/lang/Object;I)V"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_able_to_convert_class_name() {
|
void should_able_to_convert_class_name() {
|
||||||
assertEquals("Ljava/lang/String;", ClassUtil.toByteCodeClassName("java.lang.String"));
|
assertEquals("Ljava/lang/String;", ClassUtil.toByteCodeClassName("java.lang.String"));
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.alibaba.testable.agent.util;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class MethodUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void should_able_to_get_parameter_count() {
|
||||||
|
assertEquals(0, MethodUtil.getParameterTypes("()V").size());
|
||||||
|
assertEquals(1, MethodUtil.getParameterTypes("(Ljava/lang/String;)V").size());
|
||||||
|
assertEquals(6, MethodUtil.getParameterTypes("(Ljava/lang/String;IDLjava/lang/String;ZLjava/net/URL;)V").size());
|
||||||
|
assertEquals(10, MethodUtil.getParameterTypes("(ZLjava/lang/String;IJFDCSBZ)V").size());
|
||||||
|
assertEquals(3, MethodUtil.getParameterTypes("(Ljava/lang/String;[I[Ljava/lang/String;)V").size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void should_able_to_extract_parameter() {
|
||||||
|
assertEquals("", MethodUtil.extractParameters("()I"));
|
||||||
|
assertEquals("Ljava/lang/String;", MethodUtil.extractParameters("(Ljava/lang/String;)I"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void should_able_to_get_return_type() {
|
||||||
|
assertEquals("V", MethodUtil.getReturnType("(Ljava/lang/String;)V"));
|
||||||
|
assertEquals("I", MethodUtil.getReturnType("(Ljava/lang/String;)I"));
|
||||||
|
assertEquals("[I", MethodUtil.getReturnType("(Ljava/lang/String;)[I"));
|
||||||
|
assertEquals("Ljava/lang/String;", MethodUtil.getReturnType("(Ljava/lang/String;)Ljava/lang/String;"));
|
||||||
|
assertEquals("[Ljava/lang/String;", MethodUtil.getReturnType("(Ljava/lang/String;)[Ljava/lang/String;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void should_able_to_get_first_parameter() {
|
||||||
|
assertEquals("Ljava/lang/String;", MethodUtil.getFirstParameter("(Ljava/lang/String;Ljava/lang/Object;I)V"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user