blob: d53be25214b38cd1860a73945416a4566f7c51a7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.Utility;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.INVOKEVIRTUAL;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.PUSH;
/**
* Read class file(s) and patch all of its methods, so that they print
* "hello" and their name and signature before doing anything else.
*
* @version $Id$
*/
public final class helloify implements Constants {
private static String class_name;
private static ConstantPoolGen cp;
private static int out; // reference to System.out
private static int println; // reference to PrintStream.println
public static void main(String[] argv) throws Exception {
for (String arg : argv) {
if (arg.endsWith(".class")) {
JavaClass java_class = new ClassParser(arg).parse();
ConstantPool constants = java_class.getConstantPool();
String file_name = arg.substring(0, arg.length() - 6) + "_hello.class";
cp = new ConstantPoolGen(constants);
helloifyClassName(java_class);
out = cp.addFieldref("java.lang.System", "out", "Ljava/io/PrintStream;");
println = cp.addMethodref("java.io.PrintStream", "println", "(Ljava/lang/String;)V");
// Patch all methods.
Method[] methods = java_class.getMethods();
for (int j = 0; j < methods.length; j++) {
methods[j] = helloifyMethod(methods[j]);
}
// Finally dump it back to a file.
java_class.setConstantPool(cp.getFinalConstantPool());
java_class.dump(file_name);
}
}
}
/**
* Change class name to <old_name>_hello
*/
private static void helloifyClassName(JavaClass java_class) {
class_name = java_class.getClassName() + "_hello";
int index = java_class.getClassNameIndex();
index = ((ConstantClass) cp.getConstant(index)).getNameIndex();
cp.setConstant(index, new ConstantUtf8(class_name.replace('.', '/')));
}
/**
* Patch a method.
*/
private static Method helloifyMethod(Method m) {
Code code = m.getCode();
int flags = m.getAccessFlags();
String name = m.getName();
// Sanity check
if (m.isNative() || m.isAbstract() || (code == null)) {
return m;
}
// Create instruction list to be inserted at method start.
String mesg = "Hello from " + Utility.methodSignatureToString(m.getSignature(),
name,
Utility.accessToString(flags));
InstructionList patch = new InstructionList();
patch.append(new GETSTATIC(out));
patch.append(new PUSH(cp, mesg));
patch.append(new INVOKEVIRTUAL(println));
MethodGen mg = new MethodGen(m, class_name, cp);
InstructionList il = mg.getInstructionList();
InstructionHandle[] ihs = il.getInstructionHandles();
if (name.equals("<init>")) { // First let the super or other constructor be called
for (int j = 1; j < ihs.length; j++) {
if (ihs[j].getInstruction() instanceof INVOKESPECIAL) {
il.append(ihs[j], patch); // Should check: method name == "<init>"
break;
}
}
} else {
il.insert(ihs[0], patch);
}
// Stack size must be at least 2, since the println method takes 2 argument.
if (code.getMaxStack() < 2) {
mg.setMaxStack(2);
}
m = mg.getMethod();
il.dispose(); // Reuse instruction handles
return m;
}
}