All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mockito.internal.exceptions.stacktrace.StackTraceFilter Maven / Gradle / Ivy

There is a newer version: 5.12.0
Show newest version
/*
 * Copyright (c) 2007 Mockito contributors
 * This program is made available under the terms of the MIT License.
 */
package org.mockito.internal.exceptions.stacktrace;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.mockito.exceptions.stacktrace.StackTraceCleaner;
import org.mockito.internal.configuration.plugins.Plugins;

public class StackTraceFilter implements Serializable {

    static final long serialVersionUID = -5499819791513105700L;

    private static final StackTraceCleaner CLEANER =
            Plugins.getStackTraceCleanerProvider()
                    .getStackTraceCleaner(new DefaultStackTraceCleaner());

    private static Object JAVA_LANG_ACCESS;
    private static Method GET_STACK_TRACE_ELEMENT;

    static {
        try {
            JAVA_LANG_ACCESS =
                    Class.forName("sun.misc.SharedSecrets")
                            .getMethod("getJavaLangAccess")
                            .invoke(null);
            GET_STACK_TRACE_ELEMENT =
                    Class.forName("sun.misc.JavaLangAccess")
                            .getMethod("getStackTraceElement", Throwable.class, int.class);
        } catch (Exception ignored) {
            // Use the slow computational path for filtering stacktraces if fast path does not exist
            // in JVM
        }
    }

    /**
     * Example how the filter works (+/- means good/bad):
     * [a+, b+, c-, d+, e+, f-, g+] -> [a+, b+, d+, e+, g+]
     * Basically removes all bad from the middle.
     * If any good are in the middle of bad those are also removed.
     */
    public StackTraceElement[] filter(StackTraceElement[] target, boolean keepTop) {
        // TODO: profile
        // TODO: investigate "keepTop" commit history - no effect!
        final List filtered = new ArrayList<>();
        for (StackTraceElement element : target) {
            if (CLEANER.isIn(element)) {
                filtered.add(element);
            }
        }
        StackTraceElement[] result = new StackTraceElement[filtered.size()];
        return filtered.toArray(result);
    }

    /**
     * This filtering strategy makes use of a fast-path computation to retrieve stackTraceElements
     * from a Stacktrace of a Throwable. It does so, by taking advantage of {@link
     * sun.misc.SharedSecrets} and {@link sun.misc.JavaLangAccess}.
     *
     * 

The {@link sun.misc.SharedSecrets} provides a method to obtain an instance of an {@link * sun.misc.JavaLangAccess}. The latter class has a method to fast-path into {@link * Throwable#getStackTrace()} and retrieve a single {@link StackTraceElement}. This prevents the * JVM from having to generate a full stacktrace, which could potentially be expensive if * stacktraces become very large. * * @param target The throwable target to find the first {@link StackTraceElement} that should * not be filtered out per {@link StackTraceFilter#CLEANER}. * @return The first {@link StackTraceElement} outside of the {@link StackTraceFilter#CLEANER} */ public StackTraceElement filterFirst(Throwable target, boolean isInline) { boolean shouldSkip = isInline; if (GET_STACK_TRACE_ELEMENT != null) { int i = 0; // The assumption here is that the CLEANER filter will not filter out every single // element. However, since we don't want to compute the full length of the stacktrace, // we don't know the upper boundary. Therefore, simply increment the counter and go as // far as we have to go, assuming that we get there. If, in the rare occasion, we // don't, we fall back to the old slow path. while (true) { try { StackTraceElement stackTraceElement = (StackTraceElement) GET_STACK_TRACE_ELEMENT.invoke(JAVA_LANG_ACCESS, target, i); if (CLEANER.isIn(stackTraceElement)) { if (shouldSkip) { shouldSkip = false; } else { return stackTraceElement; } } } catch (Exception e) { // Fall back to slow path break; } i++; } } // If we can't use the fast path of retrieving stackTraceElements, use the slow path by // iterating over the actual stacktrace for (StackTraceElement stackTraceElement : target.getStackTrace()) { if (CLEANER.isIn(stackTraceElement)) { if (shouldSkip) { shouldSkip = false; } else { return stackTraceElement; } } } return null; } /** * Finds the source file of the target stack trace. * Returns the default value if source file cannot be found. */ public String findSourceFile(StackTraceElement[] target, String defaultValue) { for (StackTraceElement e : target) { if (CLEANER.isIn(e)) { return e.getFileName(); } } return defaultValue; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy