Home | History | Annotate | Download | only in instrumentation
      1 /**
      2  * Copyright (c) 2004-2011 QOS.ch
      3  * All rights reserved.
      4  *
      5  * Permission is hereby granted, free  of charge, to any person obtaining
      6  * a  copy  of this  software  and  associated  documentation files  (the
      7  * "Software"), to  deal in  the Software without  restriction, including
      8  * without limitation  the rights to  use, copy, modify,  merge, publish,
      9  * distribute,  sublicense, and/or sell  copies of  the Software,  and to
     10  * permit persons to whom the Software  is furnished to do so, subject to
     11  * the following conditions:
     12  *
     13  * The  above  copyright  notice  and  this permission  notice  shall  be
     14  * included in all copies or substantial portions of the Software.
     15  *
     16  * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
     17  * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
     18  * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
     19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21  * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
     22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  */
     25 package org.slf4j.instrumentation;
     26 
     27 import javassist.CtBehavior;
     28 import javassist.CtClass;
     29 import javassist.CtMethod;
     30 import javassist.Modifier;
     31 import javassist.NotFoundException;
     32 import javassist.bytecode.AttributeInfo;
     33 import javassist.bytecode.CodeAttribute;
     34 import javassist.bytecode.LocalVariableAttribute;
     35 
     36 /**
     37  * Helper methods for Javassist functionality.
     38  *
     39  */
     40 public class JavassistHelper {
     41 
     42     /**
     43      * Create a javaassist source snippet which either is empty (for anything
     44      * which does not return a value) or a explanatory text around the $_
     45      * javaassist return value variable.
     46      *
     47      * @param method
     48      *            descriptor of method
     49      * @return source snippet
     50      * @throws NotFoundException
     51      */
     52     public static String returnValue(CtBehavior method) throws NotFoundException {
     53 
     54         String returnValue = "";
     55         if (methodReturnsValue(method)) {
     56             returnValue = " returns: \" + $_ + \".";
     57         }
     58         return returnValue;
     59     }
     60 
     61     /**
     62      * determine if the given method returns a value, and return true if so.
     63      * false otherwise.
     64      *
     65      * @param method
     66      * @return
     67      * @throws NotFoundException
     68      */
     69     private static boolean methodReturnsValue(CtBehavior method) throws NotFoundException {
     70 
     71         if (method instanceof CtMethod == false) {
     72             return false;
     73         }
     74 
     75         CtClass returnType = ((CtMethod) method).getReturnType();
     76         String returnTypeName = returnType.getName();
     77 
     78         boolean isVoidMethod = "void".equals(returnTypeName);
     79 
     80         boolean methodReturnsValue = isVoidMethod == false;
     81         return methodReturnsValue;
     82     }
     83 
     84     /**
     85      * Return javaassist source snippet which lists all the parameters and their
     86      * values. If available the source names are extracted from the debug
     87      * information and used, otherwise just a number is shown.
     88      *
     89      * @param method
     90      * @return
     91      * @throws NotFoundException
     92      */
     93     public static String getSignature(CtBehavior method) throws NotFoundException {
     94 
     95         CtClass parameterTypes[] = method.getParameterTypes();
     96 
     97         CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute();
     98 
     99         LocalVariableAttribute locals = null;
    100 
    101         if (codeAttribute != null) {
    102             AttributeInfo attribute;
    103             attribute = codeAttribute.getAttribute("LocalVariableTable");
    104             locals = (LocalVariableAttribute) attribute;
    105         }
    106 
    107         String methodName = method.getName();
    108 
    109         StringBuilder sb = new StringBuilder(methodName).append("(\" ");
    110         for (int i = 0; i < parameterTypes.length; i++) {
    111             if (i > 0) {
    112                 // add a comma and a space between printed values
    113                 sb.append(" + \", \" ");
    114             }
    115 
    116             CtClass parameterType = parameterTypes[i];
    117             boolean isArray = parameterType.isArray();
    118             CtClass arrayType = parameterType.getComponentType();
    119             if (isArray) {
    120                 while (arrayType.isArray()) {
    121                     arrayType = arrayType.getComponentType();
    122                 }
    123             }
    124 
    125             sb.append(" + \"");
    126             try {
    127                 sb.append(parameterNameFor(method, locals, i));
    128             } catch (Exception e) {
    129                 sb.append(i + 1);
    130             }
    131             sb.append("\" + \"=");
    132 
    133             if (parameterType.isPrimitive()) {
    134                 // let the compiler handle primitive -> string
    135                 sb.append("\"+ $").append(i + 1);
    136             } else {
    137                 String s = "org.slf4j.instrumentation.ToStringHelper.render";
    138                 sb.append("\"+ ").append(s).append("($").append(i + 1).append(')');
    139             }
    140         }
    141         sb.append("+\")");
    142 
    143         String signature = sb.toString();
    144         return signature;
    145     }
    146 
    147     /**
    148      * Determine the name of parameter with index i in the given method. Use the
    149      * locals attributes about local variables from the classfile. Note: This is
    150      * still work in progress.
    151      *
    152      * @param method
    153      * @param locals
    154      * @param i
    155      * @return the name of the parameter if available or a number if not.
    156      */
    157     static String parameterNameFor(CtBehavior method, LocalVariableAttribute locals, int i) {
    158 
    159         if (locals == null) {
    160             return Integer.toString(i + 1);
    161         }
    162 
    163         int modifiers = method.getModifiers();
    164 
    165         int j = i;
    166 
    167         if (Modifier.isSynchronized(modifiers)) {
    168             // skip object to synchronize upon.
    169             j++;
    170             // System.err.println("Synchronized");
    171         }
    172         if (Modifier.isStatic(modifiers) == false) {
    173             // skip "this"
    174             j++;
    175             // System.err.println("Instance");
    176         }
    177         String variableName = locals.variableName(j);
    178         // if (variableName.equals("this")) {
    179         // System.err.println("'this' returned as a parameter name for "
    180         // + method.getName() + " index " + j
    181         // +
    182         // ", names are probably shifted. Please submit source for class in slf4j bugreport");
    183         // }
    184         return variableName;
    185     }
    186 }
    187