Since no one seems to be aware of the existing solution, I rolled my own quick and dirty.
It does not support all SMAP functions (it analyzes only the first layer and ignores the vendor sections and default status information), but this is enough for my needs.
Since the code to extract the SMAP attribute from the class is only about 50 lines, I decided to redefine it instead of adding ASM as a dependency. The code for use with ASM is in the comments.
Since it has been tested very little (in several test cases), I will edit the message if I encounter serious errors.
Code below:
package smap; import java.io.*; import java.util.*; import java.util.regex.*; public class SMAPSourceDebugExtension { public static void enhanceStackTrace(Throwable t, ClassLoader cl, boolean keepOriginalFrames, String... packageNames) throws IOException { enhanceStackTrace(t, new HashMap<String, SMAPSourceDebugExtension>(), cl, keepOriginalFrames, packageNames); } public static void enhanceStackTrace(Throwable t, Map<String, SMAPSourceDebugExtension> cache, ClassLoader cl, boolean keepOriginalFrames, String... packageNames) throws IOException { StackTraceElement[] elements = t.getStackTrace(); List<StackTraceElement> newElements = null; for (int i = 0; i < elements.length; i++) { String className = elements[i].getClassName(); SMAPSourceDebugExtension smap = cache.get(className); if (smap == null) { boolean found = false; for (String packageName : packageNames) { if (className.startsWith(packageName + ".")) { found = true; break; } } if (found || packageNames.length == 0) { InputStream in = cl.getResourceAsStream(className.replace('.', '/') + ".class"); if (in != null) { String value = extractSourceDebugExtension(in); in.close(); if (value != null) { value = value.replaceAll("\r\n?", "\n"); if (value.startsWith("SMAP\n")) { smap = new SMAPSourceDebugExtension(value); cache.put(className, smap); } } } } } StackTraceElement newFrame = null; if (smap != null) { int[] inputLineInfo = smap.reverseLineMapping.get(elements[i].getLineNumber()); if (inputLineInfo != null && elements[i].getFileName().equals(smap.generatedFileName)) { FileInfo inputFileInfo = smap.fileinfo.get(inputLineInfo[0]); if (inputFileInfo != null) { newFrame = new StackTraceElement("[" + smap.firstStratum + "]", inputFileInfo.path, inputFileInfo.name, inputLineInfo[1]); } } } if (newFrame != null) { if (newElements == null) { newElements = new ArrayList<StackTraceElement>(Arrays.asList(elements).subList(0, i)); } if (keepOriginalFrames) newElements.add(elements[i]); newElements.add(newFrame); } else if (newElements != null) { newElements.add(elements[i]); } } if (newElements != null) { t.setStackTrace(newElements.toArray(new StackTraceElement[newElements.size()])); } if (t.getCause() != null) enhanceStackTrace(t.getCause(), cache, cl, keepOriginalFrames, packageNames); }
source share