|
|
@@ -0,0 +1,276 @@
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/???
|
|
|
+ */
|
|
|
+package net.ranides.assira.trace;
|
|
|
+
|
|
|
+import java.util.AbstractList;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import net.ranides.assira.generic.ValueUtils;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ */
|
|
|
+public final class StackInspector {
|
|
|
+
|
|
|
+ // 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
|
|
|
+
|
|
|
+
|
|
|
+ static final Resolver RESOLVER_SR = SunResolver.get();
|
|
|
+
|
|
|
+ static final Resolver RESOLVER_SM = SMResolver.get();
|
|
|
+
|
|
|
+ static final Resolver RESOLVER_TS = () -> new FrameArray(Thread.currentThread().getStackTrace(), 4);
|
|
|
+
|
|
|
+ static final Resolver RESOLVER_ES = () -> new FrameArray(new Exception().getStackTrace(), 3); // NOPMD
|
|
|
+
|
|
|
+ /**
|
|
|
+ * if you want full list, SecurityManager is faster than SunReflection
|
|
|
+ */
|
|
|
+ static final Resolver AUTO_LIST_RESOLVER = ValueUtils.or(
|
|
|
+ RESOLVER_SM,
|
|
|
+ RESOLVER_SR,
|
|
|
+ RESOLVER_ES
|
|
|
+ );
|
|
|
+
|
|
|
+ /**
|
|
|
+ * if you want first element, SunReflection is faster than SecurityManager
|
|
|
+ */
|
|
|
+ static final Resolver AUTO_CALLER_RESOLVER = ValueUtils.or(
|
|
|
+ RESOLVER_SR,
|
|
|
+ RESOLVER_SM,
|
|
|
+ RESOLVER_ES
|
|
|
+ );
|
|
|
+
|
|
|
+ private StackInspector() { /* utility class */ }
|
|
|
+
|
|
|
+ public static List<Class> getCallers() {
|
|
|
+ return AUTO_LIST_RESOLVER.list();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static List<String> getCallerNames() {
|
|
|
+ return AUTO_LIST_RESOLVER.names();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Class getCaller() {
|
|
|
+ return AUTO_CALLER_RESOLVER.firstClass();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String getCallerName() {
|
|
|
+ return AUTO_CALLER_RESOLVER.firstName();
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD")
|
|
|
+ static List<Class> test_getClassList(Resolver resolver) {
|
|
|
+ return resolver.list();
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD")
|
|
|
+ static List<String> test_getClassNames(Resolver resolver) {
|
|
|
+ return resolver.names();
|
|
|
+ }
|
|
|
+
|
|
|
+ static Class test_getCallerClass(Resolver resolver) {
|
|
|
+ return resolver.firstClass();
|
|
|
+ }
|
|
|
+
|
|
|
+ static String test_getCallerClassName(Resolver resolver) {
|
|
|
+ return resolver.firstName();
|
|
|
+ }
|
|
|
+
|
|
|
+ interface Resolver {
|
|
|
+
|
|
|
+ default Class<?> firstClass() {
|
|
|
+ return list().get(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ default String firstName() {
|
|
|
+ return names().get(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ default List<Class> list() {
|
|
|
+ throw new UnsupportedOperationException("StackInspector: operation not supported. Please use Sun JDK or enable SecurityManager.");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<String> names();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SMResolver extends SecurityManager implements Resolver {
|
|
|
+
|
|
|
+ private static final int FIRST = 2;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String firstName() {
|
|
|
+ return super.getClassContext()[FIRST].getName();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Class<?> firstClass() {
|
|
|
+ return super.getClassContext()[FIRST];
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Class> list() {
|
|
|
+ return new ClassArray(super.getClassContext(), FIRST);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<String> names() {
|
|
|
+ return new NameArray(super.getClassContext(), FIRST);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Resolver get() {
|
|
|
+ try {
|
|
|
+ return new SMResolver();
|
|
|
+ } catch(SecurityException se) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SunResolver implements Resolver {
|
|
|
+
|
|
|
+ private static final int FIRST = 3;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String firstName() {
|
|
|
+ return sun.reflect.Reflection.getCallerClass(FIRST).getName();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Class<?> firstClass() {
|
|
|
+ return sun.reflect.Reflection.getCallerClass(FIRST);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Class> list() {
|
|
|
+ List<Class> list = new ArrayList<>(64);
|
|
|
+ int i=FIRST;
|
|
|
+ Class<?> c;
|
|
|
+ while(null != (c = sun.reflect.Reflection.getCallerClass(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 = sun.reflect.Reflection.getCallerClass(i++))) {
|
|
|
+ list.add(c.getName());
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Resolver get() {
|
|
|
+ try {
|
|
|
+ return new SunResolver();
|
|
|
+ } catch(Exception | Error se) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|