8173898: StackWalker.walk throws InternalError if called from a constructor invoked through reflection.
authordfuchs
Wed, 08 Feb 2017 16:33:52 +0000
changeset 43694 fcc6fff17bfa
parent 43693 e340c8d10cbb
child 43695 2a7ed4c3b8c7
child 43698 e3e3ae809ab4
8173898: StackWalker.walk throws InternalError if called from a constructor invoked through reflection. Summary: StackWalker is fixed to take into account constructor reflection frames. The specification of StackWalker.Option.SHOW_REFLECT_FRAMES is also clarified in this regard. Reviewed-by: bchristi, lancea, mchung, psandoz
jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java
jdk/src/java.base/share/classes/java/lang/StackWalker.java
jdk/test/java/lang/StackWalker/Basic.java
jdk/test/java/lang/StackWalker/ReflectionFrames.java
--- a/jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java	Wed Feb 08 08:40:25 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java	Wed Feb 08 16:33:52 2017 +0000
@@ -25,11 +25,13 @@
 package java.lang;
 
 import jdk.internal.reflect.MethodAccessor;
+import jdk.internal.reflect.ConstructorAccessor;
 import java.lang.StackWalker.Option;
 import java.lang.StackWalker.StackFrame;
 
 import java.lang.annotation.Native;
 import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
 import java.util.HashSet;
 import java.util.NoSuchElementException;
 import java.util.Objects;
@@ -922,7 +924,8 @@
          */
         final void setBatch(int depth, int startIndex, int endIndex) {
             if (startIndex <= 0 || endIndex <= 0)
-                throw new IllegalArgumentException("startIndex=" + startIndex + " endIndex=" + endIndex);
+                throw new IllegalArgumentException("startIndex=" + startIndex
+                        + " endIndex=" + endIndex);
 
             this.origin = startIndex;
             this.fence = endIndex;
@@ -980,13 +983,18 @@
 
     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 ||
-                MethodAccessor.class.isAssignableFrom(c) ||
-                c.getName().startsWith("java.lang.invoke.LambdaForm");
+               c == Constructor.class ||
+               MethodAccessor.class.isAssignableFrom(c) ||
+               ConstructorAccessor.class.isAssignableFrom(c) ||
+               c.getName().startsWith("java.lang.invoke.LambdaForm");
     }
 
 }
--- a/jdk/src/java.base/share/classes/java/lang/StackWalker.java	Wed Feb 08 08:40:25 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/StackWalker.java	Wed Feb 08 16:33:52 2017 +0000
@@ -29,6 +29,7 @@
 import java.util.*;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.stream.Stream;
 
 /**
@@ -207,13 +208,23 @@
         /**
          * Shows all reflection frames.
          *
-         * <p>By default, reflection frames are hidden.  This includes the
-         * {@link java.lang.reflect.Method#invoke} method
-         * and the reflection implementation classes. A {@code StackWalker} with
-         * this {@code SHOW_REFLECT_FRAMES} option will show all reflection frames.
-         * The {@link #SHOW_HIDDEN_FRAMES} option can also be used to show all
+         * <p>By default, reflection frames are hidden.  A {@code StackWalker}
+         * configured with this {@code SHOW_REFLECT_FRAMES} option
+         * will show all reflection frames that
+         * include {@link java.lang.reflect.Method#invoke} and
+         * {@link java.lang.reflect.Constructor#newInstance(Object...)}
+         * and their reflection implementation classes.
+         *
+         * <p>The {@link #SHOW_HIDDEN_FRAMES} option can also be used to show all
          * reflection frames and it will also show other hidden frames that
          * are implementation-specific.
+         *
+         * @apiNote
+         * This option includes the stack frames representing the invocation of
+         * {@code Method} and {@code Constructor}.  Any utility methods that
+         * are equivalent to calling {@code Method.invoke} or
+         * {@code Constructor.newInstance} such as {@code Class.newInstance}
+         * are not filtered or controlled by any stack walking option.
          */
         SHOW_REFLECT_FRAMES,
         /**
@@ -468,8 +479,9 @@
      * Gets the {@code Class} object of the caller invoking the method
      * that calls this {@code getCallerClass} method.
      *
-     * <p> Reflection frames, {@link java.lang.invoke.MethodHandle}, and
-     * hidden frames are filtered regardless of the
+     * <p> This method filters {@linkplain Option#SHOW_REFLECT_FRAMES reflection
+     * frames}, {@link java.lang.invoke.MethodHandle}, and
+     * {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} regardless of the
      * {@link Option#SHOW_REFLECT_FRAMES SHOW_REFLECT_FRAMES}
      * and {@link Option#SHOW_HIDDEN_FRAMES SHOW_HIDDEN_FRAMES} options
      * this {@code StackWalker} has been configured with.
--- a/jdk/test/java/lang/StackWalker/Basic.java	Wed Feb 08 08:40:25 2017 +0000
+++ b/jdk/test/java/lang/StackWalker/Basic.java	Wed Feb 08 16:33:52 2017 +0000
@@ -23,18 +23,21 @@
 
 /*
  * @test
- * @bug 8140450
+ * @bug 8140450 8173898
  * @summary Basic test for the StackWalker::walk method
  * @run testng Basic
  */
 
 import java.lang.StackWalker.StackFrame;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import static java.lang.StackWalker.Option.*;
 
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
+import static org.testng.Assert.*;
 
 public class Basic {
     private static boolean verbose = false;
@@ -60,6 +63,17 @@
         }
     }
 
+    @Test
+    public static void testWalkFromConstructor() throws Exception {
+        System.out.println("testWalkFromConstructor:");
+        List<String> found = ((ConstructorNewInstance)ConstructorNewInstance.class.getMethod("create")
+                             .invoke(null)).collectedFrames();
+        assertEquals(List.of(ConstructorNewInstance.class.getName()+"::<init>",
+                             ConstructorNewInstance.class.getName()+"::create",
+                             Basic.class.getName()+"::testWalkFromConstructor"),
+                     found);
+    }
+
     private final int depth;
     Basic(int depth) {
         this.depth = depth;
@@ -77,6 +91,47 @@
         assertEquals(limit, frames.size());
     }
 
+    static class ConstructorNewInstance {
+        static final StackWalker walker =
+            StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
+        List<String> testFramesOrReflectionFrames;
+        public ConstructorNewInstance() {
+            testFramesOrReflectionFrames = walker.walk(this::parse);
+        }
+        public List<String> collectedFrames() {
+            return testFramesOrReflectionFrames;
+        }
+        public boolean accept(StackFrame f) {
+            // Frames whose class names don't contain "."
+            // are our own test frames. These are the ones
+            // we expect.
+            // Frames whose class names contain ".reflect."
+            // are reflection frames. None should be present,
+            // since they are supposed to be filtered by
+            // by StackWalker. If we find any, we want to fail.
+            if (!f.getClassName().contains(".")
+                || f.getClassName().contains(".reflect.")) {
+                System.out.println("    " + f);
+                return true;
+            }
+            // Filter out all other frames (in particular
+            // those from the test framework) in order to
+            // have predictable results.
+            return false;
+        }
+        public String frame(StackFrame f) {
+            return f.getClassName() + "::" + f.getMethodName();
+        }
+        List<String> parse(Stream<StackFrame> s) {
+            return s.filter(this::accept)
+                    .map(this::frame)
+                    .collect(Collectors.toList());
+        }
+        public static ConstructorNewInstance create() throws Exception {
+            return ConstructorNewInstance.class.getConstructor().newInstance();
+        }
+    }
+
     class StackBuilder {
         private final int stackDepth;
         private final int limit;
@@ -131,9 +186,4 @@
         }
     }
 
-    static void assertEquals(int x, int y) {
-        if (x != y) {
-            throw new RuntimeException(x + " != " + y);
-        }
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/ReflectionFrames.java	Wed Feb 08 16:33:52 2017 +0000
@@ -0,0 +1,842 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8173898
+ * @summary Basic test for checking filtering of reflection frames
+ * @run testng ReflectionFrames
+ */
+
+import java.lang.StackWalker.StackFrame;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import static java.lang.StackWalker.Option.*;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class ReflectionFrames {
+    final static boolean verbose = false;
+
+    /**
+     * This test invokes new StackInspector() directly from
+     * the caller StackInspector.Caller.create method.
+     * It checks that the caller is StackInspector.Caller.
+     * It also checks the expected frames collected
+     * by walking the stack from the default StackInspector()
+     * constructor.
+     * This is done twice, once using a default StackWalker
+     * that hides reflection frames, once using a StackWalker
+     * configured to show reflection frames.
+     */
+    @Test
+    public static void testNewStackInspector() throws Exception {
+        // Sets the default walker which hides reflection
+        // frames.
+        StackInspector.walker.set(StackInspector.walkerHide);
+
+        // Calls the StackInspector.create method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The create method invokes new StackInspector() directly.
+        // No reflection frame should appear.
+        System.out.println("testNewStackInspector: create");
+
+        StackInspector obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("create", How.class)
+                             .invoke(null, How.NEW));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewStackInspector"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.reflect method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The reflect method invokes the create method through
+        // reflection.
+        // The create method invokes new StackInspector() directly.
+        // No reflection frame should appear.
+        System.out.println("testNewStackInspector: reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("reflect", How.class)
+                             .invoke(null, How.NEW));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             StackInspector.Caller.class.getName()
+                                 +"::reflect",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewStackInspector"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.handle method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The handle method invokes the create method using
+        // a MethodHandle.
+        // The create method invokes new StackInspector() directly.
+        // No reflection frame should appear.
+        System.out.println("testNewStackInspector: handle");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("handle", How.class)
+                             .invoke(null, How.NEW));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             StackInspector.Caller.class.getName()
+                                 +"::handle",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewStackInspector"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertEquals(obj.filtered, 0);
+
+        // Sets a non-default walker configured to show
+        // reflection frames
+        StackInspector.walker.set(StackInspector.walkerShow);
+
+        // Calls the StackInspector.create method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The create method invokes new StackInspector() directly.
+        // We should see all reflection frames, except the
+        // jdk.internal.reflect frames which we are filtering
+        // out in StackInspector::filter.
+        System.out.println("testNewStackInspector: create: show reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("create", How.class)
+                             .invoke(null, How.NEW));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             Method.class.getName()
+                                 +"::invoke",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewStackInspector"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertNotEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.reflect method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The reflect method invokes the create method through
+        // reflection.
+        // The create method invokes new StackInspector() directly.
+        // We should see all reflection frames, except the
+        // jdk.internal.reflect frames which we are filtering
+        // out in StackInspector::filter.
+        System.out.println("testNewStackInspector: reflect: show reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("reflect", How.class)
+                             .invoke(null, How.NEW));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             Method.class.getName()
+                                 +"::invoke",
+                             StackInspector.Caller.class.getName()
+                                 +"::reflect",
+                             Method.class.getName()
+                                 +"::invoke",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewStackInspector"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertNotEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.handle method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The handle method invokes the create method using
+        // MethodHandle.
+        // The create method invokes new StackInspector() directly.
+        // We should see all reflection frames, except the
+        // jdk.internal.reflect frames which we are filtering
+        // out in StackInspector::filter.
+        System.out.println("testNewStackInspector: handle: show reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("handle", How.class)
+                             .invoke(null, How.NEW));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             // MethodHandle::invoke remains hidden
+                             StackInspector.Caller.class.getName()
+                                 +"::handle",
+                             Method.class.getName()
+                                 +"::invoke",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewStackInspector"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertNotEquals(obj.filtered, 0);
+    }
+
+   /**
+     * This test invokes Constructor.newInstance() from
+     * the caller StackInspector.Caller.create method.
+     * It checks that the caller is StackInspector.Caller.
+     * It also checks the expected frames collected
+     * by walking the stack from the default StackInspector()
+     * constructor.
+     * This is done twice, once using a default StackWalker
+     * that hides reflection frames, once using a StackWalker
+     * configured to show reflection frames.
+     */
+    @Test
+    public static void testConstructor() throws Exception {
+        // Sets the default walker which hides reflection
+        // frames.
+        StackInspector.walker.set(StackInspector.walkerHide);
+
+        // Calls the StackInspector.create method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The create method invokes Constructor.newInstance().
+        // No reflection frame should appear.
+        System.out.println("testConstructor: create");
+
+        StackInspector obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("create", How.class)
+                             .invoke(null, How.CONSTRUCTOR));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             ReflectionFrames.class.getName()
+                                 +"::testConstructor"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.reflect method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The reflect method invokes the create method through
+        // reflection.
+        // The create method invokes Constructor.newInstance().
+        // No reflection frame should appear.
+        System.out.println("testConstructor: reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("reflect", How.class)
+                             .invoke(null, How.CONSTRUCTOR));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             StackInspector.Caller.class.getName()
+                                 +"::reflect",
+                             ReflectionFrames.class.getName()
+                                 +"::testConstructor"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.handle method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The handle method invokes the create method using
+        // MethodHandle.
+        // The create method invokes Constructor.newInstance().
+        // No reflection frame should appear.
+        System.out.println("testConstructor: handle");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("handle", How.class)
+                             .invoke(null, How.CONSTRUCTOR));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             StackInspector.Caller.class.getName()
+                                 +"::handle",
+                             ReflectionFrames.class.getName()
+                                 +"::testConstructor"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertEquals(obj.filtered, 0);
+
+        // Sets a non-default walker configured to show
+        // reflection frames
+        StackInspector.walker.set(StackInspector.walkerShow);
+
+        // Calls the StackInspector.create method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The create method invokes Constructor.newInstance().
+        // We should see all reflection frames, except the
+        // jdk.internal.reflect frames which we are filtering
+        // out in StackInspector::filter.
+        System.out.println("testConstructor: create: show reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("create", How.class)
+                             .invoke(null, How.CONSTRUCTOR));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             Constructor.class.getName()
+                                 +"::newInstance",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             Method.class.getName()
+                                 +"::invoke",
+                             ReflectionFrames.class.getName()
+                                 +"::testConstructor"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertNotEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.reflect method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The reflect method invokes the create method through
+        // reflection.
+        // The create method invokes Constructor.newInstance().
+        // We should see all reflection frames, except the
+        // jdk.internal.reflect frames which we are filtering
+        // out in StackInspector::filter.
+        System.out.println("testConstructor: reflect: show reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("reflect", How.class)
+                             .invoke(null, How.CONSTRUCTOR));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             Constructor.class.getName()
+                                 +"::newInstance",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             Method.class.getName()
+                                 +"::invoke",
+                             StackInspector.Caller.class.getName()
+                                 +"::reflect",
+                             Method.class.getName()
+                                 +"::invoke",
+                             ReflectionFrames.class.getName()
+                                 +"::testConstructor"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertNotEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.handle method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The handle method invokes the create method using
+        // MethodHandle.
+        // The create method invokes Constructor.newInstance().
+        // We should see all reflection frames, except the
+        // jdk.internal.reflect frames which we are filtering
+        // out in StackInspector::filter.
+        System.out.println("testConstructor: handle: show reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("handle", How.class)
+                             .invoke(null, How.CONSTRUCTOR));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             Constructor.class.getName()
+                                 +"::newInstance",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             // MethodHandle::invoke remains hidden
+                             StackInspector.Caller.class.getName()
+                                 +"::handle",
+                             Method.class.getName()
+                                 +"::invoke",
+                             ReflectionFrames.class.getName()
+                                 +"::testConstructor"));
+        assertEquals(obj.cls, StackInspector.Caller.class);
+        assertNotEquals(obj.filtered, 0);
+    }
+
+   /**
+     * This test invokes StackInspector.class.newInstance() from
+     * the caller StackInspector.Caller.create method. Because
+     * Class.newInstance() is not considered as a
+     * reflection frame, the the caller returned by
+     * getCallerClass() should appear to be java.lang.Class
+     * and not StackInspector.Caller.
+     * It also checks the expected frames collected
+     * by walking the stack from the default StackInspector()
+     * constructor.
+     * This is done twice, once using a default StackWalker
+     * that hides reflection frames, once using a StackWalker
+     * configured to show reflection frames.
+     */
+    @Test
+    public static void testNewInstance() throws Exception {
+        // Sets the default walker which hides reflection
+        // frames.
+        StackInspector.walker.set(StackInspector.walkerHide);
+
+        // Calls the StackInspector.create method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The create method invokes StackInspector.class.newInstance().
+        // No reflection frame should appear, except
+        // Class::newInstance which is not considered as
+        // a reflection frame.
+        System.out.println("testNewInstance: create");
+
+        StackInspector obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("create", How.class)
+                             .invoke(null, How.CLASS));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             Class.class.getName()
+                                 +"::newInstance",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewInstance"));
+        // Because Class.newInstance is not filtered, then the
+        // caller is Class.class
+        assertEquals(obj.cls, Class.class);
+        assertEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.reflect method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The reflect method invokes the create method through
+        // reflection.
+        // The create method invokes StackInspector.class.newInstance().
+        // No reflection frame should appear, except
+        // Class::newInstance which is not considered as
+        // a reflection frame.
+        System.out.println("testNewInstance: reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("reflect", How.class)
+                             .invoke(null, How.CLASS));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             Class.class.getName()
+                                 +"::newInstance",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             StackInspector.Caller.class.getName()
+                                 +"::reflect",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewInstance"));
+
+        // Because Class.newInstance is not filtered, then the
+        // caller is Class.class
+        assertEquals(obj.cls, Class.class);
+        assertEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.handle method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The handle method invokes the create method using
+        // reflection.
+        // The create method invokes StackInspector.class.newInstance().
+        // No reflection frame should appear, except
+        // Class::newInstance which is not considered as
+        // a reflection frame.
+        System.out.println("testNewInstance: handle");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("handle", How.class)
+                             .invoke(null, How.CLASS));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             Class.class.getName()
+                                 +"::newInstance",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             StackInspector.Caller.class.getName()
+                                 +"::handle",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewInstance"));
+
+        // Because Class.newInstance is not filtered, then the
+        // caller is Class.class
+        assertEquals(obj.cls, Class.class);
+        assertEquals(obj.filtered, 0);
+
+        // Sets a non-default walker configured to show
+        // reflection frames
+        StackInspector.walker.set(StackInspector.walkerShow);
+
+        // Calls the StackInspector.create method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The create method invokes StackInspector.class.newInstance().
+        // We should see all reflection frames, except the
+        // jdk.internal.reflect frames which we are filtering
+        // out in StackInspector::filter.
+        System.out.println("testNewInstance: create: show reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("create", How.class)
+                             .invoke(null, How.CLASS));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             Constructor.class.getName()
+                                 +"::newInstance",
+                             Class.class.getName()
+                                 +"::newInstance",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             Method.class.getName()
+                                 +"::invoke",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewInstance"));
+        // Because Class.newInstance is not filtered, then the
+        // caller is Class.class
+        assertEquals(obj.cls, Class.class);
+        assertNotEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.reflect method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The reflect method invokes the create method through
+        // reflection.
+        // The create method invokes StackInspector.class.newInstance().
+        // We should see all reflection frames, except the
+        // jdk.internal.reflect frames which we are filtering
+        // out in StackInspector::filter.
+        System.out.println("testNewInstance: reflect: show reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("reflect", How.class)
+                             .invoke(null, How.CLASS));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             Constructor.class.getName()
+                                 +"::newInstance",
+                             Class.class.getName()
+                                 +"::newInstance",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             Method.class.getName()
+                                 +"::invoke",
+                             StackInspector.Caller.class.getName()
+                                 +"::reflect",
+                             Method.class.getName()
+                                 +"::invoke",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewInstance"));
+
+        // Because Class.newInstance is not filtered, then the
+        // caller is Class.class
+        assertEquals(obj.cls, Class.class);
+        assertNotEquals(obj.filtered, 0);
+
+        // Calls the StackInspector.handle method through reflection
+        // and check the frames collected in the StackInspector
+        // default constructor.
+        // The handle method invokes the create method using
+        // MethodHandle.
+        // The create method invokes StackInspector.class.newInstance().
+        // We should see all reflection frames, except the
+        // jdk.internal.reflect frames which we are filtering
+        // out in StackInspector::filter.
+        System.out.println("testNewInstance: handle: show reflect");
+
+        obj = ((StackInspector)StackInspector.Caller.class
+                             .getMethod("handle", How.class)
+                             .invoke(null, How.CLASS));
+        assertEquals(obj.collectedFrames,
+                     List.of(StackInspector.class.getName()
+                                 +"::<init>",
+                             Constructor.class.getName()
+                                 +"::newInstance",
+                             Class.class.getName()
+                                 +"::newInstance",
+                             StackInspector.Caller.class.getName()
+                                 +"::create",
+                             // MethodHandle::invoke remains hidden
+                             StackInspector.Caller.class.getName()
+                                 +"::handle",
+                             Method.class.getName()
+                                 +"::invoke",
+                             ReflectionFrames.class.getName()
+                                 +"::testNewInstance"));
+
+        // Because Class.newInstance is not filtered, then the
+        // caller is Class.class
+        assertEquals(obj.cls, Class.class);
+        assertNotEquals(obj.filtered, 0);
+    }
+
+    @Test
+    public static void testGetCaller() throws Exception {
+        // Sets the default walker which hides reflection
+        // frames.
+        StackInspector.walker.set(StackInspector.walkerHide);
+
+        assertEquals(StackInspector.getCaller(), ReflectionFrames.class);
+        assertEquals(StackInspector.class.getMethod("getCaller").invoke(null),
+                     ReflectionFrames.class);
+
+        // Sets a non-default walker configured to show
+        // reflection frames
+        StackInspector.walker.set(StackInspector.walkerShow);
+
+        assertEquals(StackInspector.getCaller(), ReflectionFrames.class);
+        assertEquals(StackInspector.class.getMethod("getCaller").invoke(null),
+                     ReflectionFrames.class);
+    }
+
+    @Test
+    public static void testReflectCaller() throws Exception {
+        // Sets the default walker which hides reflection
+        // frames.
+        StackInspector.walker.set(StackInspector.walkerHide);
+
+        assertEquals(StackInspector.reflectCaller(), ReflectionFrames.class);
+        assertEquals(StackInspector.class.getMethod("reflectCaller").invoke(null),
+                     ReflectionFrames.class);
+
+        // Sets a non-default walker configured to show
+        // reflection frames
+        StackInspector.walker.set(StackInspector.walkerShow);
+
+        assertEquals(StackInspector.reflectCaller(), ReflectionFrames.class);
+        assertEquals(StackInspector.class.getMethod("reflectCaller").invoke(null),
+                     ReflectionFrames.class);
+    }
+
+    @Test
+    public static void testSupplyCaller() throws Exception {
+        // Sets the default walker which hides reflection
+        // frames.
+        StackInspector.walker.set(StackInspector.walkerHide);
+
+        assertEquals(StackInspector.supplyCaller(), ReflectionFrames.class);
+        assertEquals(StackInspector.class.getMethod("supplyCaller").invoke(null),
+                     ReflectionFrames.class);
+
+        // Sets a non-default walker configured to show
+        // reflection frames
+        StackInspector.walker.set(StackInspector.walkerShow);
+
+        assertEquals(StackInspector.supplyCaller(), ReflectionFrames.class);
+        assertEquals(StackInspector.class.getMethod("supplyCaller").invoke(null),
+                     ReflectionFrames.class);
+    }
+
+    @Test
+    public static void testHandleCaller() throws Exception {
+        // Sets the default walker which hides reflection
+        // frames.
+        StackInspector.walker.set(StackInspector.walkerHide);
+
+        assertEquals(StackInspector.handleCaller(), ReflectionFrames.class);
+        assertEquals(StackInspector.class.getMethod("handleCaller").invoke(null),
+                     ReflectionFrames.class);
+
+        // Sets a non-default walker configured to show
+        // reflection frames
+        StackInspector.walker.set(StackInspector.walkerShow);
+
+        assertEquals(StackInspector.handleCaller(), ReflectionFrames.class);
+        assertEquals(StackInspector.class.getMethod("handleCaller").invoke(null),
+                     ReflectionFrames.class);
+    }
+
+    static enum How { NEW, CONSTRUCTOR, CLASS};
+
+    /**
+     * An object that collect stack frames by walking the stack
+     * (and calling getCallerClass()) from within its constructor.
+     * For the purpose of this test, StackInspector objects are
+     * always created from the nested StackInspector.Caller class,
+     * which should therefore appear as the caller of the
+     * StackInspector constructor.
+     */
+    static class StackInspector {
+        static final StackWalker walkerHide =
+            StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
+        static final StackWalker walkerShow =
+            StackWalker.getInstance(EnumSet.of(
+                           StackWalker.Option.RETAIN_CLASS_REFERENCE,
+                           StackWalker.Option.SHOW_REFLECT_FRAMES));
+        final static ThreadLocal<StackWalker> walker = new ThreadLocal<>() {
+             protected StackWalker initialValue() {
+                 return walkerHide;
+             }
+        };
+
+        List<String> collectedFrames;
+        Class<?> cls = null;
+        boolean stop;
+        int filtered;
+        final boolean filterImplFrames;
+
+        public StackInspector() {
+            stop = false;
+            // if reflection frames are not hidden, we want to
+            // filter implementation frames before collecting
+            // to avoid depending on internal details.
+            filterImplFrames = walker.get() == walkerShow;
+            collectedFrames = walker.get().walk(this::parse);
+            cls = walker.get().getCallerClass();
+        }
+
+        public List<String> collectedFrames() {
+            return collectedFrames;
+        }
+
+        // The takeWhile method arrange for stopping frame collection
+        // as soon as a frame from ReflectionFrames.class is reached.
+        // The first such frame encountered is still included in the
+        // collected frames, but collection stops right after.
+        // This makes it possible to filter out anything above the
+        // the test method frame, such as frames from the test
+        // framework.
+        public boolean takeWhile(StackFrame f) {
+            if (stop) return false;
+            if (verbose) System.out.println("    " + f);
+            stop = stop || f.getDeclaringClass() == ReflectionFrames.class;
+            return true;
+        }
+
+        // filter out implementation frames to avoid depending
+        // on implementation details. If present, Class::newInstance,
+        // Method::invoke and Constructor::newInstance will
+        // still appear in the collected frames, which is
+        // sufficient for the purpose of the test.
+        // In the case where the StackWalker itself is supposed to
+        // filter the reflection frames, then this filter will always
+        // return true. This way, if such a reflection frame appears when
+        // it sjould have been filtered by StackWalker, it will make the
+        // test fail.
+        public boolean filter(StackFrame f) {
+            if (filterImplFrames &&
+                f.getClassName().startsWith("jdk.internal.reflect.")) {
+                filtered++;
+                return false;
+            }
+            if (!verbose) System.out.println("    " + f);
+            return true;
+        }
+
+        public String frame(StackFrame f) {
+            return f.getClassName() + "::" + f.getMethodName();
+        }
+
+        List<String> parse(Stream<StackFrame> s) {
+            return s.takeWhile(this::takeWhile)
+                    .filter(this::filter)
+                    .map(this::frame)
+                    .collect(Collectors.toList());
+        }
+
+        /**
+         * The Caller class is used to create instances of
+         * StackInspector, either direcltly, or throug reflection.
+         */
+        public static class Caller {
+            public static StackInspector create(How how) throws Exception {
+                switch(how) {
+                    case NEW: return new StackInspector();
+                    case CONSTRUCTOR: return StackInspector.class
+                        .getConstructor().newInstance();
+                    case CLASS: return StackInspector.class.newInstance();
+                    default: throw new AssertionError(String.valueOf(how));
+                }
+            }
+            public static StackInspector reflect(How how) throws Exception {
+                return (StackInspector) Caller.class.getMethod("create", How.class)
+                      .invoke(null, how);
+            }
+            public static StackInspector handle(How how) throws Exception {
+                Lookup lookup = MethodHandles.lookup();
+                MethodHandle mh = lookup.findStatic(Caller.class, "create",
+                        MethodType.methodType(StackInspector.class, How.class));
+                try {
+                    return (StackInspector) mh.invoke(how);
+                } catch (Error | Exception x) {
+                    throw x;
+                } catch(Throwable t) {
+                    throw new AssertionError(t);
+                }
+            }
+        }
+
+        public static Class<?> getCaller() throws Exception {
+            return walker.get().getCallerClass();
+        }
+
+        public static Class<?> reflectCaller() throws Exception {
+            return (Class<?>)StackWalker.class.getMethod("getCallerClass")
+                .invoke(walker.get());
+        }
+
+        public static Class<?> supplyCaller() throws Exception {
+            return ((Supplier<Class<?>>)StackInspector.walker.get()::getCallerClass).get();
+        }
+
+        public static Class<?> handleCaller() throws Exception {
+            Lookup lookup = MethodHandles.lookup();
+            MethodHandle mh = lookup.findVirtual(StackWalker.class, "getCallerClass",
+                    MethodType.methodType(Class.class));
+            try {
+                return (Class<?>) mh.invoke(walker.get());
+            } catch (Error | Exception x) {
+                throw x;
+            } catch(Throwable t) {
+                throw new AssertionError(t);
+            }
+        }
+    }
+}