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)
29  			throws NotFoundException {
30  
31  		String returnValue = "";
32  		if (methodReturnsValue(method)) {
33  			returnValue = " returns: \" + $_ + \".";
34  		}
35  		return returnValue;
36  	}
37  
38  	/**
39  	 * determine if the given method returns a value, and return true if so.
40  	 * false otherwise.
41  	 * 
42  	 * @param method
43  	 * @return
44  	 * @throws NotFoundException
45  	 */
46  	private static boolean methodReturnsValue(CtBehavior method)
47  			throws NotFoundException {
48  
49  		if (method instanceof CtMethod == false) {
50  			return false;
51  		}
52  
53  		CtClass returnType = ((CtMethod) method).getReturnType();
54  		String returnTypeName = returnType.getName();
55  
56  		boolean isVoidMethod = "void".equals(returnTypeName);
57  
58  		boolean methodReturnsValue = isVoidMethod == false;
59  		return methodReturnsValue;
60  	}
61  
62  	/**
63  	 * Return javaassist source snippet which lists all the parameters and their
64  	 * values. If available the source names are extracted from the debug
65  	 * information and used, otherwise just a number is shown.
66  	 * 
67  	 * @param method
68  	 * @return
69  	 * @throws NotFoundException
70  	 */
71  	public static String getSignature(CtBehavior method)
72  			throws NotFoundException {
73  
74  		CtClass parameterTypes[] = method.getParameterTypes();
75  
76  		CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute();
77  
78  		LocalVariableAttribute locals = null;
79  
80  		if (codeAttribute != null) {
81  			AttributeInfo attribute;
82  			attribute = codeAttribute.getAttribute("LocalVariableTable");
83  			locals = (LocalVariableAttribute) attribute;
84  		}
85  
86  		String methodName = method.getName();
87  
88  		StringBuffer sb = new StringBuffer(methodName + "(\" ");
89  		for (int i = 0; i < parameterTypes.length; i++) {
90  			if (i > 0) {
91  				// add a comma and a space between printed values
92  				sb.append(" + \", \" ");
93  			}
94  
95  			CtClass parameterType = parameterTypes[i];
96  			boolean isArray = parameterType.isArray();
97  			CtClass arrayType = parameterType.getComponentType();
98  			if (isArray) {
99  				while (arrayType.isArray()) {
100 					arrayType = arrayType.getComponentType();
101 				}
102 			}
103 
104 			sb.append(" + \"");
105 			try {
106 				sb.append(parameterNameFor(method, locals, i));
107 			} catch (Exception e) {
108 				sb.append("" + (i + 1));
109 			}
110 			sb.append("\" + \"=");
111 
112 			if (parameterType.isPrimitive()) {
113 				// let the compiler handle primitive -> string
114 				sb.append("\"+ $" + (i + 1));
115 			} else {
116 				String s = "org.slf4j.instrumentation.ToStringHelper.render";
117 				sb.append("\"+ " + s + "($" + (i + 1) + ")");
118 			}
119 		}
120 		sb.append("+\")");
121 
122 		String signature = sb.toString();
123 		return signature;
124 	}
125 
126 	/**
127 	 * Determine the name of parameter with index i in the given method. Use the
128 	 * locals attributes about local variables from the classfile. Note: This is
129 	 * still work in progress.
130 	 * 
131 	 * @param method
132 	 * @param locals
133 	 * @param i
134 	 * @return the name of the parameter if available or a number if not.
135 	 */
136 	static String parameterNameFor(CtBehavior method,
137 			LocalVariableAttribute locals, int i) {
138 
139 		if (locals == null) {
140 			return Integer.toString(i + 1);
141 		}
142 
143 		int modifiers = method.getModifiers();
144 
145 		int j = i;
146 
147 		if (Modifier.isSynchronized(modifiers)) {
148 			// skip object to synchronize upon.
149 			j++;
150 			// System.err.println("Synchronized");
151 		}
152 		if (Modifier.isStatic(modifiers) == false) {
153 			// skip "this"
154 			j++;
155 			// System.err.println("Instance");
156 		}
157 		String variableName = locals.variableName(j);
158 		// if (variableName.equals("this")) {
159 		// System.err.println("'this' returned as a parameter name for "
160 		// + method.getName() + " index " + j
161 		// +
162 		// ", names are probably shifted. Please submit source for class in slf4j bugreport");
163 		// }
164 		return variableName;
165 	}
166 }