Forráskód Böngészése

refactoring: remove StringTraits (there's no clear distinction between StringUtils & StringTraits)
new: StringUtils#raw
new: StackInspector: callee & caller
new: StackInspector: frames
new: StackInspector: performance boost for fail-back getCalleeName() (use FrameResolver instead of full Exception#stacktrace)

Ranides Atterwim 10 éve
szülő
commit
f59f053ce0

+ 95 - 95
assira/src/main/java/net/ranides/assira/system/RuntimeUtils.java

@@ -1,95 +1,95 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-package net.ranides.assira.system;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.LinkedList;
-import java.util.List;
-import net.ranides.assira.text.FormatNumber;
-import net.ranides.assira.trace.LoggerUtils;
-import net.ranides.assira.trace.StackInspector;
-
-/**
- *
- * @author Ranides Atterwim <ranides@gmail.com>
- */
-public final class RuntimeUtils {
-    
-    private static final org.slf4j.Logger LOGGER = LoggerUtils.getLogger();
-    
-    private RuntimeUtils() {
-        // utility class
-    }
-    
-    public static long getMemoryUse() {
-        Runtime runtime = Runtime.getRuntime();
-        return runtime.totalMemory() - runtime.freeMemory();
-    }
-    
-    
-    /**
-     * Runs the garbage collector. 
-     * @return 
-     *      Estimated amount of memory released by GC
-     *      
-     */
-    @SuppressWarnings("PMD.DoNotCallGarbageCollectionExplicitly")
-    @SuppressFBWarnings("DM_GC")
-    public static long performGC() {
-        long prev = getMemoryUse();
-        Runtime.getRuntime().gc();
-        return prev - getMemoryUse();
-    }
-    
-    /**
-     * Forces releasing {@code SoftReferences}, by causing OutOfMemoryError in VM.
-     * @return 
-     *      Estimated amount of memory released by GC
-     */
-    @SuppressWarnings({
-        "PMD.DoNotCallGarbageCollectionExplicitly", 
-        "MismatchedQueryAndUpdateOfCollection"
-    })
-    @SuppressFBWarnings("DM_GC")
-    public static long forceGC() {
-        long prev = Runtime.getRuntime().totalMemory();
-        int block = (int)Math.min(Integer.MAX_VALUE, Runtime.getRuntime().freeMemory());
-        try {
-            final List<long[]> memhog = new LinkedList<>();
-            while(true) {
-                memhog.add(new long[block]);
-            }
-        }
-        catch(final OutOfMemoryError e) {
-            // at this point all SoftReferences have been released - GUARANTEED
-            long alloc = Runtime.getRuntime().totalMemory() - prev;
-            LOGGER.debug("Forced OutOfMemoryError by allocation {} bytes", FormatNumber.group(alloc));
-            Runtime.getRuntime().gc();
-            // at this point we should use minimal amount of memory
-            return prev - getMemoryUse();
-        }
-    }
-    
-    
-    public static boolean invokedInsideMaven() {
-        return StackInspector.getCallerNames()
-            .stream().anyMatch((c)->c.startsWith("org.apache.maven.surefire."));
-    }
-
-    public static boolean invokedInsideSingleTest() {
-        return Li.SINGLE_TEST;
-    }
-    
-    private static final class Li { // NOPMD lazy init idiom
-        
-        private static final boolean SINGLE_TEST = null != AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty("test"));
-        
-    }
-
-}
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.system;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.LinkedList;
+import java.util.List;
+import net.ranides.assira.text.FormatNumber;
+import net.ranides.assira.trace.LoggerUtils;
+import net.ranides.assira.trace.StackInspector;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public final class RuntimeUtils {
+    
+    private static final org.slf4j.Logger LOGGER = LoggerUtils.getLogger();
+    
+    private RuntimeUtils() {
+        // utility class
+    }
+    
+    public static long getMemoryUse() {
+        Runtime runtime = Runtime.getRuntime();
+        return runtime.totalMemory() - runtime.freeMemory();
+    }
+    
+    
+    /**
+     * Runs the garbage collector. 
+     * @return 
+     *      Estimated amount of memory released by GC
+     *      
+     */
+    @SuppressWarnings("PMD.DoNotCallGarbageCollectionExplicitly")
+    @SuppressFBWarnings("DM_GC")
+    public static long performGC() {
+        long prev = getMemoryUse();
+        Runtime.getRuntime().gc();
+        return prev - getMemoryUse();
+    }
+    
+    /**
+     * Forces releasing {@code SoftReferences}, by causing OutOfMemoryError in VM.
+     * @return 
+     *      Estimated amount of memory released by GC
+     */
+    @SuppressWarnings({
+        "PMD.DoNotCallGarbageCollectionExplicitly", 
+        "MismatchedQueryAndUpdateOfCollection"
+    })
+    @SuppressFBWarnings("DM_GC")
+    public static long forceGC() {
+        long prev = Runtime.getRuntime().totalMemory();
+        int block = (int)Math.min(Integer.MAX_VALUE, Runtime.getRuntime().freeMemory());
+        try {
+            final List<long[]> memhog = new LinkedList<>();
+            while(true) {
+                memhog.add(new long[block]);
+            }
+        }
+        catch(final OutOfMemoryError e) {
+            // at this point all SoftReferences have been released - GUARANTEED
+            long alloc = Runtime.getRuntime().totalMemory() - prev;
+            LOGGER.debug("Forced OutOfMemoryError by allocation {} bytes", FormatNumber.group(alloc));
+            Runtime.getRuntime().gc();
+            // at this point we should use minimal amount of memory
+            return prev - getMemoryUse();
+        }
+    }
+    
+    
+    public static boolean invokedInsideMaven() {
+        return StackInspector.getNames()
+            .stream().anyMatch((c)->c.startsWith("org.apache.maven.surefire."));
+    }
+
+    public static boolean invokedInsideSingleTest() {
+        return Li.SINGLE_TEST;
+    }
+    
+    private static final class Li { // NOPMD lazy init idiom
+        
+        private static final boolean SINGLE_TEST = null != AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty("test"));
+        
+    }
+
+}

+ 0 - 73
assira/src/main/java/net/ranides/assira/text/StringTraits.java

@@ -1,73 +0,0 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-
-package net.ranides.assira.text;
-
-/**
- *
- * @author Ranides Atterwim <ranides@gmail.com>
- */
-public final class StringTraits {
-
-	private StringTraits() {
-		/* utility class */
-	}
-	
-	public static int length(String value) {
-		return null == value ? 0 : value.length();
-	}
-	
-	public static boolean isEmpty(String value) {
-		return null == value || value.isEmpty();
-	}
-	
-	public static boolean isBlank(String value) {
-		return isEmpty(value) || value.trim().isEmpty();
-	}
-	
-	public static int indexOf(CharSequence text, CharSequence pattern) {
-		int[] next = initKMP(pattern);
-		
-		int M = pattern.length();
-		int N = text.length();
-		int i, j;
-		for (i = 0, j = 0; i < N && j < M; i++) {
-			while (j >= 0 && text.charAt(i) != pattern.charAt(j)) {
-				j = next[j];
-			}
-			j++;
-		}
-		return (j == M) ? i - M : -1;
-	}
-	
-	public static boolean contains(CharSequence text, CharSequence pattern) {
-		return indexOf(text, pattern) >= 0;
-	}
-
-	private static int[] initKMP(CharSequence pattern) {
-		int M = pattern.length();
-		int[] next = new int[M];
-		int j = -1;
-		for (int i = 0; i < M; i++) {
-			if (i == 0) {
-				next[i] = -1;
-			}
-			else if (pattern.charAt(i) != pattern.charAt(j)) {
-				next[i] = j;
-			}
-			else {
-				next[i] = next[j];
-			}
-			while (j >= 0 && pattern.charAt(i) != pattern.charAt(j)) {
-				j = next[j];
-			}
-			j++;
-		}
-		return next;
-	}
-		
-}

+ 92 - 16
assira/src/main/java/net/ranides/assira/text/StringUtils.java

@@ -7,6 +7,7 @@
 
 package net.ranides.assira.text;
 
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.nio.charset.Charset;
@@ -23,12 +24,26 @@ import java.util.regex.Pattern;
 import net.ranides.assira.collection.arrays.NativeArrayAllocator;
 import net.ranides.assira.lexer.CharStreamer;
 import net.ranides.assira.math.MathUtils;
+import net.ranides.assira.reflection.util.ReflectUtils;
+import net.ranides.assira.trace.ExceptionInspector;
+import net.ranides.assira.trace.StackInspector;
 
 /**
  *
  * @author Ranides Atterwim <ranides@gmail.com>
  */
 public final class StringUtils {
+    
+    private enum CharsType { STR, OCT }
+    
+    private static final char[] C_ESC_SEQ	= new char[]{'\033','['};
+	private static final char[] C_ESC		= new char[]{'\033'};
+	private static final char[] C_SLASH		= new char[]{'\\'};
+	private static final char[] C_BKSPACE	= new char[]{'\b'};
+	private static final char[] C_FORM		= new char[]{'\f'};
+	private static final char[] C_TAB		= new char[]{'\t'};
+	private static final char[] C_CR		= new char[]{'\r'};
+	private static final char[] C_NL		= new char[]{'\n'};
 
 	private StringUtils() {
 		/* utility class */
@@ -169,17 +184,6 @@ public final class StringUtils {
         return result;
     }
 	
-	private enum CharsType { STR, OCT }
-	
-	private static final char[] C_ESC_SEQ	= new char[]{'\033','['};
-	private static final char[] C_ESC		= new char[]{'\033'};
-	private static final char[] C_SLASH		= new char[]{'\\'};
-	private static final char[] C_BKSPACE	= new char[]{'\b'};
-	private static final char[] C_FORM		= new char[]{'\f'};
-	private static final char[] C_TAB		= new char[]{'\t'};
-	private static final char[] C_CR		= new char[]{'\r'};
-	private static final char[] C_NL		= new char[]{'\n'};
-	
 	public static String compile(String input) {
 		StringBuilder builder = new StringBuilder();
         
@@ -526,11 +530,6 @@ public final class StringUtils {
     }
 
     public static String rpadd(String text, char padd, int size) {
-		
-		Collection<String> c = null;
-		int cs = 44;
-		c.stream().map((s)->s.substring(cs));
-		
         int n = text.length();
         int d = size - n;
         if(d <= 0) {
@@ -612,6 +611,30 @@ public final class StringUtils {
         }
         return text.substring(istart, iend);
     }
+    
+    /**
+     * Cholernie przekombinowana metoda, która dodaje do javy pseudo-support dla
+     * psuedo-raw-string-literals. Używa się jej prosto: <code>Strings.raw(/&#42raw text here;&#42;/)</code>
+     * <p>
+     * Komentarz wrzucony pomiędzy nawiasy jest przetwarzany i zwracany przez
+     * funkcję. Żeby to działało, aplikacja musi mieć w runtime dostęp do plików
+     * źródłowych za pomocą ClassLoadera.
+     * </p>
+     * @return 
+     */
+    public static String raw() {
+        try {
+            StackTraceElement frame = StackInspector.getCallerFrame();
+            String source = ReflectUtils.getSource(StackInspector.getCallerType());
+            int offset = 0;
+            for (int i = 1, n = frame.getLineNumber(); i < n; i++) {
+                offset = source.indexOf('\n', offset) + 1;
+            }
+            return source.substring(source.indexOf("/*", offset) + 2, source.indexOf("*/", offset));
+        } catch(IOException ioe) {
+            throw ExceptionInspector.rethrow(ioe);
+        }
+    }
 	
 	public static CharSequence wrap(char[] data) {
         return new CharWrapper(data, 0, data.length);
@@ -624,7 +647,60 @@ public final class StringUtils {
     public static CharSequence repeat(int n, char chr) {
         return new NCSequence(n, chr);
     }
+    
+    public static int length(String value) {
+		return null == value ? 0 : value.length();
+	}
+	
+	public static boolean isEmpty(String value) {
+		return null == value || value.isEmpty();
+	}
+	
+	public static boolean isBlank(String value) {
+		return isEmpty(value) || value.trim().isEmpty();
+	}
+	
+	public static int indexOf(CharSequence text, CharSequence pattern) {
+		int[] next = initKMP(pattern);
+		
+		int M = pattern.length();
+		int N = text.length();
+		int i, j;
+		for (i = 0, j = 0; i < N && j < M; i++) {
+			while (j >= 0 && text.charAt(i) != pattern.charAt(j)) {
+				j = next[j];
+			}
+			j++;
+		}
+		return (j == M) ? i - M : -1;
+	}
+	
+	public static boolean contains(CharSequence text, CharSequence pattern) {
+		return indexOf(text, pattern) >= 0;
+	}
 
+	private static int[] initKMP(CharSequence pattern) {
+		int M = pattern.length();
+		int[] next = new int[M];
+		int j = -1;
+		for (int i = 0; i < M; i++) {
+			if (i == 0) {
+				next[i] = -1;
+			}
+			else if (pattern.charAt(i) != pattern.charAt(j)) {
+				next[i] = j;
+			}
+			else {
+				next[i] = next[j];
+			}
+			while (j >= 0 && pattern.charAt(i) != pattern.charAt(j)) {
+				j = next[j];
+			}
+			j++;
+		}
+		return next;
+	}
+    
     private static class NCSequence implements CharSequence {
         private final int count;
         private final char chr;

+ 215 - 25
assira/src/main/java/net/ranides/assira/trace/StackInspector.java

@@ -8,8 +8,10 @@ package net.ranides.assira.trace;
 
 import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import net.ranides.assira.generic.ValueUtils;
+import net.ranides.assira.reflection.InspectException;
 
 /**
  * 
@@ -26,14 +28,15 @@ public final class StackInspector {
     //
     // 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 Resolver RESOLVER_SR = getSunResolver();
     
     static final Resolver RESOLVER_SM = getSMResolver();
     
-    static final Resolver RESOLVER_TS = () -> new FrameArray(Thread.currentThread().getStackTrace(), 4);
+    static final Resolver RESOLVER_TS = new ThreadResolver();
 
-    static final Resolver RESOLVER_ES = () -> new FrameArray(new Exception().getStackTrace(), 3); // NOPMD
+    static final Resolver RESOLVER_ES = new FrameResolver();
 
     /**
      * if you want full list, SecurityManager is faster than SunReflection 
@@ -55,20 +58,47 @@ public final class StackInspector {
 
     private StackInspector() { /* utility class */ }
     
-    public static List<Class> getCallers() {
+    public static List<StackTraceElement> getFrames() {
+        return new FrameList();
+    }
+    
+    public static List<Class> getTypes() {
         return AUTO_LIST_RESOLVER.list();
     }
     
-    public static List<String> getCallerNames() {
+    public static List<String> getNames() {
         return AUTO_LIST_RESOLVER.names();
     }
     
-    public static Class getCaller() {
-        return AUTO_CALLER_RESOLVER.firstClass();
+    public static StackTraceElement getCalleeFrame() {
+        return new FrameList().get(0);
+    }
+    
+    public static Class getCalleeType() {
+        return AUTO_CALLER_RESOLVER.callee();
+    }
+    
+    public static String getCalleeName() {
+        return AUTO_CALLER_RESOLVER.calleeName();
+    }
+    
+    public static StackTraceElement getCallerFrame() {
+        return new FrameList().get(1);
+    }
+    
+    public static Class getCallerType() {
+        return AUTO_CALLER_RESOLVER.caller();
     }
     
     public static String getCallerName() {
-        return AUTO_CALLER_RESOLVER.firstName();
+        return AUTO_CALLER_RESOLVER.callerName();
+    }
+    
+    @SuppressWarnings("PMD")
+    static List<StackTraceElement> test_getFramesArray() {
+        // we know that's incorrect, but we want to test performance, not "indexing"
+        // it will have 2~3 items more
+        return Arrays.asList(new Exception().getStackTrace());
     }
     
     @SuppressWarnings("PMD")
@@ -82,28 +112,26 @@ public final class StackInspector {
     }
     
 	@SuppressWarnings("PMD")
-    static Class test_getCallerClass(Resolver resolver) {
-        return resolver.firstClass();
+    static Class test_getCalleeClass(Resolver resolver) {
+        return resolver.callee();
     }
     
 	@SuppressWarnings("PMD")
-    static String test_getCallerClassName(Resolver resolver) {
-        return resolver.firstName();
+    static String test_getCalleeName(Resolver resolver) {
+        return resolver.calleeName();
     }
 
     interface Resolver {
         
-        default Class<?> firstClass() {
-            return list().get(1);
-        }
+        Class<?> callee();
         
-        default String firstName() {
-            return names().get(1);
-        }
+        String calleeName();
         
-        default List<Class> list() {
-            throw new UnsupportedOperationException("StackInspector: operation not supported. Please use Sun JDK or enable SecurityManager.");
-        }
+        Class<?> caller();
+        
+        String callerName();
+        
+        List<Class> list();
         
         List<String> names();
     }
@@ -129,15 +157,25 @@ public final class StackInspector {
         private static final int FIRST = 2;
         
         @Override
-        public String firstName() {
+        public String calleeName() {
             return super.getClassContext()[FIRST].getName();
         }
 
         @Override
-        public Class<?> firstClass() {
+        public Class<?> callee() {
             return super.getClassContext()[FIRST];
         }
         
+        @Override
+        public String callerName() {
+            return super.getClassContext()[FIRST+1].getName();
+        }
+        
+        @Override
+        public Class<?> caller() {
+            return super.getClassContext()[FIRST+1];
+        }
+        
         @Override
         public List<Class> list() {
             return new ClassArray(super.getClassContext(), FIRST);
@@ -155,14 +193,24 @@ public final class StackInspector {
         private static final int FIRST = 3;
 
         @Override
-        public String firstName() {
+        public String calleeName() {
             return sun.reflect.Reflection.getCallerClass(FIRST).getName();
         }
 
         @Override
-        public Class<?> firstClass() {
+        public Class<?> callee() {
             return sun.reflect.Reflection.getCallerClass(FIRST);
-            }
+        }
+        
+        @Override
+        public String callerName() {
+            return sun.reflect.Reflection.getCallerClass(FIRST+1).getName();
+        }
+
+        @Override
+        public Class<?> caller() {
+            return sun.reflect.Reflection.getCallerClass(FIRST+1);
+        }
         
         @Override
         public List<Class> list() {
@@ -187,7 +235,77 @@ public final class StackInspector {
         }
         
     }
+    
+    private static final class ThreadResolver implements Resolver {
+        
+        @Override
+        public List<String> names() {
+            return new FrameArray(Thread.currentThread().getStackTrace(), 3);
+        }
+
+        @Override
+        public String calleeName() {
+            return Thread.currentThread().getStackTrace()[3].getClassName();
+        }
+
+        @Override
+        public String callerName() {
+            return Thread.currentThread().getStackTrace()[4].getClassName();
+        }
+        
+        @Override
+        public List<Class> list() {
+            throw new UnsupportedOperationException(NSE);
+        }
+        
+        @Override
+        public Class<?> callee() {
+            throw new UnsupportedOperationException(NSE);
+        }
+        
+        @Override
+        public Class<?> caller() {
+            throw new UnsupportedOperationException(NSE);
+        }
+        
+    }
+
+    private static final class FrameResolver implements Resolver { 
+        
+        private static final int FIRST = 2;
 
+        @Override
+        public String calleeName() {
+            return ReflectFrame.get(new Exception(), FIRST).getClassName();
+        }
+        
+        @Override
+        public String callerName() {
+            return ReflectFrame.get(new Exception(), FIRST+1).getClassName();
+        }
+        
+        @Override
+        public List<String> names() {
+            return new FrameArray(new Exception().getStackTrace(), FIRST);
+        }
+
+        @Override
+        public List<Class> list() {
+            throw new UnsupportedOperationException(NSE);
+        }
+        
+        @Override
+        public Class<?> callee() {
+            throw new UnsupportedOperationException(NSE);
+        }
+        
+        @Override
+        public Class<?> caller() {
+            throw new UnsupportedOperationException(NSE);
+        }
+        
+    }
+    
     private static final class ClassArray extends AbstractList<Class> {
         
         private final Class[] array;
@@ -275,4 +393,76 @@ public final class StackInspector {
     
     }
     
+    private static final class FrameList extends AbstractList<StackTraceElement> { 
+        
+        private static final int FIRST = 2;
+        
+        private final Throwable cause;
+        
+        private StackTraceElement[] array;
+        
+        public FrameList() {
+            this.cause = new Throwable(); // NOPMD
+            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() {
+            if(null == array) {
+                array = new StackTraceElement[ReflectFrame.depth(cause)-FIRST];
+            }
+            return array.length;
+        }
+        
+    }
+    
+    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 ExceptionInspector.rethrow(ex);
+            }
+        }
+        
+        public static int depth(Throwable that) {
+            try {
+                return (int)DEPTH.invoke(that);
+            } catch(ReflectiveOperationException ex) {
+                throw new InspectException(ex);
+            }
+        }
+        
+        public static StackTraceElement get(Throwable that, int index) {
+            try {
+
+                return (StackTraceElement)GET.invoke(that, index);
+            } catch(ReflectiveOperationException ex) {
+                ex.printStackTrace();
+                throw new InspectException(ex);
+            }
+        }
+        
+    }
+    
 }

+ 168 - 127
assira/src/test/java/net/ranides/assira/trace/StackInspectorBenchmark.java

@@ -1,127 +1,168 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-package net.ranides.assira.trace;
-
-import com.google.caliper.SimpleBenchmark;
-import net.ranides.assira.junit.BenchmarkRunner;
-
-/**
- * Benchmark for different methods of obtaining list of caller-classess.
- * 
- * SecurityManager creates list ~10 times faster than stacktrace.
- * SecurityManager gets caller  ~15 times faster than stacktrace.
- * Best if you need all classes.
- * It's portable, but it won't work if you disable SecurityManager in JVM
- * 
- * 
- * SunReflection creates list ~2 times faster that stacktrace.
- * SunReflection gets caller  ~100 times faster than stacktrace.
- * Best if you need first class.
- * It's nonportable, but it will work even if you disable SecurityManager.
- * 
- * 
- * Notice: JDK8 Stream API is very convenient but it has much too large overhead. 
- * As effect we removed old "elegant" code. It's unacceptable to sacrifice 25%
- * of execution time because we want to use functional interfaces.
- * Streams:
- *      SecurityManager     =  1.1 us
- *      SunReflection       =  4.2 us
- *      Exception           = 10.7 us
- *      Thread              = 11.3 us
- * Arrays:
- *      SecurityManager     =  0.8 us   72 %
- *      SunReflection       =  3.6 us   85 %
- *      Exception           = 10.5 us   98 %
- *      Thread              = 11.1 us   98 %
- *
- * 
- * Most recent results for JDK 1.8.0 (Lenovo G510 ranides-laptop)
- *
- *      CallerSunReflect         =   144 ? 2
- *      CallerSecurityManager    =   822 ? 6
- *      CallerException          = 12891 ? 81
- *      CallerThread             = 13668 ? 71
- * 
- *      ListSecurityManager      =   842 ? 61
- *      ListSunReflect           =  3794 ? 24
- *      ListException            = 12006 ? 44
- *      ListThread               = 12681 ? 147
- * 
- * 
- * @author Ranides Atterwim <ranides@gmail.com>
- */
-public class StackInspectorBenchmark extends SimpleBenchmark {
-    
-    public int timeListSecurityManager(int reps) {
-        int ret = 0;
-        for(int i=0; i<reps; i++) {
-            ret += StackInspector.test_getClassNames(StackInspector.RESOLVER_SM).size();
-        }
-        return ret;
-    }
-    
-    public int timeListThread(int reps) {
-        int ret = 0;
-        for(int i=0; i<reps; i++) {
-            ret += StackInspector.test_getClassNames(StackInspector.RESOLVER_TS).size();
-        }
-        return ret;
-    }
-    
-    public int timeListSunReflect(int reps) {
-        int ret = 0;
-        for(int i=0; i<reps; i++) {
-            ret += StackInspector.test_getClassNames(StackInspector.RESOLVER_SR).size();
-        }
-        return ret;
-    }
-    
-    public int timeListException(int reps) {
-        int ret = 0;
-        for(int i=0; i<reps; i++) {
-            ret += StackInspector.test_getClassNames(StackInspector.RESOLVER_ES).size();
-        }
-        return ret;
-    }
-    
-    public int timeCallerSecurityManager(int reps) {
-        int ret = 0;
-        for(int i=0; i<reps; i++) {
-            ret += StackInspector.test_getCallerClassName(StackInspector.RESOLVER_SM).length();
-        }
-        return ret;
-    }
-    
-    public int timeCallerThread(int reps) {
-        int ret = 0;
-        for(int i=0; i<reps; i++) {
-            ret += StackInspector.test_getCallerClassName(StackInspector.RESOLVER_TS).length();
-        }
-        return ret;
-    }
-    
-    public int timeCallerSunReflect(int reps) {
-        int ret = 0;
-        for(int i=0; i<reps; i++) {
-            ret += StackInspector.test_getCallerClassName(StackInspector.RESOLVER_SR).length();
-        }
-        return ret;
-    }
-    
-    public int timeCallerException(int reps) {
-        int ret = 0;
-        for(int i=0; i<reps; i++) {
-            ret += StackInspector.test_getCallerClassName(StackInspector.RESOLVER_ES).length();
-        }
-        return ret;
-    }
-    
-    public static void main(String[] args) {
-        BenchmarkRunner.run(StackInspectorBenchmark.class);
-    }
-    
-}
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.trace;
+
+import com.google.caliper.SimpleBenchmark;
+import net.ranides.assira.junit.BenchmarkRunner;
+
+/**
+ * Benchmark for different methods of obtaining list of caller-classess.
+ * 
+ * SecurityManager creates list ~10 times faster than stacktrace.
+ * SecurityManager gets caller  ~15 times faster than stacktrace.
+ * Best if you need all classes.
+ * It's portable, but it won't work if you disable SecurityManager in JVM
+ * 
+ * 
+ * SunReflection creates list ~2 times faster that stacktrace.
+ * SunReflection gets caller  ~100 times faster than stacktrace.
+ * Best if you need first class.
+ * It's nonportable, but it will work even if you disable SecurityManager.
+ * 
+ * 
+ * Notice: JDK8 Stream API is very convenient but it has too large overhead. 
+ * As effect we removed old "elegant" code. It's unacceptable to sacrifice 25%
+ * of execution time because we want to use functional interfaces.
+ * Streams:
+ *      SecurityManager     =  1.1 us
+ *      SunReflection       =  4.2 us
+ *      Exception           = 10.7 us
+ *      Thread              = 11.3 us
+ * Arrays:
+ *      SecurityManager     =  0.8 us   72 %
+ *      SunReflection       =  3.6 us   85 %
+ *      Exception           = 10.5 us   98 %
+ *      Thread              = 11.1 us   98 %
+ *
+ * 
+ * Most recent results for JDK 1.8.0 (Lenovo G510 ranides-laptop)
+ *
+ *      CallerSunReflect         =   144 ? 2
+ *      CallerSecurityManager    =   822 ? 6
+ *      CallerException          =  1946 ? 3
+ *      CallerThread             = 13668 ? 71
+ * 
+ *      ListSecurityManager      =   842 ? 61
+ *      ListSunReflect           =  3794 ? 24
+ *      ListException            = 12006 ? 44
+ *      ListThread               = 12681 ? 147
+ *
+ *      FrameNative              =  1769 ? 17
+ *      FrameDefault             =  9903 ? 16
+ *      FrameListNative          = 10265 ? 45 
+ *      FrameListDefault         = 10043 ? 20
+ * 
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class StackInspectorBenchmark extends SimpleBenchmark {
+    
+    public int timeFrameNative(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.getFrames().get(1).getLineNumber();
+        }
+        return ret;
+    }
+    
+    
+    public int timeFrameDefault(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.test_getFramesArray().get(1).getLineNumber();
+        }
+        return ret;
+    }
+    
+     public int timeFrameListNative(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            for(StackTraceElement e : StackInspector.getFrames()) {
+                ret += e.getLineNumber();
+            }
+        }
+        return ret;
+    }
+    
+    public int timeFrameListDefault(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            for(StackTraceElement e : StackInspector.test_getFramesArray()) {
+                ret += e.getLineNumber();
+            }
+        }
+        return ret;
+    }
+    
+    public int timeListSecurityManager(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.test_getClassNames(StackInspector.RESOLVER_SM).size();
+        }
+        return ret;
+    }
+    
+    public int timeListThread(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.test_getClassNames(StackInspector.RESOLVER_TS).size();
+        }
+        return ret;
+    }
+    
+    public int timeListSunReflect(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.test_getClassNames(StackInspector.RESOLVER_SR).size();
+        }
+        return ret;
+    }
+    
+    public int timeListException(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.test_getClassNames(StackInspector.RESOLVER_ES).size();
+        }
+        return ret;
+    }
+    
+    public int timeCallerSecurityManager(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.test_getCalleeName(StackInspector.RESOLVER_SM).length();
+        }
+        return ret;
+    }
+    
+    public int timeCallerThread(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.test_getCalleeName(StackInspector.RESOLVER_TS).length();
+        }
+        return ret;
+    }
+    
+    public int timeCallerSunReflect(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.test_getCalleeName(StackInspector.RESOLVER_SR).length();
+        }
+        return ret;
+    }
+    
+    public int timeCallerException(int reps) {
+        int ret = 0;
+        for(int i=0; i<reps; i++) {
+            ret += StackInspector.test_getCalleeName(StackInspector.RESOLVER_ES).length();
+        }
+        return ret;
+    }
+    
+    public static void main(String[] args) {
+        BenchmarkRunner.run(StackInspectorBenchmark.class);
+    }
+    
+}

+ 150 - 124
assira/src/test/java/net/ranides/assira/trace/StackInspectorTest.java

@@ -1,124 +1,150 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-package net.ranides.assira.trace;
-
-import java.util.List;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- *
- * @author Ranides Atterwim <ranides@gmail.com>
- */
-public class StackInspectorTest {
-    
-    private static final Class<?> CALLER = StackInspectorTest.class;
-    
-    @Test
-    public void testCallerClass() {
-        Class callerAuto = StackInspector.getCaller();
-        Class callerSM   = StackInspector.test_getCallerClass(StackInspector.RESOLVER_SM);
-        Class callerSR   = StackInspector.test_getCallerClass(StackInspector.RESOLVER_SR);
-                
-        Assert.assertEquals(CALLER, callerAuto);
-        Assert.assertEquals(CALLER, callerSM);
-        Assert.assertEquals(CALLER, callerSR);
-    }
-    
-    @Test
-    public void testCallerClassName() {
-        String callerAuto = StackInspector.getCallerName();
-        String callerSM   = StackInspector.test_getCallerClassName(StackInspector.RESOLVER_SM);
-        String callerSR   = StackInspector.test_getCallerClassName(StackInspector.RESOLVER_SR);
-        String callerTS   = StackInspector.test_getCallerClassName(StackInspector.RESOLVER_TS);
-        String callerES   = StackInspector.test_getCallerClassName(StackInspector.RESOLVER_ES);
-        
-        Assert.assertEquals(CALLER.getName(), callerAuto);
-        Assert.assertEquals(CALLER.getName(), callerSM);
-        Assert.assertEquals(CALLER.getName(), callerSR);
-        Assert.assertEquals(CALLER.getName(), callerTS);
-        Assert.assertEquals(CALLER.getName(), callerES);
-    }
-
-    @Test
-    public void testClassList() {
-        List<Class> listAuto = StackInspector.getCallers();
-        List<Class> listSM   = StackInspector.test_getClassList(StackInspector.RESOLVER_SM);
-        List<Class> listSR   = StackInspector.test_getClassList(StackInspector.RESOLVER_SR);
-        
-        if(false) { //NOPMD
-            dump(listAuto);
-            dump(listSM);
-            dump(listSR);
-        }
-        
-        Assert.assertTrue(
-            listAuto.equals(listSM) ||
-            listAuto.equals(listSR)
-        );
-
-        Assert.assertEquals(listSM, listSR);
-        
-        Assert.assertTrue(listSM.size() > 5);
-        
-        Assert.assertEquals(CALLER, listSM.get(0));
-        Assert.assertEquals(CALLER, listSR.get(0));
-        
-        Assert.assertFalse(CALLER.equals(listSM.get(1)));
-        Assert.assertFalse(CALLER.equals(listSR.get(1)));
-    }
-
-    @Test
-    public void testClassNames() {
-        List<String> listAuto = StackInspector.getCallerNames();
-        List<String> listSM  = StackInspector.test_getClassNames(StackInspector.RESOLVER_SM);
-        List<String> listSR  = StackInspector.test_getClassNames(StackInspector.RESOLVER_SR);
-        List<String> listTS  = StackInspector.test_getClassNames(StackInspector.RESOLVER_TS);
-        List<String> listES  = StackInspector.test_getClassNames(StackInspector.RESOLVER_ES);
-        
-        if(false) { //NOPMD
-            dump(listAuto);
-            dump(listSM);
-            dump(listSR);
-            dump(listTS);
-            dump(listES);
-        }
-        
-        Assert.assertTrue(
-            listAuto.equals(listSM) ||
-            listAuto.equals(listSR) ||
-            listAuto.equals(listTS) ||
-            listAuto.equals(listES) 
-        );
-        
-        Assert.assertEquals(listSM, listSR);
-        Assert.assertEquals(listTS, listES);
-        
-        Assert.assertTrue(listSM.size() > 5);
-        Assert.assertTrue(listSM.size() <= listTS.size());
-        
-        Assert.assertEquals(CALLER.getName(), listSM.get(0));
-        Assert.assertEquals(CALLER.getName(), listSR.get(0));
-        Assert.assertEquals(CALLER.getName(), listTS.get(0));
-        Assert.assertEquals(CALLER.getName(), listES.get(0));
-        
-        Assert.assertFalse(CALLER.getName().equals(listSM.get(1)));
-        Assert.assertFalse(CALLER.getName().equals(listSR.get(1)));
-        Assert.assertFalse(CALLER.getName().equals(listTS.get(1)));
-        Assert.assertFalse(CALLER.getName().equals(listES.get(1)));
-    }
-    
-    @SuppressWarnings("PMD")
-    private static void dump(List<?> list) {
-        int i = 0;
-        for(Object c : list) {
-            System.err.printf("%d. %s%n", i++, c);
-        }
-        System.out.printf("%n");
-    }
-    
-}
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.trace;
+
+import java.util.List;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class StackInspectorTest {
+    
+    private static final Class<?> CALLER = StackInspectorTest.class;
+    
+    private static StackTraceElement innerFrame(int index) {
+        return StackInspector.getFrames().get(index);
+    }
+    
+    private static List<StackTraceElement> innerFrames() {
+        return StackInspector.getFrames();
+    }
+    
+    @Test
+    public void testCallerClass() {
+        Class callerAuto = StackInspector.getCalleeType();
+        Class callerSM   = StackInspector.test_getCalleeClass(StackInspector.RESOLVER_SM);
+        Class callerSR   = StackInspector.test_getCalleeClass(StackInspector.RESOLVER_SR);
+                
+        assertEquals(CALLER, callerAuto);
+        assertEquals(CALLER, callerSM);
+        assertEquals(CALLER, callerSR);
+    }
+    
+    @Test
+    public void testCallerClassName() {
+        String callerAuto = StackInspector.getCalleeName();
+        String callerSM   = StackInspector.test_getCalleeName(StackInspector.RESOLVER_SM);
+        String callerSR   = StackInspector.test_getCalleeName(StackInspector.RESOLVER_SR);
+        String callerTS   = StackInspector.test_getCalleeName(StackInspector.RESOLVER_TS);
+        String callerES   = StackInspector.test_getCalleeName(StackInspector.RESOLVER_ES);
+        
+        assertEquals(CALLER.getName(), callerAuto);
+        assertEquals(CALLER.getName(), callerSM);
+        assertEquals(CALLER.getName(), callerSR);
+        assertEquals(CALLER.getName(), callerTS);
+        assertEquals(CALLER.getName(), callerES);
+    }
+
+    @Test
+    public void testClassList() {
+        List<Class> listAuto = StackInspector.getTypes();
+        List<Class> listSM   = StackInspector.test_getClassList(StackInspector.RESOLVER_SM);
+        List<Class> listSR   = StackInspector.test_getClassList(StackInspector.RESOLVER_SR);
+        
+        if(false) { //NOPMD
+            dump(listAuto);
+            dump(listSM);
+            dump(listSR);
+        }
+        
+        assertTrue(
+            listAuto.equals(listSM) ||
+            listAuto.equals(listSR)
+        );
+
+        assertEquals(listSM, listSR);
+        
+        assertTrue(listSM.size() > 5);
+        
+        assertEquals(CALLER, listSM.get(0));
+        assertEquals(CALLER, listSR.get(0));
+        
+        assertFalse(CALLER.equals(listSM.get(1)));
+        assertFalse(CALLER.equals(listSR.get(1)));
+    }
+
+    @Test
+    public void testClassNames() {
+        List<String> listAuto = StackInspector.getNames();
+        List<String> listSM  = StackInspector.test_getClassNames(StackInspector.RESOLVER_SM);
+        List<String> listSR  = StackInspector.test_getClassNames(StackInspector.RESOLVER_SR);
+        List<String> listTS  = StackInspector.test_getClassNames(StackInspector.RESOLVER_TS);
+        List<String> listES  = StackInspector.test_getClassNames(StackInspector.RESOLVER_ES);
+        
+        if(false) { //NOPMD
+            dump(listAuto);
+            dump(listSM);
+            dump(listSR);
+            dump(listTS);
+            dump(listES);
+        }
+        
+        assertTrue(
+            listAuto.equals(listSM) ||
+            listAuto.equals(listSR) ||
+            listAuto.equals(listTS) ||
+            listAuto.equals(listES) 
+        );
+        
+        assertEquals(listSM, listSR);
+        assertEquals(listTS, listES);
+        
+        assertTrue(listSM.size() > 5);
+        assertTrue(listSM.size() <= listTS.size());
+        
+        assertEquals(CALLER.getName(), listSM.get(0));
+        assertEquals(CALLER.getName(), listSR.get(0));
+        assertEquals(CALLER.getName(), listTS.get(0));
+        assertEquals(CALLER.getName(), listES.get(0));
+        
+        assertFalse(CALLER.getName().equals(listSM.get(1)));
+        assertFalse(CALLER.getName().equals(listSR.get(1)));
+        assertFalse(CALLER.getName().equals(listTS.get(1)));
+        assertFalse(CALLER.getName().equals(listES.get(1)));
+    }
+    
+    @Test
+    public void testFrame() {
+        String t1 = "net.ranides.assira.trace.StackInspectorTest.innerFrame(StackInspectorTest.java:22)";
+        String t2 = "net.ranides.assira.trace.StackInspectorTest.testFrame(StackInspectorTest.java:128)";
+        assertEquals(t1, innerFrame(0).toString());
+        assertEquals(t2, innerFrame(1).toString());
+        assertFalse(innerFrame(2).toString().startsWith("net.ranides"));
+    }
+    
+    @Test
+    public void testFrames() {
+        String t1 = "net.ranides.assira.trace.StackInspectorTest.innerFrames(StackInspectorTest.java:26)";
+        String t2 = "net.ranides.assira.trace.StackInspectorTest.testFrames(StackInspectorTest.java:137)";
+        assertEquals(t1, innerFrames().get(0).toString());
+        assertEquals(t2, innerFrames().get(1).toString());
+        assertFalse(innerFrames().get(2).toString().startsWith("net.ranides"));
+    }
+    
+    @SuppressWarnings("PMD")
+    private static void dump(List<?> list) {
+        int i = 0;
+        for(Object c : list) {
+            System.err.printf("%d. %s%n", i++, c);
+        }
+        System.out.printf("%n");
+    }
+    
+}