17ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu/**
27ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * Copyright (c) 2004-2011 QOS.ch
37ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * All rights reserved.
47ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu *
57ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * Permission is hereby granted, free  of charge, to any person obtaining
67ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * a  copy  of this  software  and  associated  documentation files  (the
77ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * "Software"), to  deal in  the Software without  restriction, including
87ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * without limitation  the rights to  use, copy, modify,  merge, publish,
97ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * distribute,  sublicense, and/or sell  copies of  the Software,  and to
107ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * permit persons to whom the Software  is furnished to do so, subject to
117ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * the following conditions:
127ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu *
137ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * The  above  copyright  notice  and  this permission  notice  shall  be
147ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * included in all copies or substantial portions of the Software.
157ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu *
167ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
177ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
187ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
197ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
207ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
217ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
227ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
237ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu *
247ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu */
2516764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersenpackage org.slf4j.instrumentation;
2616764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersen
2716764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersenimport javassist.CtBehavior;
2816764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersenimport javassist.CtClass;
2916764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersenimport javassist.CtMethod;
3016764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersenimport javassist.Modifier;
3116764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersenimport javassist.NotFoundException;
32231046d8abf52f4b7f7a69d037bbc0a58dbe07cbThorbjorn Ravn Andersenimport javassist.bytecode.AttributeInfo;
3316764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersenimport javassist.bytecode.CodeAttribute;
3416764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersenimport javassist.bytecode.LocalVariableAttribute;
3516764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersen
366d8325a0c52b9f04a76085c748beddc306027d18Thorbjorn Ravn Andersen/**
376d8325a0c52b9f04a76085c748beddc306027d18Thorbjorn Ravn Andersen * Helper methods for Javassist functionality.
3842d254db97a8103ede6461d8d16303cbd037ccd5Thorbjorn Ravn Andersen *
396d8325a0c52b9f04a76085c748beddc306027d18Thorbjorn Ravn Andersen */
4016764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersenpublic class JavassistHelper {
4116764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersen
4231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    /**
4331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * Create a javaassist source snippet which either is empty (for anything
4431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * which does not return a value) or a explanatory text around the $_
4531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * javaassist return value variable.
4631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
4731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param method
4831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *            descriptor of method
4931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @return source snippet
5031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @throws NotFoundException
5131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     */
5231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    public static String returnValue(CtBehavior method) throws NotFoundException {
5331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
5431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        String returnValue = "";
5531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (methodReturnsValue(method)) {
5631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            returnValue = " returns: \" + $_ + \".";
5731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
5831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        return returnValue;
5931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    }
6031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
6131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    /**
6231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * determine if the given method returns a value, and return true if so.
6331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * false otherwise.
6431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
6531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param method
6631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @return
6731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @throws NotFoundException
6831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     */
6931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static boolean methodReturnsValue(CtBehavior method) throws NotFoundException {
7031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
7131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (method instanceof CtMethod == false) {
7231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return false;
7331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
7431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
7531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        CtClass returnType = ((CtMethod) method).getReturnType();
7631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        String returnTypeName = returnType.getName();
7731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
7831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        boolean isVoidMethod = "void".equals(returnTypeName);
7931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
8031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        boolean methodReturnsValue = isVoidMethod == false;
8131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        return methodReturnsValue;
8231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    }
8331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
8431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    /**
8531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * Return javaassist source snippet which lists all the parameters and their
8631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * values. If available the source names are extracted from the debug
8731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * information and used, otherwise just a number is shown.
8831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
8931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param method
9031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @return
9131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @throws NotFoundException
9231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     */
9331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    public static String getSignature(CtBehavior method) throws NotFoundException {
9431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
9531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        CtClass parameterTypes[] = method.getParameterTypes();
9631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
9731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute();
9831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
9931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        LocalVariableAttribute locals = null;
10031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
10131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (codeAttribute != null) {
10231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            AttributeInfo attribute;
10331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            attribute = codeAttribute.getAttribute("LocalVariableTable");
10431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            locals = (LocalVariableAttribute) attribute;
10531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
10631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
10731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        String methodName = method.getName();
10831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
10931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        StringBuilder sb = new StringBuilder(methodName).append("(\" ");
11031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (int i = 0; i < parameterTypes.length; i++) {
11131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (i > 0) {
11231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                // add a comma and a space between printed values
11331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sb.append(" + \", \" ");
11431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            }
11531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
11631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            CtClass parameterType = parameterTypes[i];
11731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            boolean isArray = parameterType.isArray();
11831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            CtClass arrayType = parameterType.getComponentType();
11931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (isArray) {
12031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                while (arrayType.isArray()) {
12131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    arrayType = arrayType.getComponentType();
12231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                }
12331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            }
12431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
12531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sb.append(" + \"");
12631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            try {
12731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sb.append(parameterNameFor(method, locals, i));
12831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } catch (Exception e) {
12931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sb.append(i + 1);
13031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            }
13131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sb.append("\" + \"=");
13231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
13331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (parameterType.isPrimitive()) {
13431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                // let the compiler handle primitive -> string
13531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sb.append("\"+ $").append(i + 1);
13631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else {
13731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                String s = "org.slf4j.instrumentation.ToStringHelper.render";
13831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sb.append("\"+ ").append(s).append("($").append(i + 1).append(')');
13931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            }
14031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
14131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sb.append("+\")");
14231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
14331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        String signature = sb.toString();
14431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        return signature;
14531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    }
14631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
14731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    /**
14831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * Determine the name of parameter with index i in the given method. Use the
14931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * locals attributes about local variables from the classfile. Note: This is
15031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * still work in progress.
15131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
15231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param method
15331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param locals
15431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param i
15531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @return the name of the parameter if available or a number if not.
15631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     */
15731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    static String parameterNameFor(CtBehavior method, LocalVariableAttribute locals, int i) {
15831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
15931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (locals == null) {
16031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return Integer.toString(i + 1);
16131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
16231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
16331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        int modifiers = method.getModifiers();
16431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
16531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        int j = i;
16631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
16731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (Modifier.isSynchronized(modifiers)) {
16831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            // skip object to synchronize upon.
16931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            j++;
17031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            // System.err.println("Synchronized");
17131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
17231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (Modifier.isStatic(modifiers) == false) {
17331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            // skip "this"
17431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            j++;
17531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            // System.err.println("Instance");
17631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
17731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        String variableName = locals.variableName(j);
17831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        // if (variableName.equals("this")) {
17931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        // System.err.println("'this' returned as a parameter name for "
18031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        // + method.getName() + " index " + j
18131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        // +
18231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        // ", names are probably shifted. Please submit source for class in slf4j bugreport");
18331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        // }
18431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        return variableName;
18531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    }
18616764e712db81772de71ba3f7932b01fbbe2eec3Thorbjorn Ravn Andersen}
187