Path: blob/master/test/jdk/java/lang/invoke/CallerSensitiveAccess.java
41149 views
/*1* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/* @test24* @bug 8196830 823535125* @modules java.base/jdk.internal.reflect26* @run testng/othervm CallerSensitiveAccess27* @summary Check Lookup findVirtual, findStatic and unreflect behavior with28* caller sensitive methods with focus on AccessibleObject.setAccessible29*/3031import java.io.IOException;32import java.io.UncheckedIOException;33import java.lang.invoke.MethodHandle;34import java.lang.invoke.MethodHandles;35import java.lang.invoke.MethodHandles.Lookup;36import java.lang.invoke.MethodType;37import java.lang.module.ModuleReader;38import java.lang.module.ModuleReference;39import java.lang.reflect.AccessibleObject;40import java.lang.reflect.Field;41import java.lang.reflect.InaccessibleObjectException;42import java.lang.reflect.Method;43import java.lang.reflect.Modifier;44import java.util.Arrays;45import java.util.List;46import java.util.StringJoiner;47import java.util.stream.Collectors;48import java.util.stream.Stream;4950import jdk.internal.reflect.CallerSensitive;5152import org.testng.annotations.DataProvider;53import org.testng.annotations.NoInjection;54import org.testng.annotations.Test;55import static org.testng.Assert.*;5657public class CallerSensitiveAccess {5859/**60* Caller sensitive methods in APIs exported by java.base.61*/62@DataProvider(name = "callerSensitiveMethods")63static Object[][] callerSensitiveMethods() {64return callerSensitiveMethods(Object.class.getModule())65.map(m -> new Object[] { m, shortDescription(m) })66.toArray(Object[][]::new);67}6869/**70* Using publicLookup, attempt to use findVirtual or findStatic to obtain a71* method handle to a caller sensitive method.72*/73@Test(dataProvider = "callerSensitiveMethods",74expectedExceptions = IllegalAccessException.class)75public void testPublicLookupFind(@NoInjection Method method, String desc) throws Exception {76Lookup lookup = MethodHandles.publicLookup();77Class<?> refc = method.getDeclaringClass();78String name = method.getName();79MethodType mt = MethodType.methodType(method.getReturnType(), method.getParameterTypes());80if (Modifier.isStatic(method.getModifiers())) {81lookup.findStatic(refc, name, mt);82} else {83lookup.findVirtual(refc, name, mt);84}85}8687/**88* Using publicLookup, attempt to use unreflect to obtain a method handle to a89* caller sensitive method.90*/91@Test(dataProvider = "callerSensitiveMethods",92expectedExceptions = IllegalAccessException.class)93public void testPublicLookupUnreflect(@NoInjection Method method, String desc) throws Exception {94MethodHandles.publicLookup().unreflect(method);95}9697/**98* public accessible caller sensitive methods in APIs exported by java.base.99*/100@DataProvider(name = "accessibleCallerSensitiveMethods")101static Object[][] accessibleCallerSensitiveMethods() {102return callerSensitiveMethods(Object.class.getModule())103.filter(m -> Modifier.isPublic(m.getModifiers()))104.map(m -> { m.setAccessible(true); return m; })105.map(m -> new Object[] { m, shortDescription(m) })106.toArray(Object[][]::new);107}108109/**110* Using publicLookup, attempt to use unreflect to obtain a method handle to a111* caller sensitive method.112*/113@Test(dataProvider = "accessibleCallerSensitiveMethods",114expectedExceptions = IllegalAccessException.class)115public void testLookupUnreflect(@NoInjection Method method, String desc) throws Exception {116MethodHandles.publicLookup().unreflect(method);117}118119/**120* Using a Lookup with no original access that can't lookup caller-sensitive121* method122*/123@Test(dataProvider = "callerSensitiveMethods",124expectedExceptions = IllegalAccessException.class)125public void testLookupNoOriginalAccessFind(@NoInjection Method method, String desc) throws Exception {126Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.ORIGINAL);127assertTrue(lookup.hasFullPrivilegeAccess());128Class<?> refc = method.getDeclaringClass();129String name = method.getName();130MethodType mt = MethodType.methodType(method.getReturnType(), method.getParameterTypes());131if (Modifier.isStatic(method.getModifiers())) {132lookup.findStatic(refc, name, mt);133} else {134lookup.findVirtual(refc, name, mt);135}136}137138/**139* Using a Lookup with no original access that can't unreflect caller-sensitive140* method141*/142@Test(dataProvider = "callerSensitiveMethods",143expectedExceptions = IllegalAccessException.class)144public void testLookupNoOriginalAccessUnreflect(@NoInjection Method method, String desc) throws Exception {145Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.ORIGINAL);146assertTrue(lookup.hasFullPrivilegeAccess());147lookup.unreflect(method);148}149150// -- Test method handles to setAccessible --151152private int aField;153154Field accessibleField() {155try {156return getClass().getDeclaredField("aField");157} catch (NoSuchFieldException e) {158fail();159return null;160}161}162163Field inaccessibleField() {164try {165return String.class.getDeclaredField("hash");166} catch (NoSuchFieldException e) {167fail();168return null;169}170}171172void findAndInvokeSetAccessible(Class<? extends AccessibleObject> refc, Field f) throws Throwable {173MethodType mt = MethodType.methodType(void.class, boolean.class);174MethodHandle mh = MethodHandles.lookup().findVirtual(refc, "setAccessible", mt);175mh.invoke(f, true); // may throw InaccessibleObjectException176assertTrue(f.isAccessible());177}178179void unreflectAndInvokeSetAccessible(Method m, Field f) throws Throwable {180assertTrue(m.getName().equals("setAccessible"));181assertFalse(Modifier.isStatic(m.getModifiers()));182MethodHandle mh = MethodHandles.lookup().unreflect(m);183mh.invoke(f, true); // may throw InaccessibleObjectException184assertTrue(f.isAccessible());185}186187/**188* Create a method handle to setAccessible and use it to suppress access to an189* accessible member.190*/191@Test192public void testSetAccessible1() throws Throwable {193findAndInvokeSetAccessible(AccessibleObject.class, accessibleField());194}195@Test196public void testSetAccessible2() throws Throwable {197findAndInvokeSetAccessible(Field.class, accessibleField());198}199@Test200public void testSetAccessible3() throws Throwable {201Method m = AccessibleObject.class.getMethod("setAccessible", boolean.class);202unreflectAndInvokeSetAccessible(m, accessibleField());203}204@Test205public void testSetAccessible4() throws Throwable {206Method m = Field.class.getMethod("setAccessible", boolean.class);207unreflectAndInvokeSetAccessible(m, accessibleField());208}209210/**211* Create a method handle to setAccessible and attempt to use it to suppress212* access to an inaccessible member.213*/214@Test(expectedExceptions = InaccessibleObjectException.class)215public void testSetAccessible5() throws Throwable {216findAndInvokeSetAccessible(AccessibleObject.class, inaccessibleField());217}218@Test(expectedExceptions = InaccessibleObjectException.class)219public void testSetAccessible6() throws Throwable {220findAndInvokeSetAccessible(Field.class, inaccessibleField());221}222@Test(expectedExceptions = InaccessibleObjectException.class)223public void testSetAccessible7() throws Throwable {224Method m = AccessibleObject.class.getMethod("setAccessible", boolean.class);225unreflectAndInvokeSetAccessible(m, inaccessibleField());226}227@Test(expectedExceptions = InaccessibleObjectException.class)228public void testSetAccessible8() throws Throwable {229Method m = Field.class.getMethod("setAccessible", boolean.class);230unreflectAndInvokeSetAccessible(m, inaccessibleField());231}232233234// -- Test sub-classes of AccessibleObject --235236/**237* Custom AccessibleObject objects. One class overrides setAccessible, the other238* does not override this method.239*/240@DataProvider(name = "customAccessibleObjects")241static Object[][] customAccessibleObjectClasses() {242return new Object[][] { { new S1() }, { new S2() } };243}244public static class S1 extends AccessibleObject { }245public static class S2 extends AccessibleObject {246@Override247public void setAccessible(boolean flag) {248super.setAccessible(flag);249}250}251252void findAndInvokeSetAccessible(Lookup lookup, AccessibleObject obj) throws Throwable {253MethodType mt = MethodType.methodType(void.class, boolean.class);254MethodHandle mh = lookup.findVirtual(obj.getClass(), "setAccessible", mt);255mh.invoke(obj, true);256assertTrue(obj.isAccessible());257}258259void unreflectAndInvokeSetAccessible(Lookup lookup, AccessibleObject obj) throws Throwable {260Method m = obj.getClass().getMethod("setAccessible", boolean.class);261MethodHandle mh = lookup.unreflect(m);262mh.invoke(obj, true);263assertTrue(obj.isAccessible());264}265266/**267* Using publicLookup, create a method handle to setAccessible and invoke it268* on a custom AccessibleObject object.269*/270@Test(expectedExceptions = IllegalAccessException.class)271public void testPublicLookupSubclass1() throws Throwable {272// S1 does not override setAccessible273findAndInvokeSetAccessible(MethodHandles.publicLookup(), new S1());274}275@Test276public void testPublicLookupSubclass2() throws Throwable {277// S2 overrides setAccessible278findAndInvokeSetAccessible(MethodHandles.publicLookup(), new S2());279}280@Test(expectedExceptions = IllegalAccessException.class)281public void testPublicLookupSubclass3() throws Throwable {282// S1 does not override setAccessible283unreflectAndInvokeSetAccessible(MethodHandles.publicLookup(), new S1());284}285@Test286public void testPublicLookupSubclass4() throws Throwable {287// S2 overrides setAccessible288unreflectAndInvokeSetAccessible(MethodHandles.publicLookup(), new S2());289}290291/**292* Using a full power lookup, create a method handle to setAccessible and293* invoke it on a custom AccessibleObject object.294*/295@Test(dataProvider = "customAccessibleObjects")296public void testLookupSubclass1(AccessibleObject obj) throws Throwable {297findAndInvokeSetAccessible(MethodHandles.lookup(), obj);298}299@Test(dataProvider = "customAccessibleObjects")300public void testLookupSubclass2(AccessibleObject obj) throws Throwable {301unreflectAndInvokeSetAccessible(MethodHandles.lookup(), obj);302}303304/**305* Using a full power lookup, create a method handle to setAccessible on a306* sub-class of AccessibleObject and then attempt to invoke it on a Field object.307*/308@Test(dataProvider = "customAccessibleObjects",309expectedExceptions = ClassCastException.class)310public void testLookupSubclass3(AccessibleObject obj) throws Throwable {311MethodType mt = MethodType.methodType(void.class, boolean.class);312Lookup lookup = MethodHandles.lookup();313MethodHandle mh = lookup.findVirtual(obj.getClass(), "setAccessible", mt);314mh.invoke(accessibleField(), true); // should throw ClassCastException315}316317/**318* Using a full power lookup, use unreflect to create a method handle to319* setAccessible on a sub-class of AccessibleObject, then attempt to invoke320* it on a Field object.321*/322@Test323public void testLookupSubclass4() throws Throwable {324// S1 does not override setAccessible325Method m = S1.class.getMethod("setAccessible", boolean.class);326assertTrue(m.getDeclaringClass() == AccessibleObject.class);327MethodHandle mh = MethodHandles.lookup().unreflect(m);328Field f = accessibleField();329mh.invoke(f, true);330assertTrue(f.isAccessible());331}332@Test(expectedExceptions = InaccessibleObjectException.class)333public void testLookupSubclass5() throws Throwable {334// S1 does not override setAccessible335Method m = S1.class.getMethod("setAccessible", boolean.class);336assertTrue(m.getDeclaringClass() == AccessibleObject.class);337MethodHandle mh = MethodHandles.lookup().unreflect(m);338mh.invoke(inaccessibleField(), true); // should throw InaccessibleObjectException339}340@Test(expectedExceptions = ClassCastException.class)341public void testLookupSubclass6() throws Throwable {342// S2 overrides setAccessible343Method m = S2.class.getMethod("setAccessible", boolean.class);344assertTrue(m.getDeclaringClass() == S2.class);345MethodHandle mh = MethodHandles.lookup().unreflect(m);346mh.invoke(accessibleField(), true); // should throw ClassCastException347}348@Test(expectedExceptions = ClassCastException.class)349public void testLookupSubclass7() throws Throwable {350// S2 overrides setAccessible351Method m = S2.class.getMethod("setAccessible", boolean.class);352assertTrue(m.getDeclaringClass() == S2.class);353MethodHandle mh = MethodHandles.lookup().unreflect(m);354mh.invoke(inaccessibleField(), true); // should throw ClassCastException355}356357358// -- supporting methods --359360/**361* Returns a stream of all caller sensitive methods on public classes in packages362* exported by a named module.363*/364static Stream<Method> callerSensitiveMethods(Module module) {365assert module.isNamed();366ModuleReference mref = module.getLayer().configuration()367.findModule(module.getName())368.orElseThrow(() -> new RuntimeException())369.reference();370// find all ".class" resources in the module371// transform the resource name to a class name372// load every class in the exported packages373// return the caller sensitive methods of the public classes374try (ModuleReader reader = mref.open()) {375return reader.list()376.filter(rn -> rn.endsWith(".class"))377.map(rn -> rn.substring(0, rn.length() - 6)378.replace('/', '.'))379.filter(cn -> module.isExported(packageName(cn)))380.map(cn -> Class.forName(module, cn))381.filter(refc -> refc != null382&& Modifier.isPublic(refc.getModifiers()))383.map(refc -> callerSensitiveMethods(refc))384.flatMap(List::stream);385} catch (IOException ioe) {386throw new UncheckedIOException(ioe);387}388}389390static String packageName(String cn) {391int last = cn.lastIndexOf('.');392if (last > 0) {393return cn.substring(0, last);394} else {395return "";396}397}398399/**400* Returns a list of the caller sensitive methods directly declared by the given401* class.402*/403static List<Method> callerSensitiveMethods(Class<?> refc) {404return Arrays.stream(refc.getDeclaredMethods())405.filter(m -> m.isAnnotationPresent(CallerSensitive.class))406.collect(Collectors.toList());407}408409/**410* Returns a short description of the given method for tracing purposes.411*/412static String shortDescription(Method m) {413var sb = new StringBuilder();414sb.append(m.getDeclaringClass().getName());415sb.append('.');416sb.append(m.getName());417sb.append('(');418StringJoiner sj = new StringJoiner(",");419for (Class<?> parameterType : m.getParameterTypes()) {420sj.add(parameterType.getTypeName());421}422sb.append(sj);423sb.append(')');424return sb.toString();425}426}427428429