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