1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertNull;
21
22 import java.lang.reflect.InvocationTargetException;
23 import java.util.LinkedList;
24 import java.util.List;
25
26 import org.apache.bcel.classfile.JavaClass;
27 import org.apache.bcel.classfile.LocalVariableTypeTable;
28 import org.apache.bcel.classfile.Method;
29 import org.apache.bcel.generic.ACONST_NULL;
30 import org.apache.bcel.generic.ALOAD;
31 import org.apache.bcel.generic.ConstantPoolGen;
32 import org.apache.bcel.generic.GETSTATIC;
33 import org.apache.bcel.generic.INVOKEVIRTUAL;
34 import org.apache.bcel.generic.Instruction;
35 import org.apache.bcel.generic.InstructionList;
36 import org.apache.bcel.generic.LocalVariableGen;
37 import org.apache.bcel.generic.MethodGen;
38 import org.apache.bcel.generic.Type;
39 import org.junit.jupiter.api.Test;
40
41 public class LocalVariableTypeTableTestCase extends AbstractTestCase {
42
43 public class TestClassLoader extends ClassLoader {
44
45 public TestClassLoader(final ClassLoader parent) {
46 super(parent);
47 }
48
49 public Class<?> findClass(final String name, final byte[] bytes) {
50 return defineClass(name, bytes, 0, bytes.length);
51 }
52 }
53
54 public InstructionList createPrintln(final ConstantPoolGen cp, final Instruction instruction) {
55 final InstructionList il = new InstructionList();
56
57 final int out = cp.addFieldref("java.lang.System", "out", "Ljava/io/PrintStream;");
58 final int println = cp.addMethodref("java.io.PrintStream", "println", "(Ljava/lang/String;)V");
59 il.append(new GETSTATIC(out));
60 il.append(instruction);
61 il.append(new INVOKEVIRTUAL(println));
62
63 return il;
64 }
65
66 public int findFirstStringLocalVariableOffset(final Method method) {
67 final Type[] argumentTypes = method.getArgumentTypes();
68 int offset = -1;
69
70 for (int i = 0, count = argumentTypes.length; i < count; i++) {
71 if (Type.STRING.getSignature().equals(argumentTypes[i].getSignature())) {
72 if (method.isStatic()) {
73 offset = i;
74 } else {
75 offset = i + 1;
76 }
77
78 break;
79 }
80 }
81
82 return offset;
83 }
84
85 private byte[] getBytesFromClass(final String className) throws ClassNotFoundException {
86 final JavaClass clazz = getTestJavaClass(className);
87 final ConstantPoolGen cp = new ConstantPoolGen(clazz.getConstantPool());
88
89 final Method[] methods = clazz.getMethods();
90
91 for (int i = 0; i < methods.length; i++) {
92 final Method method = methods[i];
93 if (!method.isNative() && !method.isAbstract()) {
94 methods[i] = injection(clazz, method, cp, findFirstStringLocalVariableOffset(method));
95 }
96 }
97
98 clazz.setConstantPool(cp.getFinalConstantPool());
99
100 return clazz.getBytes();
101 }
102
103 public Method injection(final JavaClass clazz, Method method, final ConstantPoolGen cp, final int firstStringOffset) {
104 final MethodGen methodGen = new MethodGen(method, clazz.getClassName(), cp);
105
106 final InstructionList instructionList = methodGen.getInstructionList();
107 instructionList.insert(instructionList.getStart(), makeWillBeAddedInstructionList(methodGen, firstStringOffset));
108
109 methodGen.setMaxStack();
110 methodGen.setMaxLocals();
111
112 method = methodGen.getMethod();
113 instructionList.dispose();
114
115 return method;
116 }
117
118 public InstructionList makeWillBeAddedInstructionList(final MethodGen methodGen, final int firstStringOffset) {
119 if (firstStringOffset == -1) {
120 return new InstructionList();
121 }
122
123 final LocalVariableGen localVariableGen = methodGen.getLocalVariables()[firstStringOffset];
124 Instruction instruction;
125
126 if (localVariableGen != null) {
127 instruction = new ALOAD(localVariableGen.getIndex());
128 } else {
129 instruction = new ACONST_NULL();
130 }
131
132 return createPrintln(methodGen.getConstantPool(), instruction);
133 }
134
135 @Test
136 public void testGetLocalVariableTypeTable() throws ClassNotFoundException, NoSuchMethodException, SecurityException {
137 final JavaClass testJavaClass = getTestJavaClass("org/apache/commons/lang3/function/TriFunction");
138 final String expectedToString = "LocalVariableTypes(startPc = 0, length = 17, index = 0:org.apache.commons.lang3.function.TriFunction<T, U, V, R> this)";
139 for (final Method method : testJavaClass.getMethods()) {
140 if ("lambda$andThen$0".equals(method.getName())) {
141 final LocalVariableTypeTable localVariableTypeTable = method.getLocalVariableTypeTable();
142 assertEquals(expectedToString, localVariableTypeTable.toString());
143 }
144 if ("apply".equals(method.getName())) {
145 assertNull(method.getLocalVariableTypeTable());
146 }
147 }
148 assertNull(Repository.lookupClass(Object.class).getMethod(Object.class.getMethod("toString")).getLocalVariableTypeTable());
149 }
150
151 @Test
152 public void testWithGenericArguement() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
153 final String targetClass = PACKAGE_BASE_NAME + ".data.SimpleClassHasMethodIncludeGenericArgument";
154 final TestClassLoader loader = new TestClassLoader(getClass().getClassLoader());
155 final Class<?> cls = loader.findClass(targetClass, getBytesFromClass(targetClass));
156
157 java.lang.reflect.Method method = cls.getDeclaredMethod("a", String.class, List.class);
158 method.invoke(null, "a1", new LinkedList<>());
159 method = cls.getDeclaredMethod("b", String.class, List.class);
160 method.invoke(null, "b1", new LinkedList<>());
161 method = cls.getDeclaredMethod("c", String.class, String.class);
162 method.invoke(null, "c1", "c2");
163 method = cls.getDeclaredMethod("d", List.class, String.class);
164 method.invoke(null, new LinkedList<>(), "d2");
165 }
166 }