1   package org.slf4j.instrumentation;
2   
3   import javassist.CtBehavior;
4   import javassist.CtClass;
5   import javassist.CtMethod;
6   import javassist.Modifier;
7   import javassist.NotFoundException;
8   import javassist.bytecode.AttributeInfo;
9   import javassist.bytecode.CodeAttribute;
10  import javassist.bytecode.LocalVariableAttribute;
11  
12  /**
13   * Helper methods for Javassist functionality.
14   * 
15   */
16  public class JavassistHelper {
17  
18    /**
19     * Create a javaassist source snippet which either is empty (for anything
20     * which does not return a value) or a explanatory text around the $_
21     * javaassist return value variable.
22     * 
23     * @param method
24     *          descriptor of method
25     * @return source snippet
26     * @throws NotFoundException
27     */
28    public static String returnValue(CtBehavior method) throws NotFoundException {
29  
30      String returnValue = "";
31      if (methodReturnsValue(method)) {
32        returnValue = " returns: \" + $_ + \".";
33      }
34      return returnValue;
35    }
36  
37    /**
38     * determine if the given method returns a value, and return true if so. false
39     * otherwise.
40     * 
41     * @param method
42     * @return
43     * @throws NotFoundException
44     */
45    private static boolean methodReturnsValue(CtBehavior method)
46        throws NotFoundException {
47  
48      if (method instanceof CtMethod == false) {
49        return false;
50      }
51  
52      CtClass returnType = ((CtMethod) method).getReturnType();
53      String returnTypeName = returnType.getName();
54  
55      boolean isVoidMethod = "void".equals(returnTypeName);
56  
57      boolean methodReturnsValue = isVoidMethod == false;
58      return methodReturnsValue;
59    }
60  
61    /**
62     * Return javaassist source snippet which lists all the parameters and their
63     * values. If available the source names are extracted from the debug
64     * information and used, otherwise just a number is shown.
65     * 
66     * @param method
67     * @return
68     * @throws NotFoundException
69     */
70    public static String getSignature(CtBehavior method) throws NotFoundException {
71  
72      CtClass parameterTypes[] = method.getParameterTypes();
73  
74      CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute();
75  
76      LocalVariableAttribute locals = null;
77  
78      if (codeAttribute != null) {
79        AttributeInfo attribute;
80        attribute = codeAttribute.getAttribute("LocalVariableTable");
81        locals = (LocalVariableAttribute) attribute;
82      }
83  
84      String methodName = method.getName();
85  
86      StringBuffer sb = new StringBuffer(methodName + "(\" ");
87      for (int i = 0; i < parameterTypes.length; i++) {
88        if (i > 0) {
89          // add a comma and a space between printed values
90          sb.append(" + \", \" ");
91        }
92  
93        CtClass parameterType = parameterTypes[i];
94        boolean isArray = parameterType.isArray();
95        CtClass arrayType = parameterType.getComponentType();
96        if (isArray) {
97          while (arrayType.isArray()) {
98            arrayType = arrayType.getComponentType();
99          }
100       }
101 
102       sb.append(" + \"");
103       sb.append(parameterNameFor(method, locals, i));
104       sb.append("\" + \"=");
105 
106       // use Arrays.asList() to render array of objects.
107       if (isArray && !arrayType.isPrimitive()) {
108         sb.append("\"+ java.util.Arrays.asList($" + (i + 1) + ")");
109       } else {
110         sb.append("\"+ $" + (i + 1));
111       }
112     }
113     sb.append("+\")");
114 
115     String signature = sb.toString();
116     return signature;
117   }
118 
119   /**
120    * Determine the name of parameter with index i in the given method. Use the
121    * locals attributes about local variables from the classfile. Note: This is
122    * still work in progress.
123    * 
124    * @param method
125    * @param locals
126    * @param i
127    * @return the name of the parameter if available or a number if not.
128    */
129   static String parameterNameFor(CtBehavior method,
130       LocalVariableAttribute locals, int i) {
131 
132     if (locals == null) {
133       return Integer.toString(i + 1);
134     }
135 
136     int modifiers = method.getModifiers();
137 
138     int j = i;
139 
140     if (Modifier.isSynchronized(modifiers)) {
141       // skip object to synchronize upon.
142       j++;
143       // System.err.println("Synchronized");
144     }
145     if (Modifier.isStatic(modifiers) == false) {
146       // skip "this"
147       j++;
148       // System.err.println("Instance");
149     }
150     String variableName = locals.variableName(j);
151 //    if (variableName.equals("this")) {
152 //      System.err.println("'this' returned as a parameter name for "
153 //          + method.getName() + " index " + j
154 //          + ", names are probably shifted. Please submit source for class in slf4j bugreport");
155 //    }
156     return variableName;
157   }
158 }