blob: 2e45c1f1c75c79d1e10acea223eb64a5a56b5e18 [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
*
* https://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.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.
*/
public final class Helloify {
private static String className;
private static ConstantPoolGen cp;
private static int out; // reference to System.out
private static int println; // reference to PrintStream.println
/**
* Change class name to <old_name>_hello
*/
private static void helloifyClassName(final JavaClass javaClass) {
className = javaClass.getClassName() + "_hello";
int index = javaClass.getClassNameIndex();
index = ((ConstantClass) cp.getConstant(index)).getNameIndex();
cp.setConstant(index, new ConstantUtf8(Utility.packageToPath(className)));
}
/**
* Patch a method.
*/
private static Method helloifyMethod(Method m) {
final Code code = m.getCode();
final int flags = m.getAccessFlags();
final String name = m.getName();
// Sanity check
if (m.isNative() || m.isAbstract() || code == null) {
return m;
}
// Create instruction list to be inserted at method start.
final String mesg = "Hello from " + Utility.methodSignatureToString(m.getSignature(), name, Utility.accessToString(flags));
final InstructionList patch = new InstructionList();
patch.append(new GETSTATIC(out));
patch.append(new PUSH(cp, mesg));
patch.append(new INVOKEVIRTUAL(println));
final MethodGen mg = new MethodGen(m, className, cp);
final InstructionList il = mg.getInstructionList();
final 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;
}
public static void main(final String[] argv) throws Exception {
for (final String arg : argv) {
if (arg.endsWith(JavaClass.EXTENSION)) {
final JavaClass javaClass = new ClassParser(arg).parse();
final ConstantPool constants = javaClass.getConstantPool();
final String fileName = arg.substring(0, arg.length() - 6) + "_hello.class";
cp = new ConstantPoolGen(constants);
helloifyClassName(javaClass);
out = cp.addFieldref("java.lang.System", "out", "Ljava/io/PrintStream;");
println = cp.addMethodref("java.io.PrintStream", "println", "(Ljava/lang/String;)V");
// Patch all methods.
final Method[] methods = javaClass.getMethods();
for (int j = 0; j < methods.length; j++) {
methods[j] = helloifyMethod(methods[j]);
}
// Finally dump it back to a file.
javaClass.setConstantPool(cp.getFinalConstantPool());
javaClass.dump(fileName);
}
}
}
}