| /* | |
| * Copyright (c) 2017, 2022, 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; | |
| final static Class<?> REFLECT_ACCESS = findClass("java.lang.reflect.ReflectAccess"); | |
| final static Class<?> REFLECTION_FACTORY = findClass("jdk.internal.reflect.ReflectionFactory"); | |
| private static Class<?> findClass(String cn) { | |
| try { | |
| return Class.forName(cn); | |
| } catch (ClassNotFoundException e) { | |
| throw new AssertionError(e); | |
| } | |
| } | |
| /** | |
| * 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() | |
| +"::newInstanceWithCaller", | |
| 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() | |
| +"::newInstanceWithCaller", | |
| 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() | |
| +"::newInstanceWithCaller", | |
| 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 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>", | |
| REFLECT_ACCESS.getName() | |
| +"::newInstance", | |
| REFLECTION_FACTORY.getName() | |
| +"::newInstance", | |
| Class.class.getName() | |
| +"::newInstance", | |
| StackInspector.Caller.class.getName() | |
| +"::create", | |
| ReflectionFrames.class.getName() | |
| +"::testNewInstance")); | |
| // Because implementation frames are not filtered, then the | |
| // caller is ReflectAccess.class | |
| assertEquals(obj.cls, REFLECT_ACCESS); | |
| 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>", | |
| REFLECT_ACCESS.getName() | |
| +"::newInstance", | |
| REFLECTION_FACTORY.getName() | |
| +"::newInstance", | |
| Class.class.getName() | |
| +"::newInstance", | |
| StackInspector.Caller.class.getName() | |
| +"::create", | |
| StackInspector.Caller.class.getName() | |
| +"::reflect", | |
| ReflectionFrames.class.getName() | |
| +"::testNewInstance")); | |
| // Because implementation frames are not filtered, then the | |
| // caller is ReflectAccess.class | |
| assertEquals(obj.cls, REFLECT_ACCESS); | |
| 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>", | |
| REFLECT_ACCESS.getName() | |
| +"::newInstance", | |
| REFLECTION_FACTORY.getName() | |
| +"::newInstance", | |
| Class.class.getName() | |
| +"::newInstance", | |
| StackInspector.Caller.class.getName() | |
| +"::create", | |
| StackInspector.Caller.class.getName() | |
| +"::handle", | |
| ReflectionFrames.class.getName() | |
| +"::testNewInstance")); | |
| // Because implementation frames are not filtered, then the | |
| // caller is ReflectAccess.class | |
| assertEquals(obj.cls, REFLECT_ACCESS); | |
| 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() | |
| +"::newInstanceWithCaller", | |
| REFLECT_ACCESS.getName() | |
| +"::newInstance", | |
| Class.class.getName() | |
| +"::newInstance", | |
| StackInspector.Caller.class.getName() | |
| +"::create", | |
| Method.class.getName() | |
| +"::invoke", | |
| ReflectionFrames.class.getName() | |
| +"::testNewInstance")); | |
| // Because implementation frames are not filtered, then the | |
| // caller is ReflectAccess.class | |
| assertEquals(obj.cls, REFLECT_ACCESS); | |
| 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)); | |
| System.out.println(obj.collectedFrames); | |
| assertEquals(obj.collectedFrames, | |
| List.of(StackInspector.class.getName() | |
| +"::<init>", | |
| Constructor.class.getName() | |
| +"::newInstanceWithCaller", | |
| REFLECT_ACCESS.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 implementation frames are not filtered, then the | |
| // caller is ReflectAccess.class | |
| assertEquals(obj.cls, REFLECT_ACCESS); | |
| 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() | |
| +"::newInstanceWithCaller", | |
| REFLECT_ACCESS.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 implementation frames are not filtered, then the | |
| // caller is ReflectAccess.class | |
| assertEquals(obj.cls, REFLECT_ACCESS); | |
| 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); | |
| } | |
| } | |
| } | |
| } |