/*
* ============================================================================
* The Apache Software License, Version 1.1
* ============================================================================
*
* Copyright (C) 2000-2003 Lucas Bruand. All
* rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The end-user documentation included with the redistribution, if any, must
* include the following acknowledgment: "This product includes software
* developed by the Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself, if
* and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Just4Log" and "Apache Software Foundation" must not be used to
* endorse or promote products derived from this software without prior
* written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache", nor may
* "Apache" appear in their name, without prior written permission of the
* Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
* DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Apache Software Foundation. For more information on the
* Apache Software Foundation, please see <http://www.apache.org/>.
*
*/
package net.sf.just4log.transform;
import java.util.Iterator;
import java.util.LinkedList;
import net.sf.just4log.NoMatchingInvokeInterfaceInstruction;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.ReturnInstruction;
import org.apache.bcel.generic.Type;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Lucas Bruand
*/
public abstract class Transform {
private static Log logger = LogFactory.getLog(Transform.class);
private static String CLASSID =
"$Id: Transform.java,v 1.15 2003/09/23 21:21:15 lbruand Exp $";
static {
ApacheCommonTransform.register();
Log4jTransform.register();
LogJDK14Transform.register();
}
InstructionFactory instFact;
private String classname;
/**
*
*/
public Transform() {
super();
}
private static LinkedList transmap;
public static void register(Transform trans) {
if (transmap == null) {
transmap = new LinkedList();
}
synchronized (transmap) {
transmap.add(trans);
}
}
public static Transform getTransformationForJavaClass(
JavaClass claz,
ConstantPoolGen cp) {
Field[] fields = claz.getFields();
ObjectType fieldType;
Transform t;
for (int i = 0; i < fields.length; i++) {
try {
fieldType = (ObjectType) fields[i].getType();
} catch (ClassCastException ex) {
continue;
}
for (Iterator j = transmap.iterator(); j.hasNext();) {
t = (Transform) j.next();
if (fieldType.equals(t.getLogType())
|| fieldType.isCastableTo(t.getLogType())) {
logger.info(
"Found logger attribute: "
+ fieldType
+ " "
+ fields[i].getName());
t.init(claz, fields[i], cp);
return t;
}
}
}
return null;
}
JavaClass clazz;
Field loggerAttribute;
public void init(JavaClass clazz, Field logger, ConstantPoolGen cp) {
this.clazz = clazz;
this.loggerAttribute = logger;
this.instFact = new InstructionFactory(cp);
}
public void done() {
this.loggerAttribute = null;
this.instFact = null;
}
public abstract InstructionHandle insertFork(
InstructionList il,
InstructionHandle getStaticHandle,
InstructionHandle invokeHandle,
ConstantPoolGen cp);
public abstract InstructionHandle insertEnter(
MethodGen orig,
InstructionList il,
InstructionHandle firstInstructionHandle,
ConstantPoolGen cp);
public abstract InstructionHandle insertExit(
MethodGen orig,
InstructionList il,
InstructionHandle returnInstructionHandle,
ConstantPoolGen cp);
public void transform(
MethodGen mg,
InstructionList il,
InstructionHandle handle,
ConstantPoolGen cp)
throws NoMatchingInvokeInterfaceInstruction {
logger.info("Found a logger instruction.");
InstructionHandle lastLogHandle = handle;
InvokeInstruction invokeInterface = null;
Instruction inst = null;
while (true) {
handle = handle.getNext();
if (handle == null) {
logger.warn("No matching invokeInterface found.");
throw new NoMatchingInvokeInterfaceInstruction("No matching invokeInterface found.");
}
inst = handle.getInstruction();
if (inst instanceof InvokeInstruction) {
invokeInterface = (InvokeInstruction) inst;
if (invokeInterface.getClassType(cp).equals(getLogType())) {
logger.info("Found matching invokeInstruction.");
if (level_logs == FORKLOGS) {
InstructionHandle insertHandle =
insertFork(il, lastLogHandle, handle, cp);
if (insertHandle != null) {
il.redirectBranches(lastLogHandle, insertHandle);
il.redirectExceptionHandlers(
mg.getExceptionHandlers(),
lastLogHandle,
insertHandle);
il.redirectLocalVariables(
mg.getLocalVariables(),
lastLogHandle,
insertHandle);
}
}
break;
}
}
}
}
/**
* This optimizes a method in particular.
* @param orig The original method to optimize.
* @param mg The MethodGen that is going to be used as a basis for generation.
* @param cp The ConstantPool that is going to be used as a basis for generation. This is going to be modified.
* @return the new optimized method.
* @throws NoMatchingInvokeInterfaceInstruction
*/
public Method speedup(Method orig, MethodGen mg, ConstantPoolGen cp)
throws NoMatchingInvokeInterfaceInstruction {
logger.info("Entry speedup(Method " + mg.getName() + ")");
if (orig.getName().equals("<clinit>")
|| orig.getName().equals("<init>")
|| orig.getName().equals("class$")) {
logger.info(
"This method "
+ mg.getName()
+ " is a special method and won't be modified.");
return orig;
}
InstructionList il = null;
InstructionHandle handle = null;
Instruction inst = null;
GETSTATIC getStatic = null;
Method m = null;
try {
il = mg.getInstructionList();
handle = il.getStart();
if (level_enters == SIMPLEENTER) {
insertEnter(mg, il, handle, cp);
insertFork(il, il.getStart(), handle.getPrev(), cp);
}
for (; handle != null; handle = handle.getNext()) {
inst = handle.getInstruction();
logger.info("next instruction: " + inst);
if (level_exits == SIMPLEEXIT
&& inst instanceof ReturnInstruction) {
InstructionHandle lastLogHandle = handle;
logger.info(
"Found a ReturnInstruction of type: "
+ inst.toString());
int stackSize =
inst.consumeStack(cp) - inst.produceStack(cp);
while (stackSize != 0) {
handle = handle.getPrev();
inst = handle.getInstruction();
logger.info("prev instruction: " + inst);
stackSize += inst.consumeStack(cp)
- inst.produceStack(cp);
}
InstructionHandle insertHandle =
insertExit(mg, il, handle, cp);
if (insertHandle != null) {
// We need to redirect branches etc.... twice
// so that the if fork doesn't get erroneous modified.
il.redirectBranches(handle, insertHandle);
il.redirectExceptionHandlers(
mg.getExceptionHandlers(),
handle,
insertHandle);
il.redirectLocalVariables(
mg.getLocalVariables(),
handle,
insertHandle);
handle =
insertFork(il, insertHandle, handle.getPrev(), cp);
il.redirectBranches(insertHandle, handle );
il.redirectExceptionHandlers(
mg.getExceptionHandlers(),
insertHandle,
handle);
il.redirectLocalVariables(
mg.getLocalVariables(),
insertHandle,
handle);
}
handle = lastLogHandle;
}
if (inst instanceof GETSTATIC) {
getStatic = (GETSTATIC) inst;
Type type = getStatic.getFieldType(cp);
logger.info(
"Found a GETSTATIC instruction of type: "
+ type.toString());
if (getStatic
.getFieldName(cp)
.equals(loggerAttribute.getName())) {
logger.info("This is a logger access");
transform(mg, il, handle, cp);
}
}
}
if (orig.getLineNumberTable() == null) {
logger.info("Removing line numbers");
mg.removeLineNumbers();
}
if (orig.getLocalVariableTable() == null) {
logger.info("Removing Local variables table");
mg.removeLocalVariables();
}
m = mg.getMethod();
} finally {
il.dispose();
}
return m;
}
/**
* This method transforms a not optimized JavaClass into a new Log-optimised
* JavaClass
* @param source the JavaClass to optimize.
* @return the optimized JavaClass
*/
public static JavaClass speedup(JavaClass source) {
try {
logger.info("Entry speedup(JavaClass)");
ClassGen cg = new ClassGen(source);
Method[] methods = cg.getMethods();
ConstantPoolGen cp = cg.getConstantPool();
Transform trans = getTransformationForJavaClass(source, cp);
if (trans == null) {
logger.warn(
"No transformer found for class " + source.getClassName());
return source;
}
for (int i = 0; i < methods.length; i++) {
if (!(methods[i].isAbstract() || methods[i].isNative())) {
MethodGen mg =
new MethodGen(methods[i], source.getClassName(), cp);
Method stripped = trans.speedup(methods[i], mg, cp);
if (stripped != null)
cg.replaceMethod(methods[i], stripped);
}
}
trans.done();
JavaClass target = cg.getJavaClass();
target.setFileName(source.getFileName());
return target;
} catch (Exception e) {
e.printStackTrace();
}
return source;
}
public static String getMethodRepr(MethodGen mg) {
StringBuffer sb =
new StringBuffer(mg.getReturnType() + " " + mg.getName() + "(");
Type[] types = mg.getArgumentTypes();
for (int i = 0; i < types.length; i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(types[i].toString());
}
sb.append(")");
logger.info("Method representation to enter: " + sb.toString());
return sb.toString();
}
/**
* @return
*/
public abstract ObjectType getLogType();
public static final int NOEXIT = 0;
public static final int SIMPLEEXIT = 1;
public static final int PARAMETEREXIT = 2;
public static int level_exits = NOEXIT;
public static final int REMOVELOGS = 0;
public static final int LEAVELOGS = 1;
public static final int FORKLOGS = 2;
public static int level_logs = FORKLOGS;
public static final int NOENTER = 0;
public static final int SIMPLEENTER = 1;
public static final int PARAMETERENTER = 2;
public static int level_enters = NOENTER;
public static final String ENTER_STRING = "enters: ";
public static final String EXIT_STRING = "exits: ";
}