View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.internal.util;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   
10  import java.io.IOException;
11  
12  /**
13   * Provides utility methods to extract and filter stack trace information.
14   */
15  public final class StackTrace {
16      @NonNull
17      private final Throwable throwable;
18      @NonNull
19      private StackTraceElement[] elements;
20  
21      public StackTrace(@NonNull Throwable throwable) {
22          this.throwable = throwable;
23          elements = throwable.getStackTrace();
24      }
25  
26      public int getDepth() {
27          return elements.length;
28      }
29  
30      @NonNull
31      public StackTraceElement getElement(int index) {
32          return elements[index];
33      }
34  
35      public static void filterStackTrace(@NonNull Throwable t) {
36          new StackTrace(t).filter();
37      }
38  
39      public void filter() {
40          StackTraceElement[] filteredST = new StackTraceElement[elements.length];
41          int i = 0;
42  
43          for (StackTraceElement ste : elements) {
44              if (ste.getFileName() != null) {
45                  String where = ste.getClassName();
46  
47                  if (!isJDKOr3rdPartyLibraryInternalMethod(ste) && !isTestFrameworkMethod(where)
48                          && !isJMockitMethod(where)) {
49                      filteredST[i] = ste;
50                      i++;
51                  }
52              }
53          }
54  
55          StackTraceElement[] newStackTrace = new StackTraceElement[i];
56          System.arraycopy(filteredST, 0, newStackTrace, 0, i);
57          throwable.setStackTrace(newStackTrace);
58          elements = newStackTrace;
59  
60          Throwable cause = throwable.getCause();
61  
62          if (cause != null) {
63              new StackTrace(cause).filter();
64          }
65      }
66  
67      private static boolean isJDKOr3rdPartyLibraryInternalMethod(@NonNull StackTraceElement ste) {
68          String className = ste.getClassName();
69  
70          return className.startsWith("sun.") && !ste.isNativeMethod() || className.startsWith("jdk.")
71                  || className.startsWith("java.util.") || className.contains(".reflect.")
72                  || className.contains(".surefire.") || className.contains(".gradle.")
73                  || className.contains(".intellij.") || className.contains(".jdt.");
74      }
75  
76      private static boolean isTestFrameworkMethod(@NonNull String where) {
77          return where.startsWith("org.junit.") || where.startsWith("org.testng.");
78      }
79  
80      private static boolean isJMockitMethod(@NonNull String where) {
81          if (!where.startsWith("mockit.")) {
82              return false;
83          }
84  
85          int p = where.indexOf('$');
86  
87          if (p < 0) {
88              int q = where.lastIndexOf("Test");
89              return q < 0 || q + 4 < where.length();
90          }
91  
92          int q = where.lastIndexOf("Test", p - 4);
93  
94          if (q < 0) {
95              return true;
96          }
97  
98          q += 4;
99          return q < where.length() && where.charAt(q) != '$';
100     }
101 
102     public void print(@NonNull Appendable output) {
103         String previousFileName = null;
104         int previousLineNumber = 0;
105         String sep = "";
106 
107         for (int i = 0, d = getDepth(); i < d; i++) {
108             StackTraceElement ste = elements[i];
109 
110             if (ste.getLineNumber() != previousLineNumber || !ste.getFileName().equals(previousFileName)) {
111                 try {
112                     output.append(sep).append("\tat ").append(ste.toString());
113                 } catch (IOException ignore) {
114                 }
115                 sep = "\n";
116                 previousFileName = ste.getFileName();
117                 previousLineNumber = ste.getLineNumber();
118             }
119         }
120     }
121 }