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