001/** 002 * Copyright (c) 2004-2011 QOS.ch 003 * All rights reserved. 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining 006 * a copy of this software and associated documentation files (the 007 * "Software"), to deal in the Software without restriction, including 008 * without limitation the rights to use, copy, modify, merge, publish, 009 * distribute, sublicense, and/or sell copies of the Software, and to 010 * permit persons to whom the Software is furnished to do so, subject to 011 * the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be 014 * included in all copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 021 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 023 * 024 */ 025package org.slf4j.instrumentation; 026 027import javassist.CtBehavior; 028import javassist.CtClass; 029import javassist.CtMethod; 030import javassist.Modifier; 031import javassist.NotFoundException; 032import javassist.bytecode.AttributeInfo; 033import javassist.bytecode.CodeAttribute; 034import javassist.bytecode.LocalVariableAttribute; 035 036/** 037 * Helper methods for Javassist functionality. 038 * 039 */ 040public class JavassistHelper { 041 042 /** 043 * Create a javassist source snippet which either is empty (for anything 044 * which does not return a value) or an explanatory text around the $_ 045 * javassist return value variable. 046 * 047 * @param method 048 * descriptor of method 049 * @return source snippet 050 * @throws NotFoundException 051 */ 052 public static String returnValue(CtBehavior method) throws NotFoundException { 053 054 String returnValue = ""; 055 if (methodReturnsValue(method)) { 056 returnValue = " returns: \" + $_ + \"."; 057 } 058 return returnValue; 059 } 060 061 /** 062 * determine if the given method returns a value, and return true if so. 063 * false otherwise. 064 * 065 * @param method 066 * @return 067 * @throws NotFoundException 068 */ 069 private static boolean methodReturnsValue(CtBehavior method) throws NotFoundException { 070 071 if (method instanceof CtMethod == false) { 072 return false; 073 } 074 075 CtClass returnType = ((CtMethod) method).getReturnType(); 076 String returnTypeName = returnType.getName(); 077 078 boolean isVoidMethod = "void".equals(returnTypeName); 079 080 boolean methodReturnsValue = isVoidMethod == false; 081 return methodReturnsValue; 082 } 083 084 /** 085 * Return javassist source snippet which lists all the parameters and their 086 * values. If available the source names are extracted from the debug 087 * information and used, otherwise just a number is shown. 088 * 089 * @param method 090 * @return 091 * @throws NotFoundException 092 */ 093 public static String getSignature(CtBehavior method) throws NotFoundException { 094 095 CtClass[] parameterTypes = method.getParameterTypes(); 096 097 CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute(); 098 099 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}