FULL PRODUCT VERSION :
Happens on all Java version 8u60 and later; I'm currently using:
openjdk version "1.8.0_91"
OpenJDK Runtime Environment (build 1.8.0_91-b14)
OpenJDK 64-Bit Server VM (build 25.91-b14, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Happens on all OS's, here's the one I'm currently using:
Linux candrews-l1 4.5.5-300.fc24.x86_64 #1 SMP Thu May 19 13:05:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
The javadoc for java.lang.SecurityManager.getClassContext() states:
"The length of the array is the number of methods on the execution stack. The element at index 0 is the class of the currently executing method, the element at index 1 is the class of that method's caller, and so on." - https://docs.oracle.com/javase/8/docs/api/java/lang/SecurityManager.html#getClassContext--
However, java.lang.Throwable.getStackTrace() filters out generated lambda classes as of Java 8u60, see http://bugs.java.com/view_bug.do?bug_id=8025636 Since java.lang.SecurityManager.getClassContext() does not filter out generated lambda classes, the javadoc statement that getClassContext() matches the call stack does not hold true.
REGRESSION. Last worked in version 8u73
ADDITIONAL REGRESSION INFORMATION:
java.lang.SecurityManager.getClassContext() aligns with java.lang.Throwable.getStackTrace() when called within a lambda in any Java version before 8u60 as documented at http://bugs.java.com/view_bug.do?bug_id=8025636
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the test case I've provided in the answer to the "Source code for an executable test case" question.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test case should output:
==========Testing without lambdas==========
Bug is NOT present
==========Testing with lambdas==========
Bug is NOT present
ACTUAL -
The test case actually outputs (on Java 8u60 and later):
==========Testing without lambdas==========
Bug is NOT present
==========Testing with lambdas==========
BUG IS PRESENT
Stacktrace length: 4
Class Context length: 6
Stacktrace:
Test.testStackTraceLengthMatchesClassContextLength(Test.java:24)
Test.lambda$0(Test.java:19)
java.lang.Thread.run(Thread.java:745)
Test.main(Test.java:20)
Class Context:
class Test$ClassContextSecurityManager
class Test
class Test
class Test$$Lambda$1/791452441
class java.lang.Thread
class Test
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.util.Arrays;
public class Test {
private static class ClassContextSecurityManager extends SecurityManager {
@Override
public Class[] getClassContext() {
return super.getClassContext();
}
}
public static void main(String[] args) {
// first, show the bug is not present when there aren't lambdas in the
// call stack
System.out.println("==========Testing without lambdas==========");
testStackTraceLengthMatchesClassContextLength();
new Thread(() -> {
System.out.println("==========Testing with lambdas==========");
testStackTraceLengthMatchesClassContextLength();
}).run();
}
private static void testStackTraceLengthMatchesClassContextLength() {
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
Class[] classContext = new ClassContextSecurityManager().getClassContext();
// class context will always include ClassContextSecurityManager,
// which is not in the stack trace, so the length of the class
// context array should always be 1 more than the stack trace length
if (stackTrace.length == classContext.length - 1) {
System.out.println("Bug is NOT present");
} else {
System.out.println("BUG IS PRESENT");
System.out.println("Stacktrace length: " + stackTrace.length);
System.out.println("Class Context length: " + classContext.length);
System.out.println("Stacktrace:");
Arrays.stream(stackTrace).forEach(System.out::println);
System.out.println("Class Context:");
Arrays.stream(classContext).forEach(System.out::println);
}
}
}
---------- END SOURCE ----------