Path: blob/master/test/jdk/java/lang/invoke/AccessControlTest.java
41149 views
/*1* Copyright (c) 2012, 2019, 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* @summary test access checking by java.lang.invoke.MethodHandles.Lookup25* @compile AccessControlTest.java AccessControlTest_subpkg/Acquaintance_remote.java26* @run testng/othervm test.java.lang.invoke.AccessControlTest27*/2829package test.java.lang.invoke;3031import java.lang.invoke.*;32import java.lang.reflect.*;33import java.lang.reflect.Modifier;34import java.util.*;35import org.testng.annotations.*;3637import static java.lang.invoke.MethodHandles.*;38import static java.lang.invoke.MethodHandles.Lookup.*;39import static java.lang.invoke.MethodType.*;40import static org.testng.Assert.*;4142import test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote;434445/**46* Test many combinations of Lookup access and cross-class lookupStatic.47* @author jrose48*/49public class AccessControlTest {50static final Class<?> THIS_CLASS = AccessControlTest.class;51// How much output?52static int verbosity = 0;53static {54String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbosity");55if (vstr == null)56vstr = System.getProperty(THIS_CLASS.getName()+".verbosity");57if (vstr != null) verbosity = Integer.parseInt(vstr);58}5960private class LookupCase implements Comparable<LookupCase> {61final Lookup lookup;62final Class<?> lookupClass;63final Class<?> prevLookupClass;64final int lookupModes;65public LookupCase(Lookup lookup) {66this.lookup = lookup;67this.lookupClass = lookup.lookupClass();68this.prevLookupClass = lookup.previousLookupClass();69this.lookupModes = lookup.lookupModes();7071assert(lookupString().equals(lookup.toString()));72numberOf(lookupClass().getClassLoader()); // assign CL#73}74public LookupCase(Class<?> lookupClass, Class<?> prevLookupClass, int lookupModes) {75this.lookup = null;76this.lookupClass = lookupClass;77this.prevLookupClass = prevLookupClass;78this.lookupModes = lookupModes;79numberOf(lookupClass().getClassLoader()); // assign CL#80}8182public final Class<?> lookupClass() { return lookupClass; }83public final Class<?> prevLookupClass() { return prevLookupClass; }84public final int lookupModes() { return lookupModes; }8586public Lookup lookup() { lookup.getClass(); return lookup; }8788@Override89public int compareTo(LookupCase that) {90Class<?> c1 = this.lookupClass();91Class<?> c2 = that.lookupClass();92Class<?> p1 = this.prevLookupClass();93Class<?> p2 = that.prevLookupClass();94if (c1 != c2) {95int cmp = c1.getName().compareTo(c2.getName());96if (cmp != 0) return cmp;97cmp = numberOf(c1.getClassLoader()) - numberOf(c2.getClassLoader());98assert(cmp != 0);99return cmp;100} else if (p1 != p2){101if (p1 == null)102return 1;103else if (p2 == null)104return -1;105int cmp = p1.getName().compareTo(p2.getName());106if (cmp != 0) return cmp;107cmp = numberOf(p1.getClassLoader()) - numberOf(p2.getClassLoader());108assert(cmp != 0);109return cmp;110}111return -(this.lookupModes() - that.lookupModes());112}113114@Override115public boolean equals(Object that) {116return (that instanceof LookupCase && equals((LookupCase)that));117}118public boolean equals(LookupCase that) {119return (this.lookupClass() == that.lookupClass() &&120this.prevLookupClass() == that.prevLookupClass() &&121this.lookupModes() == that.lookupModes());122}123124@Override125public int hashCode() {126return lookupClass().hashCode() + (lookupModes() * 31);127}128129/** Simulate all assertions in the spec. for Lookup.toString. */130private String lookupString() {131String name = lookupClass.getName();132if (prevLookupClass != null)133name += "/" + prevLookupClass.getName();134String suffix = "";135if (lookupModes == 0)136suffix = "/noaccess";137else if (lookupModes == PUBLIC)138suffix = "/public";139else if (lookupModes == UNCONDITIONAL)140suffix = "/publicLookup";141else if (lookupModes == (PUBLIC|MODULE))142suffix = "/module";143else if (lookupModes == (PUBLIC|PACKAGE)144|| lookupModes == (PUBLIC|MODULE|PACKAGE))145suffix = "/package";146else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE)147|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))148suffix = "/private";149else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE|PROTECTED)150|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED)151|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|ORIGINAL))152suffix = "";153else154suffix = "/#"+Integer.toHexString(lookupModes);155return name+suffix;156}157158/** Simulate all assertions from the spec. for Lookup.in:159* <hr>160* Creates a lookup on the specified new lookup class.161* [A1] The resulting object will report the specified162* class as its own {@link #lookupClass lookupClass}.163* [A1-a] However, the resulting {@code Lookup} object is guaranteed164* to have no more access capabilities than the original.165* In particular, access capabilities can be lost as follows:<ul>166* [A2] If the new lookup class is not the same as the old lookup class,167* then {@link #ORIGINAL ORIGINAL} access is lost.168* [A3] If the new lookup class is in a different module from the old one,169* i.e. {@link #MODULE MODULE} access is lost.170* [A4] If the new lookup class is in a different package171* than the old one, protected and default (package) members will not be accessible,172* i.e. {@link #PROTECTED PROTECTED} and {@link #PACKAGE PACKAGE} access are lost.173* [A5] If the new lookup class is not within the same package member174* as the old one, private members will not be accessible, and protected members175* will not be accessible by virtue of inheritance,176* i.e. {@link #PRIVATE PRIVATE} access is lost.177* (Protected members may continue to be accessible because of package sharing.)178* [A6] If the new lookup class is not179* {@linkplain #accessClass(Class) accessible} to this lookup,180* then no members, not even public members, will be accessible181* i.e. all access modes are lost.182* [A7] If the new lookup class, the old lookup class and the previous lookup class183* are all in different modules i.e. teleporting to a third module,184* all access modes are lost.185* <p>186* The new previous lookup class is chosen as follows:187* [A8] If the new lookup object has {@link #UNCONDITIONAL UNCONDITIONAL} bit,188* the new previous lookup class is {@code null}.189* [A9] If the new lookup class is in the same module as the old lookup class,190* the new previous lookup class is the old previous lookup class.191* [A10] If the new lookup class is in a different module from the old lookup class,192* the new previous lookup class is the the old lookup class.193*194* Other than the above cases, the new lookup will have the same195* access capabilities as the original. [A11]196* <hr>197*/198public LookupCase in(Class<?> c2) {199Class<?> c1 = lookupClass();200Module m1 = c1.getModule();201Module m2 = c2.getModule();202Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : c1.getModule();203int modes1 = lookupModes();204int changed = 0;205// for the purposes of access control then treat classes in different unnamed206// modules as being in the same module.207boolean sameModule = (m1 == m2) ||208(!m1.isNamed() && !m2.isNamed());209boolean samePackage = (c1.getClassLoader() == c2.getClassLoader() &&210c1.getPackageName().equals(c2.getPackageName()));211boolean sameTopLevel = (topLevelClass(c1) == topLevelClass(c2));212boolean sameClass = (c1 == c2);213assert(samePackage || !sameTopLevel);214assert(sameTopLevel || !sameClass);215boolean accessible = sameClass;216217if ((modes1 & PACKAGE) != 0) accessible |= samePackage;218if ((modes1 & PUBLIC ) != 0) {219if (isModuleAccessible(c2))220accessible |= (c2.getModifiers() & PUBLIC) != 0;221else222accessible = false;223}224if ((modes1 & UNCONDITIONAL) != 0) {225if (m2.isExported(c2.getPackageName()))226accessible |= (c2.getModifiers() & PUBLIC) != 0;227else228accessible = false;229}230if (!accessible) {231// no access to c2; lose all access.232changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|UNCONDITIONAL); // [A7]233}234if (!sameClass) {235changed |= ORIGINAL; // [A2]236}237if (m2 != m1 && m0 != m1) {238// hop to a third module; lose all access239changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A8]240}241if (!sameModule) {242changed |= MODULE; // [A3]243}244if (!samePackage) {245// Different package; loose PACKAGE and lower access.246changed |= (PACKAGE|PRIVATE|PROTECTED); // [A4]247}248if (!sameTopLevel) {249// Different top-level class. Lose PRIVATE and PROTECTED access.250changed |= (PRIVATE|PROTECTED); // [A5]251}252if (sameClass) {253assert(changed == 0); // [A11] (no deprivation if same class)254}255256if (accessible) assert((changed & PUBLIC) == 0);257int modes2 = modes1 & ~changed;258Class<?> plc = (m1 == m2) ? prevLookupClass() : c1; // [A9] [A10]259if ((modes1 & UNCONDITIONAL) != 0) plc = null; // [A8]260LookupCase l2 = new LookupCase(c2, plc, modes2);261assert(l2.lookupClass() == c2); // [A1]262assert((modes1 | modes2) == modes1); // [A1-a] (no elevation of access)263assert(l2.prevLookupClass() == null || (modes2 & MODULE) == 0);264return l2;265}266267LookupCase dropLookupMode(int modeToDrop) {268int oldModes = lookupModes();269int newModes = oldModes & ~(modeToDrop | PROTECTED | ORIGINAL);270switch (modeToDrop) {271case PUBLIC: newModes &= ~(MODULE|PACKAGE|PROTECTED|PRIVATE); break;272case MODULE: newModes &= ~(PACKAGE|PRIVATE); break;273case PACKAGE: newModes &= ~(PRIVATE); break;274case PROTECTED:275case PRIVATE:276case ORIGINAL:277case UNCONDITIONAL: break;278default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");279}280if (newModes == oldModes) return this; // return self if no change281LookupCase l2 = new LookupCase(lookupClass(), prevLookupClass(), newModes);282assert((oldModes | newModes) == oldModes); // [A2] (no elevation of access)283assert(l2.prevLookupClass() == null || (newModes & MODULE) == 0);284return l2;285}286287boolean isModuleAccessible(Class<?> c) {288Module m1 = lookupClass().getModule();289Module m2 = c.getModule();290Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : m1;291String pn = c.getPackageName();292boolean accessible = m1.canRead(m2) && m2.isExported(pn, m1);293if (m1 != m0) {294accessible = accessible && m0.canRead(m2) && m2.isExported(pn, m0);295}296return accessible;297}298299@Override300public String toString() {301String s = lookupClass().getSimpleName();302String lstr = lookupString();303int sl = lstr.indexOf('/');304if (sl >= 0) s += lstr.substring(sl);305ClassLoader cld = lookupClass().getClassLoader();306if (cld != THIS_LOADER) s += "/loader#"+numberOf(cld);307return s;308}309310/** Predict the success or failure of accessing this method. */311public boolean willAccess(Method m) {312Class<?> c1 = lookupClass();313Class<?> c2 = m.getDeclaringClass();314Module m1 = c1.getModule();315Module m2 = c2.getModule();316Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;317// unconditional has access to all public types/members of types that is in a package318// are unconditionally exported319if ((lookupModes & UNCONDITIONAL) != 0) {320return m2.isExported(c2.getPackageName())321&& Modifier.isPublic(c2.getModifiers())322&& Modifier.isPublic(m.getModifiers());323}324325// c1 and c2 are in different module326if (m1 != m2 || m0 != m2) {327return (lookupModes & PUBLIC) != 0328&& isModuleAccessible(c2)329&& Modifier.isPublic(c2.getModifiers())330&& Modifier.isPublic(m.getModifiers());331}332333assert(m1 == m2 && prevLookupClass == null);334335if (!willAccessClass(c2, false))336return false;337338LookupCase lc = this.in(c2);339int modes1 = lc.lookupModes();340int modes2 = fixMods(m.getModifiers());341// allow private lookup on nestmates. Otherwise, privacy is strictly enforced342if (c1 != c2 && ((modes2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {343modes1 &= ~PRIVATE;344}345// protected access is sometimes allowed346if ((modes2 & PROTECTED) != 0) {347int prev = modes2;348modes2 |= PACKAGE; // it acts like a package method also349if ((lookupModes() & PROTECTED) != 0 &&350c2.isAssignableFrom(c1))351modes2 |= PUBLIC; // from a subclass, it acts like a public method also352}353if (verbosity >= 2)354System.out.format("%s willAccess %s modes1=0x%h modes2=0x%h => %s%n", lookupString(), lc.lookupString(), modes1, modes2, (modes2 & modes1) != 0);355return (modes2 & modes1) != 0;356}357358/** Predict the success or failure of accessing this class. */359public boolean willAccessClass(Class<?> c2, boolean load) {360Class<?> c1 = lookupClass();361if (load && c2.getClassLoader() != null) {362if (c1.getClassLoader() == null) {363// not visible364return false;365}366}367368Module m1 = c1.getModule();369Module m2 = c2.getModule();370Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;371// unconditional has access to all public types that is in an unconditionally exported package372if ((lookupModes & UNCONDITIONAL) != 0) {373return m2.isExported(c2.getPackageName()) && Modifier.isPublic(c2.getModifiers());374}375// c1 and c2 are in different module376if (m1 != m2 || m0 != m2) {377return (lookupModes & PUBLIC) != 0378&& isModuleAccessible(c2)379&& Modifier.isPublic(c2.getModifiers());380}381382assert(m1 == m2 && prevLookupClass == null);383384LookupCase lc = this.in(c2);385int modes1 = lc.lookupModes();386boolean r = false;387if (modes1 == 0) {388r = false;389} else {390if (Modifier.isPublic(c2.getModifiers())) {391if ((modes1 & MODULE) != 0)392r = true;393else if ((modes1 & PUBLIC) != 0)394r = m1.isExported(c2.getPackageName());395} else {396if ((modes1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage())397r = true;398}399}400if (verbosity >= 2) {401System.out.println(this+" willAccessClass "+lc+" c1="+c1+" c2="+c2+" => "+r);402}403return r;404}405}406407private static Class<?> topLevelClass(Class<?> cls) {408Class<?> c = cls;409for (Class<?> ec; (ec = c.getEnclosingClass()) != null; )410c = ec;411assert(c.getEnclosingClass() == null);412assert(c == cls || cls.getEnclosingClass() != null);413return c;414}415416private final TreeSet<LookupCase> CASES = new TreeSet<>();417private final TreeMap<LookupCase,TreeSet<LookupCase>> CASE_EDGES = new TreeMap<>();418private final ArrayList<ClassLoader> LOADERS = new ArrayList<>();419private final ClassLoader THIS_LOADER = this.getClass().getClassLoader();420{ if (THIS_LOADER != null) LOADERS.add(THIS_LOADER); } // #1421422private LookupCase lookupCase(String name) {423for (LookupCase lc : CASES) {424if (lc.toString().equals(name))425return lc;426}427throw new AssertionError(name);428}429430private int numberOf(ClassLoader cl) {431if (cl == null) return 0;432int i = LOADERS.indexOf(cl);433if (i < 0) {434i = LOADERS.size();435LOADERS.add(cl);436}437return i+1;438}439440private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2, int dropAccess) {441TreeSet<LookupCase> edges = CASE_EDGES.get(l2);442if (edges == null) CASE_EDGES.put(l2, edges = new TreeSet<>());443if (edges.add(l1)) {444Class<?> c1 = l1.lookupClass();445assert(l2.lookupClass() == c2); // [A1]446int m1 = l1.lookupModes();447int m2 = l2.lookupModes();448assert((m1 | m2) == m1); // [A2] (no elevation of access)449LookupCase expect = dropAccess == 0 ? l1.in(c2) : l1.in(c2).dropLookupMode(dropAccess);450if (!expect.equals(l2))451System.out.println("*** expect "+l1+" => "+expect+" but got "+l2);452assertEquals(l2, expect);453}454}455456private void makeCases(Lookup[] originalLookups) {457// make initial set of lookup test cases458CASES.clear(); LOADERS.clear(); CASE_EDGES.clear();459ArrayList<Class<?>> classes = new ArrayList<>();460for (Lookup l : originalLookups) {461CASES.add(new LookupCase(l));462classes.remove(l.lookupClass()); // no dups please463classes.add(l.lookupClass());464}465System.out.println("loaders = "+LOADERS);466int rounds = 0;467for (int lastCount = -1; lastCount != CASES.size(); ) {468lastCount = CASES.size(); // if CASES grow in the loop we go round again469for (LookupCase lc1 : CASES.toArray(new LookupCase[0])) {470for (int mode : ACCESS_CASES) {471LookupCase lc2 = new LookupCase(lc1.lookup().dropLookupMode(mode));472addLookupEdge(lc1, lc1.lookupClass(), lc2, mode);473CASES.add(lc2);474}475for (Class<?> c2 : classes) {476LookupCase lc2 = new LookupCase(lc1.lookup().in(c2));477addLookupEdge(lc1, c2, lc2, 0);478CASES.add(lc2);479}480}481rounds++;482}483System.out.println("filled in "+CASES.size()+" cases from "+originalLookups.length+" original cases in "+rounds+" rounds");484if (false) {485System.out.println("CASES: {");486for (LookupCase lc : CASES) {487System.out.println(lc);488Set<LookupCase> edges = CASE_EDGES.get(lc);489if (edges != null)490for (LookupCase prev : edges) {491System.out.println("\t"+prev);492}493}494System.out.println("}");495}496}497498@Test public void test() {499makeCases(lookups());500if (verbosity > 0) {501verbosity += 9;502Method pro_in_self = targetMethod(THIS_CLASS, PROTECTED, methodType(void.class));503testOneAccess(lookupCase("AccessControlTest/module"), pro_in_self, "find");504testOneAccess(lookupCase("Remote_subclass/module"), pro_in_self, "find");505testOneAccess(lookupCase("Remote_subclass"), pro_in_self, "find");506verbosity -= 9;507}508Set<Class<?>> targetClassesDone = new HashSet<>();509for (LookupCase targetCase : CASES) {510Class<?> targetClass = targetCase.lookupClass();511if (!targetClassesDone.add(targetClass)) continue; // already saw this one512String targetPlace = placeName(targetClass);513if (targetPlace == null) continue; // Object, String, not a target514for (int targetAccess : ACCESS_CASES) {515if (targetAccess == MODULE || targetAccess == UNCONDITIONAL)516continue;517MethodType methodType = methodType(void.class);518Method method = targetMethod(targetClass, targetAccess, methodType);519// Try to access target method from various contexts.520for (LookupCase sourceCase : CASES) {521testOneAccess(sourceCase, method, "findClass");522testOneAccess(sourceCase, method, "accessClass");523testOneAccess(sourceCase, method, "find");524testOneAccess(sourceCase, method, "unreflect");525}526}527}528System.out.println("tested "+testCount+" access scenarios; "+testCountFails+" accesses were denied");529}530531private int testCount, testCountFails;532533private void testOneAccess(LookupCase sourceCase, Method method, String kind) {534Class<?> targetClass = method.getDeclaringClass();535String methodName = method.getName();536MethodType methodType = methodType(method.getReturnType(), method.getParameterTypes());537boolean isFindOrAccessClass = "findClass".equals(kind) || "accessClass".equals(kind);538boolean willAccess = isFindOrAccessClass ?539sourceCase.willAccessClass(targetClass, "findClass".equals(kind)) : sourceCase.willAccess(method);540boolean didAccess = false;541ReflectiveOperationException accessError = null;542try {543switch (kind) {544case "accessClass":545sourceCase.lookup().accessClass(targetClass);546break;547case "findClass":548sourceCase.lookup().findClass(targetClass.getName());549break;550case "find":551if ((method.getModifiers() & Modifier.STATIC) != 0)552sourceCase.lookup().findStatic(targetClass, methodName, methodType);553else554sourceCase.lookup().findVirtual(targetClass, methodName, methodType);555break;556case "unreflect":557sourceCase.lookup().unreflect(method);558break;559default:560throw new AssertionError(kind);561}562didAccess = true;563} catch (ReflectiveOperationException ex) {564accessError = ex;565}566if (willAccess != didAccess) {567System.out.println(sourceCase+" => "+targetClass.getSimpleName()+(isFindOrAccessClass?"":"."+methodName+methodType));568System.out.println("fail "+(isFindOrAccessClass?kind:"on "+method)+" ex="+accessError);569assertEquals(willAccess, didAccess);570}571testCount++;572if (!didAccess) testCountFails++;573}574575static Method targetMethod(Class<?> targetClass, int targetAccess, MethodType methodType) {576String methodName = accessName(targetAccess)+placeName(targetClass);577if (verbosity >= 2)578System.out.println(targetClass.getSimpleName()+"."+methodName+methodType);579try {580Method method = targetClass.getDeclaredMethod(methodName, methodType.parameterArray());581assertEquals(method.getReturnType(), methodType.returnType());582int haveMods = method.getModifiers();583assert(Modifier.isStatic(haveMods));584assert(targetAccess == fixMods(haveMods));585return method;586} catch (NoSuchMethodException ex) {587throw new AssertionError(methodName, ex);588}589}590591static String placeName(Class<?> cls) {592// return "self", "sibling", "nestmate", etc.593if (cls == AccessControlTest.class) return "self";594String cln = cls.getSimpleName();595int under = cln.lastIndexOf('_');596if (under < 0) return null;597return cln.substring(under+1);598}599static String accessName(int acc) {600switch (acc) {601case PUBLIC: return "pub_in_";602case PROTECTED: return "pro_in_";603case PACKAGE: return "pkg_in_";604case PRIVATE: return "pri_in_";605}606assert(false);607return "?";608}609private static final int[] ACCESS_CASES = {610PUBLIC, PACKAGE, PRIVATE, PROTECTED, MODULE, UNCONDITIONAL611};612/*613* Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL614* Adjust 0 => PACKAGE615*/616/** Return one of the ACCESS_CASES. */617static int fixMods(int mods) {618mods &= (PUBLIC|PRIVATE|PROTECTED);619switch (mods) {620case PUBLIC: case PRIVATE: case PROTECTED: return mods;621case 0: return PACKAGE;622}623throw new AssertionError(mods);624}625626static Lookup[] lookups() {627ArrayList<Lookup> tem = new ArrayList<>();628Collections.addAll(tem,629AccessControlTest.lookup_in_self(),630Inner_nestmate.lookup_in_nestmate(),631AccessControlTest_sibling.lookup_in_sibling());632if (true) {633Collections.addAll(tem,Acquaintance_remote.lookups());634} else {635try {636Class<?> remc = Class.forName("test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote");637Lookup[] remls = (Lookup[]) remc.getMethod("lookups").invoke(null);638Collections.addAll(tem, remls);639} catch (ReflectiveOperationException ex) {640throw new LinkageError("reflection failed", ex);641}642}643tem.add(publicLookup());644tem.add(publicLookup().in(String.class));645tem.add(publicLookup().in(List.class));646return tem.toArray(new Lookup[0]);647}648649static Lookup lookup_in_self() {650return MethodHandles.lookup();651}652public static void pub_in_self() { }653protected static void pro_in_self() { }654static /*package*/ void pkg_in_self() { }655private static void pri_in_self() { }656657static class Inner_nestmate {658static Lookup lookup_in_nestmate() {659return MethodHandles.lookup();660}661public static void pub_in_nestmate() { }662protected static void pro_in_nestmate() { }663static /*package*/ void pkg_in_nestmate() { }664private static void pri_in_nestmate() { }665}666}667class AccessControlTest_sibling {668static Lookup lookup_in_sibling() {669return MethodHandles.lookup();670}671public static void pub_in_sibling() { }672protected static void pro_in_sibling() { }673static /*package*/ void pkg_in_sibling() { }674private static void pri_in_sibling() { }675}676677// This guy tests access from outside the package:678/*679package test.java.lang.invoke.AccessControlTest_subpkg;680public class Acquaintance_remote {681public static Lookup[] lookups() { ...682}683...684}685*/686687688