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) 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 javaassist 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 } 187