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