/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.internal; import java.util.ArrayList; import java.util.Iterator; import java.util.Spliterator; import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * */ public final class StackTraces { private StackTraces() { } /** * Returns the script stack trace elements. * * @param e * the throwable object * @return the script stack trace elements */ public static Stream<StackTraceElement> stackTraceStream(Throwable e) { StackTraceElementIterator iterator = new StackTraceElementIterator(e); int characteristics = Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED; Spliterator<StackTraceElement> spliterator = Spliterators.spliteratorUnknownSize(iterator, characteristics); return StreamSupport.stream(spliterator, false).map(StackTraces::toScriptFrame); } /** * Returns the script stack trace elements. * * @param e * the throwable object * @return the script stack trace elements */ public static StackTraceElement[] scriptStackTrace(Throwable e) { ArrayList<StackTraceElement> list = new ArrayList<>(); for (Iterator<StackTraceElement> it = new StackTraceElementIterator(e); it.hasNext();) { list.add(toScriptFrame(it.next())); } return list.toArray(new StackTraceElement[0]); } /** * Returns a script stack trace element. * * @param e * the stack trace element * @return the script stack trace element */ private static StackTraceElement toScriptFrame(StackTraceElement e) { String methodName = JVMNames.fromBytecodeName(e.getMethodName()); assert methodName.charAt(0) != '!'; int i = methodName.lastIndexOf('~'); String scriptMethod = methodName.substring(0, (i != -1 ? i : methodName.length())); return new StackTraceElement("", scriptMethod, e.getFileName(), e.getLineNumber()); } private static final class StackTraceElementIterator extends SimpleIterator<StackTraceElement> { private StackTraceElement[] elements; private int cursor; StackTraceElementIterator(Throwable e) { this.elements = e.getStackTrace(); } @Override protected StackTraceElement findNext() { StackTraceElement[] elements = this.elements; if (elements != null) { int c = cursor; while (cursor < elements.length) { StackTraceElement element = elements[cursor++]; if (isScriptStackFrame(element)) { return element; } } this.elements = null; // Return an "Interpreter" frame if no script stack frames were found. if (c == 0) { return interpreterFrame(); } } return null; } private static boolean isScriptStackFrame(StackTraceElement element) { // Filter stacktrace elements based on the encoding in Compiler/CodeGenerator. return element.getClassName().charAt(0) == '#' && JVMNames.fromBytecodeName(element.getMethodName()).charAt(0) != '!' && element.getLineNumber() > 0; } private static StackTraceElement interpreterFrame() { return new StackTraceElement("#Interpreter", "~interpreter", "<Interpreter>", 1); } } }