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 javassist source snippet which either is empty (for anything
44 * which does not return a value) or a explanatory text around the $_
45 * javassist 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 javassist 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 }