|
|
@@ -0,0 +1,467 @@
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/assira
|
|
|
+ */
|
|
|
+package net.ranides.assira.trace;
|
|
|
+
|
|
|
+import net.ranides.assira.generic.ValueUtils;
|
|
|
+
|
|
|
+import java.util.AbstractList;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Optional;
|
|
|
+import java.util.function.Predicate;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ */
|
|
|
+public final class FStackWalker8 implements TraceUtils.IStackWalker {
|
|
|
+
|
|
|
+ // Please note that class is implemented in very verbose way:
|
|
|
+ // we don't use any functional interfaces, adapters, JDK Stream API etc.
|
|
|
+ //
|
|
|
+ // Every syntactic elegance will slow down us. It matters, because functions
|
|
|
+ // are designed to be *extremaly* fast (~1 us per call) and every overhead
|
|
|
+ // is noticeable.
|
|
|
+ //
|
|
|
+ // We use "package scope" because we want to test our resolvers in junit
|
|
|
+
|
|
|
+ private static final String NSE = "StackInspector: operation not supported. Please use Sun JDK or enable SecurityManager.";
|
|
|
+
|
|
|
+ static final SunResolver RESOLVER_SUN = getSunResolver();
|
|
|
+
|
|
|
+ static final SecurityResolver RESOLVER_SECURITY = getSecurityResolver();
|
|
|
+
|
|
|
+ static final ThreadResolver RESOLVER_THREAD = new ThreadResolver();
|
|
|
+
|
|
|
+ static final ExceptionResolver RESOLVER_EXCEPT = new ExceptionResolver();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * if you want full list, SecurityManager is faster than SunReflection
|
|
|
+ */
|
|
|
+ static final Resolver AUTO_LIST_RESOLVER = ValueUtils.or(
|
|
|
+ RESOLVER_SECURITY,
|
|
|
+ RESOLVER_SUN,
|
|
|
+ RESOLVER_EXCEPT
|
|
|
+ );
|
|
|
+
|
|
|
+ /**
|
|
|
+ * if you want first element, SunReflection is faster than SecurityManager
|
|
|
+ */
|
|
|
+ static final Resolver AUTO_CALLER_RESOLVER = ValueUtils.or(
|
|
|
+ RESOLVER_SUN,
|
|
|
+ RESOLVER_SECURITY,
|
|
|
+ RESOLVER_EXCEPT
|
|
|
+ );
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<StackTraceElement> getFrames() {
|
|
|
+ return new FrameList();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Class<?>> getTypes() {
|
|
|
+ return AUTO_LIST_RESOLVER.list();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<String> getNames() {
|
|
|
+ return AUTO_LIST_RESOLVER.names();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Optional<StackTraceElement> getFrame(Predicate<StackTraceElement> filter) {
|
|
|
+ return new FrameList().stream().filter(filter).findFirst();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Optional<Class<?>> getType(Predicate<Class<?>> filter) {
|
|
|
+ return AUTO_LIST_RESOLVER.list().stream().filter(filter).findFirst();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Optional<String> getName(Predicate<String> filter) {
|
|
|
+ return AUTO_LIST_RESOLVER.names().stream().filter(filter).findFirst();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StackTraceElement getFrame(int index) {
|
|
|
+ return new FrameList().get(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Class<?> getType(int index) {
|
|
|
+ return AUTO_CALLER_RESOLVER.type(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getName(int index) {
|
|
|
+ return AUTO_CALLER_RESOLVER.name(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD")
|
|
|
+ static List<Class<?>> test_getTypes(Resolver resolver) {
|
|
|
+ return test1_getTypes(resolver);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<Class<?>> test1_getTypes(Resolver resolver) {
|
|
|
+ return resolver.list();
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD")
|
|
|
+ static List<String> test_getNames(Resolver resolver) {
|
|
|
+ return test1_getNames(resolver);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<String> test1_getNames(Resolver resolver) {
|
|
|
+ return resolver.names();
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD")
|
|
|
+ static Class<?> test_getCalleeType(Resolver resolver) {
|
|
|
+ return test_getCalleeType1(resolver);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Class<?> test_getCalleeType1(Resolver resolver) {
|
|
|
+ return resolver.type(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD")
|
|
|
+ static String test_getCalleeName(Resolver resolver) {
|
|
|
+ return test_getCalleeName1(resolver);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String test_getCalleeName1(Resolver resolver) {
|
|
|
+ return resolver.name(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD")
|
|
|
+ static Class<?> test_getCallerType(Resolver resolver) {
|
|
|
+ return test_getCallerType1(resolver);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Class<?> test_getCallerType1(Resolver resolver) {
|
|
|
+ return resolver.type(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD")
|
|
|
+ static String test_getCallerName(Resolver resolver) {
|
|
|
+ return test_getCallerName1(resolver);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String test_getCallerName1(Resolver resolver) {
|
|
|
+ return resolver.name(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ interface Resolver {
|
|
|
+
|
|
|
+ Class<?> type(int index);
|
|
|
+
|
|
|
+ String name(int index);
|
|
|
+
|
|
|
+ List<Class<?>> list();
|
|
|
+
|
|
|
+ List<String> names();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static SunResolver getSunResolver() {
|
|
|
+ try {
|
|
|
+ return new SunResolver();
|
|
|
+ } catch(Exception | Error cause) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static SecurityResolver getSecurityResolver() {
|
|
|
+ try {
|
|
|
+ return new SecurityResolver();
|
|
|
+ } catch(SecurityException se) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SecurityResolver extends SecurityManager implements Resolver {
|
|
|
+
|
|
|
+ private static final int FIRST = 3;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Class<?> type(int index) {
|
|
|
+ return super.getClassContext()[FIRST+index];
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String name(int index) {
|
|
|
+ return super.getClassContext()[FIRST+index].getName();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Class<?>> list() {
|
|
|
+ return new ClassArray(super.getClassContext(), FIRST);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<String> names() {
|
|
|
+ return new NameArray(super.getClassContext(), FIRST);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SunResolver implements Resolver {
|
|
|
+
|
|
|
+ private static final int FIRST = 3+3;
|
|
|
+
|
|
|
+ public SunResolver() {
|
|
|
+ $caller(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Class<?> type(int index) {
|
|
|
+ return $caller(FIRST+index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String name(int index) {
|
|
|
+ return $caller(FIRST+index).getName();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Class<?>> list() {
|
|
|
+ List<Class<?>> list = new ArrayList<>(64);
|
|
|
+ int i=FIRST;
|
|
|
+ Class<?> c;
|
|
|
+ while(null != (c = $caller(i++))) {
|
|
|
+ list.add(c);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<String> names() {
|
|
|
+ List<String> list = new ArrayList<>(64);
|
|
|
+ int i=FIRST;
|
|
|
+ Class<?> c;
|
|
|
+ while(null != (c = $caller(i++))) {
|
|
|
+ list.add(c.getName());
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Class<?> $caller(int index) {
|
|
|
+ return sun.reflect.Reflection.getCallerClass(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class ThreadResolver implements Resolver {
|
|
|
+
|
|
|
+ private static final int FIRST = 4;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<String> names() {
|
|
|
+ return new FrameArray(Thread.currentThread().getStackTrace(), FIRST);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Class<?> type(int index) {
|
|
|
+ throw new UnsupportedOperationException(NSE);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String name(int index) {
|
|
|
+ return Thread.currentThread().getStackTrace()[FIRST+index].getClassName();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Class<?>> list() {
|
|
|
+ throw new UnsupportedOperationException(NSE);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class ExceptionResolver implements Resolver {
|
|
|
+
|
|
|
+ private static final int FIRST = 3;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Class<?> type(int index) {
|
|
|
+ throw new UnsupportedOperationException(NSE);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String name(int index) {
|
|
|
+ return ReflectFrame.get(new Exception(), FIRST+index).getClassName(); //NOPMD
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<String> names() {
|
|
|
+ return new FrameArray(new Exception().getStackTrace(), FIRST); //NOPMD
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Class<?>> list() {
|
|
|
+ throw new UnsupportedOperationException(NSE);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class ClassArray extends AbstractList<Class<?>> {
|
|
|
+
|
|
|
+ private final Class<?>[] array;
|
|
|
+ private final int offset;
|
|
|
+ private final int length;
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD.ArrayIsStoredDirectly")
|
|
|
+ public ClassArray(Class<?>[] array, int offset, int length) {
|
|
|
+ this.array = array;
|
|
|
+ this.offset = offset;
|
|
|
+ this.length = length;
|
|
|
+ }
|
|
|
+
|
|
|
+ public ClassArray(Class<?>[] array, int offset) {
|
|
|
+ this(array, offset, array.length-offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Class<?> get(int index) {
|
|
|
+ return array[offset+index];
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return length;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class NameArray extends AbstractList<String> {
|
|
|
+
|
|
|
+ private final Class<?>[] array;
|
|
|
+ private final int offset;
|
|
|
+ private final int length;
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD.ArrayIsStoredDirectly")
|
|
|
+ public NameArray(Class<?>[] array, int offset, int length) {
|
|
|
+ this.array = array;
|
|
|
+ this.offset = offset;
|
|
|
+ this.length = length;
|
|
|
+ }
|
|
|
+
|
|
|
+ public NameArray(Class<?>[] array, int offset) {
|
|
|
+ this(array, offset, array.length-offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String get(int index) {
|
|
|
+ return array[offset+index].getName();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return length;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class FrameArray extends AbstractList<String> {
|
|
|
+
|
|
|
+ private final StackTraceElement[] array;
|
|
|
+ private final int offset;
|
|
|
+ private final int length;
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD.ArrayIsStoredDirectly")
|
|
|
+ public FrameArray(StackTraceElement[] array, int offset, int length) {
|
|
|
+ this.array = array;
|
|
|
+ this.offset = offset;
|
|
|
+ this.length = length;
|
|
|
+ }
|
|
|
+
|
|
|
+ public FrameArray(StackTraceElement[] array, int offset) {
|
|
|
+ this(array, offset, array.length-offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String get(int index) {
|
|
|
+ return array[offset+index].getClassName();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return length;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class FrameList extends AbstractList<StackTraceElement> {
|
|
|
+
|
|
|
+ private static final int FIRST = 3;
|
|
|
+
|
|
|
+ private final Throwable cause;
|
|
|
+
|
|
|
+ private final int size;
|
|
|
+
|
|
|
+ private StackTraceElement[] array;
|
|
|
+
|
|
|
+ public FrameList() {
|
|
|
+ this.cause = new Throwable(); // NOPMD
|
|
|
+ this.size = ReflectFrame.depth(cause)-FIRST;
|
|
|
+ this.array = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StackTraceElement get(int index) {
|
|
|
+ if(null == array) {
|
|
|
+ array = new StackTraceElement[ReflectFrame.depth(cause)];
|
|
|
+ }
|
|
|
+ if(null == array[index]) {
|
|
|
+ array[index] = ReflectFrame.get(cause, FIRST+index);
|
|
|
+ }
|
|
|
+ return array[index];
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return size;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class ReflectFrame { // NOPMD - lazy init idiom
|
|
|
+
|
|
|
+ public static final java.lang.reflect.Method GET;
|
|
|
+
|
|
|
+ public static final java.lang.reflect.Method DEPTH;
|
|
|
+
|
|
|
+ static {
|
|
|
+ try {
|
|
|
+ GET = Throwable.class.getDeclaredMethod("getStackTraceElement", int.class);
|
|
|
+ GET.setAccessible(true);
|
|
|
+
|
|
|
+ DEPTH = Throwable.class.getDeclaredMethod("getStackTraceDepth");
|
|
|
+ DEPTH.setAccessible(true);
|
|
|
+ } catch(ReflectiveOperationException ex) {
|
|
|
+ throw new UnsupportedOperationException(NSE, ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static int depth(Throwable that) {
|
|
|
+ try {
|
|
|
+ return (int)DEPTH.invoke(that);
|
|
|
+ } catch(ReflectiveOperationException ex) {
|
|
|
+ throw new UnsupportedOperationException(NSE, ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static StackTraceElement get(Throwable that, int index) {
|
|
|
+ try {
|
|
|
+ return (StackTraceElement)GET.invoke(that, index);
|
|
|
+ } catch(ReflectiveOperationException ex) {
|
|
|
+ throw new UnsupportedOperationException(NSE, ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|