JDK-8173898 : StackWalker.walk throws InternalError if called from a constructor invoked through reflection.
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 9
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2017-02-03
  • Updated: 2017-05-17
  • Resolved: 2017-02-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 10 JDK 9
10Fixed 9 b157Fixed
Related Reports
Blocks :  
Description
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:466)
	at Test.main(Test.java:45)
Caused by: java.lang.InternalError: Not jdk.internal.reflect.MethodAccessor: class jdk.internal.reflect.NativeConstructorAccessorImpl
	at java.base/java.lang.StackStreamFactory.isReflectionFrame(StackStreamFactory.java:984)
	at java.base/java.lang.StackStreamFactory.access$000(StackStreamFactory.java:59)
	at java.base/java.lang.StackStreamFactory$AbstractStackWalker.peekFrame(StackStreamFactory.java:264)
	at java.base/java.lang.StackStreamFactory$AbstractStackWalker.hasNext(StackStreamFactory.java:349)
	at java.base/java.lang.StackStreamFactory$StackFrameTraverser.tryAdvance(StackStreamFactory.java:591)

Comments
Daniel and I discussed this and agree to include Class::newInstance as a reflection frame in addition to Constructor::newInstance although it is a deprecated and utility method. Class::newInstance has been widely used and having every client of StackWalker API to special case is inconvenient. We should have some assertion in the code to catch if Class.newInstance implementation is updated that Class::newInstance is not the immediate frame following Constructor::newInstance.
07-02-2017

Taking a second look at the code, I now understand why Class::newInstance is not filtered: In the case of StackWalker::getCallerClass(), the only information we have is the Class<?> object on the stack. We don't know which method was called, and we probably should not have to care. So we can't simply filter out Class.class, that would be wrong. So now I believe we should not attempt to filter out Class::newInstance when SHOW_REFLECT_FRAMES is Off, even if Class::newInstance conceptually *is* a reflection frame. I think we should ascribe that to one more reason why Class::newInstance is @Deprecated, and why we should stay away from it.
07-02-2017

http://cr.openjdk.java.net/~dfuchs/webrev_8173898/webrev.01/
03-02-2017

And the test... static class Dummy { static final StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); public Dummy() { walker.walk(this::print); } Void print(Stream<StackFrame> s) { s.forEach((f) -> System.out.println(f.toString())); return null; } static Dummy create() throws Exception { return Dummy.class.getConstructor().newInstance(); } }
03-02-2017

Simple fix - but needs to write a test. diff --git a/src/java.base/share/classes/java/lang/StackStreamFactory.java b/src/java.base/share/classes/java/lang/StackStreamFactory.java --- a/src/java.base/share/classes/java/lang/StackStreamFactory.java +++ b/src/java.base/share/classes/java/lang/StackStreamFactory.java @@ -25,6 +25,7 @@ package java.lang; import jdk.internal.reflect.MethodAccessor; +import jdk.internal.reflect.ConstructorAccessor; import java.lang.StackWalker.Option; import java.lang.StackWalker.StackFrame; @@ -980,8 +981,10 @@ private static boolean isReflectionFrame(Class<?> c) { if (c.getName().startsWith("jdk.internal.reflect") && - !MethodAccessor.class.isAssignableFrom(c)) { - throw new InternalError("Not jdk.internal.reflect.MethodAccessor: " + c.toString()); + !MethodAccessor.class.isAssignableFrom(c) && + !ConstructorAccessor.class.isAssignableFrom(c)) { + throw new InternalError("Not jdk.internal.reflect.MethodAccessor or jdk.internal.reflect.ConstructorAccessor: " + + c.toString()); } // ## should filter all @Hidden frames? return c == Method.class ||
03-02-2017