View Javadoc

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 }