1 /*
   2  * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /* @test
  25  * @summary test access checking by java.lang.invoke.MethodHandles.Lookup
  26  * @compile AccessControlTest.java AccessControlTest_subpkg/Acquaintance_remote.java
  27  * @run testng/othervm test.java.lang.invoke.AccessControlTest
  28  */
  29 
  30 package test.java.lang.invoke;
  31 
  32 import java.lang.invoke.*;
  33 import java.lang.reflect.*;
  34 import java.lang.reflect.Modifier;
  35 import java.util.*;
  36 import org.testng.*;
  37 import org.testng.annotations.*;
  38 
  39 import static java.lang.invoke.MethodHandles.*;
  40 import static java.lang.invoke.MethodHandles.Lookup.*;
  41 import static java.lang.invoke.MethodType.*;
  42 import static org.testng.Assert.*;
  43 
  44 import test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote;
  45 
  46 
  47 /**
  48  * Test many combinations of Lookup access and cross-class lookupStatic.
  49  * @author jrose
  50  */
  51 public class AccessControlTest {
  52     static final Class<?> THIS_CLASS = AccessControlTest.class;
  53     // How much output?
  54     static int verbosity = 0;
  55     static {
  56         String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbosity");
  57         if (vstr == null)
  58             vstr = System.getProperty(THIS_CLASS.getName()+".verbosity");
  59         if (vstr != null)  verbosity = Integer.parseInt(vstr);
  60     }
  61 
  62     private class LookupCase implements Comparable<LookupCase> {
  63         final Lookup   lookup;
  64         final Class<?> lookupClass;
  65         final int      lookupModes;
  66         public LookupCase(Lookup lookup) {
  67             this.lookup = lookup;
  68             this.lookupClass = lookup.lookupClass();
  69             this.lookupModes = lookup.lookupModes();
  70             assert(lookupString().equals(lookup.toString()));
  71             numberOf(lookupClass().getClassLoader()); // assign CL#
  72         }
  73         public LookupCase(Class<?> lookupClass, int lookupModes) {
  74             this.lookup = null;
  75             this.lookupClass = lookupClass;
  76             this.lookupModes = lookupModes;
  77             numberOf(lookupClass().getClassLoader()); // assign CL#
  78         }
  79 
  80         public final Class<?> lookupClass() { return lookupClass; }
  81         public final int      lookupModes() { return lookupModes; }
  82 
  83         public Lookup lookup() { lookup.getClass(); return lookup; }
  84 
  85         @Override
  86         public int compareTo(LookupCase that) {
  87             Class<?> c1 = this.lookupClass();
  88             Class<?> c2 = that.lookupClass();
  89             if (c1 != c2) {
  90                 int cmp = c1.getName().compareTo(c2.getName());
  91                 if (cmp != 0)  return cmp;
  92                 cmp = numberOf(c1.getClassLoader()) - numberOf(c2.getClassLoader());
  93                 assert(cmp != 0);
  94                 return cmp;
  95             }
  96             return -(this.lookupModes() - that.lookupModes());
  97         }
  98 
  99         @Override
 100         public boolean equals(Object that) {
 101             return (that instanceof LookupCase && equals((LookupCase)that));
 102         }
 103         public boolean equals(LookupCase that) {
 104             return (this.lookupClass() == that.lookupClass() &&
 105                     this.lookupModes() == that.lookupModes());
 106         }
 107 
 108         @Override
 109         public int hashCode() {
 110             return lookupClass().hashCode() + (lookupModes() * 31);
 111         }
 112 
 113         /** Simulate all assertions in the spec. for Lookup.toString. */
 114         private String lookupString() {
 115             String name = lookupClass.getName();
 116             String suffix = "";
 117             if (lookupModes == 0)
 118                 suffix = "/noaccess";
 119             else if (lookupModes == PUBLIC)
 120                 suffix = "/public";
 121             else if (lookupModes == (PUBLIC|MODULE))
 122                 suffix = "/module";
 123             else if (lookupModes == (PUBLIC|MODULE|PACKAGE))
 124                 suffix = "/package";
 125             else if (lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
 126                 suffix = "/private";
 127             else if (lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED))
 128                 suffix = "";
 129             else
 130                 suffix = "/#"+Integer.toHexString(lookupModes);
 131             return name+suffix;
 132         }
 133 
 134         /** Simulate all assertions from the spec. for Lookup.in:
 135          * <hr>
 136          * Creates a lookup on the specified new lookup class.
 137          * [A1] The resulting object will report the specified
 138          * class as its own {@link #lookupClass lookupClass}.
 139          * <p>
 140          * [A2] However, the resulting {@code Lookup} object is guaranteed
 141          * to have no more access capabilities than the original.
 142          * In particular, access capabilities can be lost as follows:<ul>
 143          * <li>[A3] If the new lookup class differs from the old one,
 144          * protected members will not be accessible by virtue of inheritance.
 145          * (Protected members may continue to be accessible because of package sharing.)
 146          * <li>[A4] If the new lookup class is in a different package
 147          * than the old one, protected and default (package) members will not be accessible.
 148          * <li>[A5] If the new lookup class is not within the same package member
 149          * as the old one, private members will not be accessible.
 150          * <li>[A6] If the new lookup class is not accessible to the old lookup class,
 151          * using the original access modes,
 152          * then no members, not even public members, will be accessible.
 153          * <li>[A7] If the new lookup class for this {@code Lookup} is in the unnamed module,
 154          * and the new lookup class is in a named module {@code M}, then no members in
 155          * {@code M}'s non-exported packages will be accessible.
 156          * <li>[A8] If the lookup for this {@code Lookup} is in a named module, and the
 157          * new lookup class is in a different module, then no members, not even
 158          * public members in {@code M}'s exported packages, will be accessible.
 159          * [A8] (In all other cases, public members will continue to be accessible.)
 160          * </ul>
 161          * Other than the above cases, the new lookup will have the same
 162          * access capabilities as the original. [A10]
 163          * <hr>
 164          */
 165         public LookupCase in(Class<?> c2) {
 166             Class<?> c1 = lookupClass();
 167             int m1 = lookupModes();
 168             int changed = 0;
 169             // for the purposes of access control then treat classes in different unnamed
 170             // modules as being in the same module.
 171             boolean sameModule = (c1.getModule() == c2.getModule()) ||
 172                                  (!c1.getModule().isNamed() && !c2.getModule().isNamed());
 173             boolean samePackage = (c1.getClassLoader() == c2.getClassLoader() &&
 174                                    packagePrefix(c1).equals(packagePrefix(c2)));
 175             boolean sameTopLevel = (topLevelClass(c1) == topLevelClass(c2));
 176             boolean sameClass = (c1 == c2);
 177             assert(samePackage  || !sameTopLevel);
 178             assert(sameTopLevel || !sameClass);
 179             boolean accessible = sameClass;  // [A6]
 180             if ((m1 & PACKAGE) != 0)  accessible |= samePackage;
 181             if ((m1 & PUBLIC ) != 0)  accessible |= (c2.getModifiers() & PUBLIC) != 0;
 182             if (!sameModule) {
 183                 if (c1.getModule().isNamed()) {
 184                     accessible = false;  // [A8]
 185                 } else {
 186                     // Different module; loose MODULE and lower access.
 187                     changed |= (MODULE|PACKAGE|PRIVATE|PROTECTED);  // [A7]
 188                 }
 189             }
 190             if (!accessible) {
 191                 // Different package and no access to c2; lose all access.
 192                 changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED);  // [A6]
 193             }
 194             if (!samePackage) {
 195                 // Different package; loose PACKAGE and lower access.
 196                 changed |= (PACKAGE|PRIVATE|PROTECTED);  // [A4]
 197             }
 198             if (!sameTopLevel) {
 199                 // Different top-level class.  Lose PRIVATE and lower access.
 200                 changed |= (PRIVATE|PROTECTED);  // [A5]
 201             }
 202             if (!sameClass) {
 203                 changed |= (PROTECTED);     // [A3]
 204             } else {
 205                 assert(changed == 0);       // [A10] (no deprivation if same class)
 206             }
 207             if (accessible)  assert((changed & PUBLIC) == 0);  // [A9]
 208             int m2 = m1 & ~changed;
 209             LookupCase l2 = new LookupCase(c2, m2);
 210             assert(l2.lookupClass() == c2); // [A1]
 211             assert((m1 | m2) == m1);        // [A2] (no elevation of access)
 212             return l2;
 213         }
 214 
 215         @Override
 216         public String toString() {
 217             String s = lookupClass().getSimpleName();
 218             String lstr = lookupString();
 219             int sl = lstr.indexOf('/');
 220             if (sl >= 0)  s += lstr.substring(sl);
 221             ClassLoader cld = lookupClass().getClassLoader();
 222             if (cld != THIS_LOADER)  s += "/loader#"+numberOf(cld);
 223             return s;
 224         }
 225 
 226         /** Predict the success or failure of accessing this method. */
 227         public boolean willAccess(Method m) {
 228             Class<?> c1 = lookupClass();
 229             Class<?> c2 = m.getDeclaringClass();
 230 
 231             // if the lookup class is in a loose module with PUBLIC access then
 232             // public members of public types in all unnamed modules can be accessed
 233             if (isLooseModule(c1.getModule())
 234                 && (lookupModes & PUBLIC) != 0
 235                 && (!c2.getModule().isNamed())
 236                 && Modifier.isPublic(c2.getModifiers())
 237                 && Modifier.isPublic(m.getModifiers()))
 238                 return true;
 239 
 240             LookupCase lc = this.in(c2);
 241             int m1 = lc.lookupModes();
 242             int m2 = fixMods(m.getModifiers());
 243             // privacy is strictly enforced on lookups
 244             if (c1 != c2)  m1 &= ~PRIVATE;
 245             // protected access is sometimes allowed
 246             if ((m2 & PROTECTED) != 0) {
 247                 int prev = m2;
 248                 m2 |= PACKAGE;  // it acts like a package method also
 249                 if ((lookupModes() & PROTECTED) != 0 &&
 250                     c2.isAssignableFrom(c1))
 251                     m2 |= PUBLIC;  // from a subclass, it acts like a public method also
 252             }
 253             if (verbosity >= 2)
 254                 System.out.println(this+" willAccess "+lc+" m1="+m1+" m2="+m2+" => "+((m2 & m1) != 0));
 255             return (m2 & m1) != 0;
 256         }
 257 
 258         /** Predict the success or failure of accessing this class. */
 259         public boolean willAccessClass(Class<?> c2, boolean load) {
 260             Class<?> c1 = lookupClass();
 261             if (load && c2.getClassLoader() != null) {
 262                 if (c1.getClassLoader() == null) {
 263                     // not visible
 264                 return false;
 265             }
 266                 if (c1 == publicLookup().lookupClass()) {
 267                     // not visible as lookup class is defined by child of the boot loader
 268                     return false;
 269                 }
 270             }
 271 
 272             // if the lookup class is in a loose module with PUBLIC access then
 273             // public types in all unnamed modules can be accessed
 274             if (isLooseModule(c1.getModule())
 275                 && (lookupModes & PUBLIC) != 0
 276                 && (!c2.getModule().isNamed())
 277                 && Modifier.isPublic(c2.getModifiers()))
 278                 return true;
 279 
 280             LookupCase lc = this.in(c2);
 281             int m1 = lc.lookupModes();
 282             boolean r = false;
 283             if (m1 == 0) {
 284                 r = false;
 285             } else {
 286                 int m2 = fixMods(c2.getModifiers());
 287                 if ((m2 & PUBLIC) != 0) {
 288                     r = true;
 289                 } else if ((m1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage()) {
 290                     r = true;
 291                 }
 292             }
 293             if (verbosity >= 2) {
 294                 System.out.println(this+" willAccessClass "+lc+" c1="+c1+" c2="+c2+" => "+r);
 295             }
 296             return r;
 297         }
 298 
 299         private boolean isLooseModule(Module m) {
 300             ClassLoader cl = new ClassLoader() { };
 301             return m.canRead(cl.getUnnamedModule());
 302         }
 303     }
 304 
 305     private static Class<?> topLevelClass(Class<?> cls) {
 306         Class<?> c = cls;
 307         for (Class<?> ec; (ec = c.getEnclosingClass()) != null; )
 308             c = ec;
 309         assert(c.getEnclosingClass() == null);
 310         assert(c == cls || cls.getEnclosingClass() != null);
 311         return c;
 312     }
 313 
 314     private static String packagePrefix(Class<?> c) {
 315         while (c.isArray())  c = c.getComponentType();
 316         String s = c.getName();
 317         assert(s.indexOf('/') < 0);
 318         return s.substring(0, s.lastIndexOf('.')+1);
 319     }
 320 
 321 
 322     private final TreeSet<LookupCase> CASES = new TreeSet<>();
 323     private final TreeMap<LookupCase,TreeSet<LookupCase>> CASE_EDGES = new TreeMap<>();
 324     private final ArrayList<ClassLoader> LOADERS = new ArrayList<>();
 325     private final ClassLoader THIS_LOADER = this.getClass().getClassLoader();
 326     { if (THIS_LOADER != null)  LOADERS.add(THIS_LOADER); }  // #1
 327 
 328     private LookupCase lookupCase(String name) {
 329         for (LookupCase lc : CASES) {
 330             if (lc.toString().equals(name))
 331                 return lc;
 332         }
 333         throw new AssertionError(name);
 334     }
 335 
 336     private int numberOf(ClassLoader cl) {
 337         if (cl == null)  return 0;
 338         int i = LOADERS.indexOf(cl);
 339         if (i < 0) {
 340             i = LOADERS.size();
 341             LOADERS.add(cl);
 342         }
 343         return i+1;
 344     }
 345 
 346     private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2) {
 347         TreeSet<LookupCase> edges = CASE_EDGES.get(l2);
 348         if (edges == null)  CASE_EDGES.put(l2, edges = new TreeSet<>());
 349         if (edges.add(l1)) {
 350             Class<?> c1 = l1.lookupClass();
 351             assert(l2.lookupClass() == c2); // [A1]
 352             int m1 = l1.lookupModes();
 353             int m2 = l2.lookupModes();
 354             assert((m1 | m2) == m1);        // [A2] (no elevation of access)
 355             LookupCase expect = l1.in(c2);
 356             if (!expect.equals(l2))
 357                 System.out.println("*** expect "+l1+" => "+expect+" but got "+l2);
 358             assertEquals(l2, expect);
 359         }
 360     }
 361 
 362     private void makeCases(Lookup[] originalLookups) {
 363         // make initial set of lookup test cases
 364         CASES.clear(); LOADERS.clear(); CASE_EDGES.clear();
 365         ArrayList<Class<?>> classes = new ArrayList<>();
 366         for (Lookup l : originalLookups) {
 367             CASES.add(new LookupCase(l));
 368             classes.remove(l.lookupClass());  // no dups please
 369             classes.add(l.lookupClass());
 370         }
 371         System.out.println("loaders = "+LOADERS);
 372         int rounds = 0;
 373         for (int lastCount = -1; lastCount != CASES.size(); ) {
 374             lastCount = CASES.size();  // if CASES grow in the loop we go round again
 375             for (LookupCase lc1 : CASES.toArray(new LookupCase[0])) {
 376                 for (Class<?> c2 : classes) {
 377                     LookupCase lc2 = new LookupCase(lc1.lookup().in(c2));
 378                     addLookupEdge(lc1, c2, lc2);
 379                     CASES.add(lc2);
 380                 }
 381             }
 382             rounds++;
 383         }
 384         System.out.println("filled in "+CASES.size()+" cases from "+originalLookups.length+" original cases in "+rounds+" rounds");
 385         if (false) {
 386             System.out.println("CASES: {");
 387             for (LookupCase lc : CASES) {
 388                 System.out.println(lc);
 389                 Set<LookupCase> edges = CASE_EDGES.get(lc);
 390                 if (edges != null)
 391                     for (LookupCase prev : edges) {
 392                         System.out.println("\t"+prev);
 393                     }
 394             }
 395             System.out.println("}");
 396         }
 397     }
 398 
 399     @Test public void test() {
 400         makeCases(lookups());
 401         if (verbosity > 0) {
 402             verbosity += 9;
 403             Method pro_in_self = targetMethod(THIS_CLASS, PROTECTED, methodType(void.class));
 404             testOneAccess(lookupCase("AccessControlTest/public"),  pro_in_self, "find");
 405             testOneAccess(lookupCase("Remote_subclass/public"),    pro_in_self, "find");
 406             testOneAccess(lookupCase("Remote_subclass"),           pro_in_self, "find");
 407             verbosity -= 9;
 408         }
 409         Set<Class<?>> targetClassesDone = new HashSet<>();
 410         for (LookupCase targetCase : CASES) {
 411             Class<?> targetClass = targetCase.lookupClass();
 412             if (!targetClassesDone.add(targetClass))  continue;  // already saw this one
 413             String targetPlace = placeName(targetClass);
 414             if (targetPlace == null)  continue;  // Object, String, not a target
 415             for (int targetAccess : ACCESS_CASES) {
 416                 MethodType methodType = methodType(void.class);
 417                 Method method = targetMethod(targetClass, targetAccess, methodType);
 418                 // Try to access target method from various contexts.
 419                 for (LookupCase sourceCase : CASES) {
 420                     testOneAccess(sourceCase, method, "findClass");
 421                     testOneAccess(sourceCase, method, "accessClass");
 422                     testOneAccess(sourceCase, method, "find");
 423                     testOneAccess(sourceCase, method, "unreflect");
 424                 }
 425             }
 426         }
 427         System.out.println("tested "+testCount+" access scenarios; "+testCountFails+" accesses were denied");
 428     }
 429 
 430     private int testCount, testCountFails;
 431 
 432     private void testOneAccess(LookupCase sourceCase, Method method, String kind) {
 433         Class<?> targetClass = method.getDeclaringClass();
 434         String methodName = method.getName();
 435         MethodType methodType = methodType(method.getReturnType(), method.getParameterTypes());
 436         boolean isFindOrAccessClass = "findClass".equals(kind) || "accessClass".equals(kind);
 437         boolean willAccess = isFindOrAccessClass ?
 438                 sourceCase.willAccessClass(targetClass, "findClass".equals(kind)) : sourceCase.willAccess(method);
 439         boolean didAccess = false;
 440         ReflectiveOperationException accessError = null;
 441         try {
 442             switch (kind) {
 443             case "accessClass":
 444                 sourceCase.lookup().accessClass(targetClass);
 445                 break;
 446             case "findClass":
 447                 sourceCase.lookup().findClass(targetClass.getName());
 448                 break;
 449             case "find":
 450                 if ((method.getModifiers() & Modifier.STATIC) != 0)
 451                     sourceCase.lookup().findStatic(targetClass, methodName, methodType);
 452                 else
 453                     sourceCase.lookup().findVirtual(targetClass, methodName, methodType);
 454                 break;
 455             case "unreflect":
 456                 sourceCase.lookup().unreflect(method);
 457                 break;
 458             default:
 459                 throw new AssertionError(kind);
 460             }
 461             didAccess = true;
 462         } catch (ReflectiveOperationException ex) {
 463             accessError = ex;
 464         }
 465         if (willAccess != didAccess) {
 466             System.out.println(sourceCase+" => "+targetClass.getSimpleName()+(isFindOrAccessClass?"":"."+methodName+methodType));
 467             System.out.println("fail "+(isFindOrAccessClass?kind:"on "+method)+" ex="+accessError);
 468             assertEquals(willAccess, didAccess);
 469         }
 470         testCount++;
 471         if (!didAccess)  testCountFails++;
 472     }
 473 
 474     static Method targetMethod(Class<?> targetClass, int targetAccess, MethodType methodType) {
 475         assert targetAccess != MODULE;
 476         String methodName = accessName(targetAccess)+placeName(targetClass);
 477         if (verbosity >= 2)
 478             System.out.println(targetClass.getSimpleName()+"."+methodName+methodType);
 479         try {
 480             Method method = targetClass.getDeclaredMethod(methodName, methodType.parameterArray());
 481             assertEquals(method.getReturnType(), methodType.returnType());
 482             int haveMods = method.getModifiers();
 483             assert(Modifier.isStatic(haveMods));
 484             assert(targetAccess == fixMods(haveMods));
 485             return method;
 486         } catch (NoSuchMethodException ex) {
 487             throw new AssertionError(methodName, ex);
 488         }
 489     }
 490 
 491     static String placeName(Class<?> cls) {
 492         // return "self", "sibling", "nestmate", etc.
 493         if (cls == AccessControlTest.class)  return "self";
 494         String cln = cls.getSimpleName();
 495         int under = cln.lastIndexOf('_');
 496         if (under < 0)  return null;
 497         return cln.substring(under+1);
 498     }
 499     static String accessName(int acc) {
 500         switch (acc) {
 501         case PUBLIC:     return "pub_in_";
 502         case PROTECTED:  return "pro_in_";
 503         case PACKAGE:    return "pkg_in_";
 504         case PRIVATE:    return "pri_in_";
 505         }
 506         assert(false);
 507         return "?";
 508     }
 509     // MODULE not a test case at this time
 510     private static final int[] ACCESS_CASES = {
 511         PUBLIC, PACKAGE, PRIVATE, PROTECTED
 512     };
 513     /** Return one of the ACCESS_CASES. */
 514     static int fixMods(int mods) {
 515         mods &= (PUBLIC|PRIVATE|PROTECTED);
 516         switch (mods) {
 517         case PUBLIC: case PRIVATE: case PROTECTED: return mods;
 518         case 0:  return PACKAGE;
 519         }
 520         throw new AssertionError(mods);
 521     }
 522 
 523     static Lookup[] lookups() {
 524         ArrayList<Lookup> tem = new ArrayList<>();
 525         Collections.addAll(tem,
 526                            AccessControlTest.lookup_in_self(),
 527                            Inner_nestmate.lookup_in_nestmate(),
 528                            AccessControlTest_sibling.lookup_in_sibling());
 529         if (true) {
 530             Collections.addAll(tem,Acquaintance_remote.lookups());
 531         } else {
 532             try {
 533                 Class<?> remc = Class.forName("test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote");
 534                 Lookup[] remls = (Lookup[]) remc.getMethod("lookups").invoke(null);
 535                 Collections.addAll(tem, remls);
 536             } catch (ReflectiveOperationException ex) {
 537                 throw new LinkageError("reflection failed", ex);
 538             }
 539         }
 540         tem.add(publicLookup());
 541         tem.add(publicLookup().in(String.class));
 542         tem.add(publicLookup().in(List.class));
 543         return tem.toArray(new Lookup[0]);
 544     }
 545 
 546     static Lookup lookup_in_self() {
 547         return MethodHandles.lookup();
 548     }
 549     public static      void pub_in_self() { }
 550     protected static   void pro_in_self() { }
 551     static /*package*/ void pkg_in_self() { }
 552     private static     void pri_in_self() { }
 553 
 554     static class Inner_nestmate {
 555         static Lookup lookup_in_nestmate() {
 556             return MethodHandles.lookup();
 557         }
 558         public static      void pub_in_nestmate() { }
 559         protected static   void pro_in_nestmate() { }
 560         static /*package*/ void pkg_in_nestmate() { }
 561         private static     void pri_in_nestmate() { }
 562     }
 563 }
 564 class AccessControlTest_sibling {
 565     static Lookup lookup_in_sibling() {
 566         return MethodHandles.lookup();
 567     }
 568     public static      void pub_in_sibling() { }
 569     protected static   void pro_in_sibling() { }
 570     static /*package*/ void pkg_in_sibling() { }
 571     private static     void pri_in_sibling() { }
 572 }
 573 
 574 // This guy tests access from outside the package:
 575 /*
 576 package test.java.lang.invoke.AccessControlTest_subpkg;
 577 public class Acquaintance_remote {
 578     public static Lookup[] lookups() { ...
 579     }
 580     ...
 581 }
 582 */