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  */
18 
19 import org.apache.bcel.Constants;
20 import org.apache.bcel.classfile.ClassParser;
21 import org.apache.bcel.classfile.Code;
22 import org.apache.bcel.classfile.ConstantClass;
23 import org.apache.bcel.classfile.ConstantPool;
24 import org.apache.bcel.classfile.ConstantUtf8;
25 import org.apache.bcel.classfile.JavaClass;
26 import org.apache.bcel.classfile.Method;
27 import org.apache.bcel.classfile.Utility;
28 import org.apache.bcel.generic.ConstantPoolGen;
29 import org.apache.bcel.generic.GETSTATIC;
30 import org.apache.bcel.generic.INVOKESPECIAL;
31 import org.apache.bcel.generic.INVOKEVIRTUAL;
32 import org.apache.bcel.generic.InstructionHandle;
33 import org.apache.bcel.generic.InstructionList;
34 import org.apache.bcel.generic.MethodGen;
35 import org.apache.bcel.generic.PUSH;
36 
37 /**
38  * Read class file(s) and patch all of its methods, so that they print
39  * "hello" and their name and signature before doing anything else.
40  *
41  * @version $Id$
42  */
43 public final class helloify implements Constants {
44 
45     private static String class_name;
46     private static ConstantPoolGen cp;
47     private static int out;     // reference to System.out
48     private static int println; // reference to PrintStream.println
49 
main(String[] argv)50     public static void main(String[] argv) throws Exception {
51         for (String arg : argv) {
52             if (arg.endsWith(".class")) {
53                 JavaClass java_class = new ClassParser(arg).parse();
54                 ConstantPool constants = java_class.getConstantPool();
55                 String file_name = arg.substring(0, arg.length() - 6) + "_hello.class";
56                 cp = new ConstantPoolGen(constants);
57 
58                 helloifyClassName(java_class);
59 
60                 out = cp.addFieldref("java.lang.System", "out", "Ljava/io/PrintStream;");
61                 println = cp.addMethodref("java.io.PrintStream", "println", "(Ljava/lang/String;)V");
62                 // Patch all methods.
63                 Method[] methods = java_class.getMethods();
64 
65                 for (int j = 0; j < methods.length; j++) {
66                     methods[j] = helloifyMethod(methods[j]);
67                 }
68 
69                 // Finally dump it back to a file.
70                 java_class.setConstantPool(cp.getFinalConstantPool());
71                 java_class.dump(file_name);
72             }
73         }
74     }
75 
76     /**
77      * Change class name to <old_name>_hello
78      */
helloifyClassName(JavaClass java_class)79     private static void helloifyClassName(JavaClass java_class) {
80         class_name = java_class.getClassName() + "_hello";
81         int index = java_class.getClassNameIndex();
82 
83         index = ((ConstantClass) cp.getConstant(index)).getNameIndex();
84         cp.setConstant(index, new ConstantUtf8(class_name.replace('.', '/')));
85     }
86 
87     /**
88      * Patch a method.
89      */
helloifyMethod(Method m)90     private static Method helloifyMethod(Method m) {
91         Code code = m.getCode();
92         int flags = m.getAccessFlags();
93         String name = m.getName();
94 
95         // Sanity check
96         if (m.isNative() || m.isAbstract() || (code == null)) {
97             return m;
98         }
99 
100         // Create instruction list to be inserted at method start.
101         String mesg = "Hello from " + Utility.methodSignatureToString(m.getSignature(),
102                 name,
103                 Utility.accessToString(flags));
104         InstructionList patch = new InstructionList();
105         patch.append(new GETSTATIC(out));
106         patch.append(new PUSH(cp, mesg));
107         patch.append(new INVOKEVIRTUAL(println));
108 
109         MethodGen mg = new MethodGen(m, class_name, cp);
110         InstructionList il = mg.getInstructionList();
111         InstructionHandle[] ihs = il.getInstructionHandles();
112 
113         if (name.equals("<init>")) { // First let the super or other constructor be called
114             for (int j = 1; j < ihs.length; j++) {
115                 if (ihs[j].getInstruction() instanceof INVOKESPECIAL) {
116                     il.append(ihs[j], patch); // Should check: method name == "<init>"
117                     break;
118                 }
119             }
120         } else {
121             il.insert(ihs[0], patch);
122         }
123 
124         // Stack size must be at least 2, since the println method takes 2 argument.
125         if (code.getMaxStack() < 2) {
126             mg.setMaxStack(2);
127         }
128 
129         m = mg.getMethod();
130 
131         il.dispose(); // Reuse instruction handles
132 
133         return m;
134     }
135 }
136