/* * $Id: ArraysUtil.java 1011 2008-06-16 17:57:36Z amandel $ * * Copyright 2006, The jCoderZ.org Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of the jCoderZ.org Project nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS 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 REGENTS AND CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, 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. */ package org.jcoderz.commons.tracing; import org.jcoderz.commons.tracing.TracingInjector.Matcher; import java.util.Iterator; import java.util.logging.Level; import java.util.logging.Logger; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.LineNumberNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; /** * Takes care to add Tracing to the methods. * @author mandelan */ public class ClassTracingInjector { private static final int STATIC_BLOCK_LINE_NUMBER = 1; private static final String CLASSNAME = ClassTracingInjector.class.getName(); private static final Logger logger = Logger.getLogger(CLASSNAME); private final ClassNode mClassNode; private final String mClassName; private boolean mStaticLoggerInserted = false; private MethodNode mStaticInit; private final boolean mJava5; private final boolean mPai; /** * * @param cn * @param java5 * @param pai */ public ClassTracingInjector(ClassNode cn, boolean java5, boolean pai) { mPai = pai; mJava5 = java5; mClassNode = cn; mClassName = cn.name.replace('/', '.'); } /** * Injects logging to all methods that match the given matcher. * @param matcher the matcher to identify methods to inject. */ public void inject (Matcher matcher) { // No tracing for interfaces. if ((mClassNode.access & Opcodes.ACC_INTERFACE) == 0) { final Iterator i = mClassNode.methods.iterator(); while (i.hasNext()) { final MethodNode mn = (MethodNode) i.next(); if (matcher.matches(mClassNode, mn)) { if (logger.isLoggable(Level.FINEST)) { logger.finest("Will inject tracing into: " + AsmUtil.toString(mClassNode, mn)); } final MethodTracingInjector mi = new MethodTracingInjector(mn, this); mi.inject(); } else { if (logger.isLoggable(Level.FINEST)) { logger.finest("No match: " + AsmUtil.toString(mClassNode, mn)); } } if ("<clinit>".equals(mn.name) && mStaticInit == null) { mStaticInit = mn; } } if (!mStaticLoggerInserted) { MethodNode mn; if (mStaticInit == null) { if (logger.isLoggable(Level.FINEST)) { logger.finest("Creating new static initializer for class: " + AsmUtil.toString(mClassNode)); } mn = new MethodNode( Opcodes.ACC_STATIC, "<clinit>", "()V", null, null); mn.instructions = new InsnList(); final LabelNode start = new LabelNode(); mn.instructions.add(start); mn.instructions.add(new LineNumberNode(STATIC_BLOCK_LINE_NUMBER, start)); mn.instructions.add(new InsnNode(Opcodes.RETURN)); mClassNode.methods.add(mn); } else { mn = mStaticInit; if (logger.isLoggable(Level.FINEST)) { logger.finest("Using existing static initializer: " + AsmUtil.toString(mClassNode, mn)); } } final InsnList cmd = new InsnList(); injectStaticLoggerMember(cmd); // get first none label node AbstractInsnNode first = mn.instructions.getFirst(); while (first.getNext() != null && first.getType() == AbstractInsnNode.LABEL) { first = first.getNext(); } mn.instructions.insertBefore(first, cmd); mStaticLoggerInserted = true; } } else { // No tracing for interfaces. if (logger.isLoggable(Level.FINEST)) { logger.finest("No tracing in interfaces! " + AsmUtil.toString(mClassNode)); } } } public ClassNode getClassNode () { return mClassNode; } public String getClassName () { return mClassName; } /** * * @param cmd */ public void getStaticLoggerOnStack (InsnList cmd) { cmd.add(new FieldInsnNode( Opcodes.GETSTATIC, mClassNode.name, TracingInjector.STATIC_LOGGER_FIELD_NAME, "Ljava/util/logging/Logger;")); } /** * Load the class name reference to the stack. * @param cmd the instruction list where to add the command to. */ public void getClassNameOnStack(InsnList cmd) { cmd.add(new LdcInsnNode(mClassName)); } /** * Injects the static logger member and takes care for the * static initialization. * @param cmd the command list to add the code to. */ public void injectStaticLoggerMember (InsnList cmd) { if (!mStaticLoggerInserted) { final FieldNode staticLogger = new FieldNode( Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC, TracingInjector.STATIC_LOGGER_FIELD_NAME, "Ljava/util/logging/Logger;", null, null); mClassNode.fields.add(staticLogger); getClassNameOnStack(cmd); cmd.add(new MethodInsnNode( Opcodes.INVOKESTATIC, "java/util/logging/Logger", "getLogger", "(Ljava/lang/String;)Ljava/util/logging/Logger;")); cmd.add(new FieldInsnNode( Opcodes.PUTSTATIC, mClassNode.name, TracingInjector.STATIC_LOGGER_FIELD_NAME, "Ljava/util/logging/Logger;")); mStaticLoggerInserted = true; } } public boolean isJava5() { return mJava5; } public boolean isPai() { return mPai; } }