1 /*
2  * Copyright (c) 2012, 2019, 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.annotations.*;
37 
38 import static java.lang.invoke.MethodHandles.*;
39 import static java.lang.invoke.MethodHandles.Lookup.*;
40 import static java.lang.invoke.MethodType.*;
41 import static org.testng.Assert.*;
42 
43 import test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote;
44 
45 
46 /**
47  * Test many combinations of Lookup access and cross-class lookupStatic.
48  * @author jrose
49  */
50 public class AccessControlTest {
51     static final Class<?> THIS_CLASS = AccessControlTest.class;
52     // How much output?
53     static int verbosity = 0;
54     static {
55         String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbosity");
56         if (vstr == null)
57             vstr = System.getProperty(THIS_CLASS.getName()+".verbosity");
58         if (vstr != null)  verbosity = Integer.parseInt(vstr);
59     }
60 
61     private class LookupCase implements Comparable<LookupCase> {
62         final Lookup   lookup;
63         final Class<?> lookupClass;
64         final Class<?> prevLookupClass;
65         final int      lookupModes;
LookupCase(Lookup lookup)66         public LookupCase(Lookup lookup) {
67             this.lookup = lookup;
68             this.lookupClass = lookup.lookupClass();
69             this.prevLookupClass = lookup.previousLookupClass();
70             this.lookupModes = lookup.lookupModes();
71 
72             assert(lookupString().equals(lookup.toString()));
73             numberOf(lookupClass().getClassLoader()); // assign CL#
74         }
LookupCase(Class<?> lookupClass, Class<?> prevLookupClass, int lookupModes)75         public LookupCase(Class<?> lookupClass, Class<?> prevLookupClass, int lookupModes) {
76             this.lookup = null;
77             this.lookupClass = lookupClass;
78             this.prevLookupClass = prevLookupClass;
79             this.lookupModes = lookupModes;
80             numberOf(lookupClass().getClassLoader()); // assign CL#
81         }
82 
lookupClass()83         public final Class<?> lookupClass()     { return lookupClass; }
prevLookupClass()84         public final Class<?> prevLookupClass() { return prevLookupClass; }
lookupModes()85         public final int      lookupModes()     { return lookupModes; }
86 
lookup()87         public Lookup lookup() { lookup.getClass(); return lookup; }
88 
89         @Override
compareTo(LookupCase that)90         public int compareTo(LookupCase that) {
91             Class<?> c1 = this.lookupClass();
92             Class<?> c2 = that.lookupClass();
93             Class<?> p1 = this.prevLookupClass();
94             Class<?> p2 = that.prevLookupClass();
95             if (c1 != c2) {
96                 int cmp = c1.getName().compareTo(c2.getName());
97                 if (cmp != 0)  return cmp;
98                 cmp = numberOf(c1.getClassLoader()) - numberOf(c2.getClassLoader());
99                 assert(cmp != 0);
100                 return cmp;
101             } else if (p1 != p2){
102                 if (p1 == null)
103                     return 1;
104                 else if (p2 == null)
105                     return -1;
106                 int cmp = p1.getName().compareTo(p2.getName());
107                 if (cmp != 0)  return cmp;
108                 cmp = numberOf(p1.getClassLoader()) - numberOf(p2.getClassLoader());
109                 assert(cmp != 0);
110                 return cmp;
111             }
112             return -(this.lookupModes() - that.lookupModes());
113         }
114 
115         @Override
equals(Object that)116         public boolean equals(Object that) {
117             return (that instanceof LookupCase && equals((LookupCase)that));
118         }
equals(LookupCase that)119         public boolean equals(LookupCase that) {
120             return (this.lookupClass() == that.lookupClass() &&
121                     this.prevLookupClass() == that.prevLookupClass() &&
122                     this.lookupModes() == that.lookupModes());
123         }
124 
125         @Override
hashCode()126         public int hashCode() {
127             return lookupClass().hashCode() + (lookupModes() * 31);
128         }
129 
130         /** Simulate all assertions in the spec. for Lookup.toString. */
lookupString()131         private String lookupString() {
132             String name = lookupClass.getName();
133             if (prevLookupClass != null)
134                 name += "/" + prevLookupClass.getName();
135             String suffix = "";
136             if (lookupModes == 0)
137                 suffix = "/noaccess";
138             else if (lookupModes == PUBLIC)
139                 suffix = "/public";
140              else if (lookupModes == UNCONDITIONAL)
141                 suffix = "/publicLookup";
142             else if (lookupModes == (PUBLIC|MODULE))
143                 suffix = "/module";
144             else if (lookupModes == (PUBLIC|PACKAGE)
145                      || lookupModes == (PUBLIC|MODULE|PACKAGE))
146                 suffix = "/package";
147             else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE)
148                     || lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
149                 suffix = "/private";
150             else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE|PROTECTED)
151                      || lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED))
152                 suffix = "";
153             else
154                 suffix = "/#"+Integer.toHexString(lookupModes);
155             return name+suffix;
156         }
157 
158         /** 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 specified
162          * class as its own {@link #lookupClass lookupClass}.
163          * [A2] However, the resulting {@code Lookup} object is guaranteed
164          * to have no more access capabilities than the original.
165          * In particular, access capabilities can be lost as follows:<ul>
166          * [A3] If the new lookup class is in a different module from the old one,
167          * i.e. {@link #MODULE MODULE} access is lost.
168          * [A4] If the new lookup class is in a different package
169          * than the old one, protected and default (package) members will not be accessible,
170          * i.e. {@link #PROTECTED PROTECTED} and {@link #PACKAGE PACKAGE} access are lost.
171          * [A5] If the new lookup class is not within the same package member
172          * as the old one, private members will not be accessible, and protected members
173          * will not be accessible by virtue of inheritance,
174          * i.e. {@link #PRIVATE PRIVATE} access is lost.
175          * (Protected members may continue to be accessible because of package sharing.)
176          * [A6] If the new lookup class is not
177          * {@linkplain #accessClass(Class) accessible} to this lookup,
178          * then no members, not even public members, will be accessible
179          * i.e. all access modes are lost.
180          * [A7] If the new lookup class, the old lookup class and the previous lookup class
181          * are all in different modules i.e. teleporting to a third module,
182          * all access modes are lost.
183          * <p>
184          * The new previous lookup class is chosen as follows:
185          * [A8] If the new lookup object has {@link #UNCONDITIONAL UNCONDITIONAL} bit,
186          * the new previous lookup class is {@code null}.
187          * [A9] If the new lookup class is in the same module as the old lookup class,
188          * the new previous lookup class is the old previous lookup class.
189          * [A10] If the new lookup class is in a different module from the old lookup class,
190          * the new previous lookup class is the the old lookup class.
191          *
192          * Other than the above cases, the new lookup will have the same
193          * access capabilities as the original. [A11]
194          * <hr>
195          */
in(Class<?> c2)196         public LookupCase in(Class<?> c2) {
197             Class<?> c1 = lookupClass();
198             Module m1 = c1.getModule();
199             Module m2 = c2.getModule();
200             Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : c1.getModule();
201             int modes1 = lookupModes();
202             int changed = 0;
203             // for the purposes of access control then treat classes in different unnamed
204             // modules as being in the same module.
205             boolean sameModule = (m1 == m2) ||
206                                  (!m1.isNamed() && !m2.isNamed());
207             boolean samePackage = (c1.getClassLoader() == c2.getClassLoader() &&
208                                    c1.getPackageName().equals(c2.getPackageName()));
209             boolean sameTopLevel = (topLevelClass(c1) == topLevelClass(c2));
210             boolean sameClass = (c1 == c2);
211             assert(samePackage  || !sameTopLevel);
212             assert(sameTopLevel || !sameClass);
213             boolean accessible = sameClass;
214 
215             if ((modes1 & PACKAGE) != 0)  accessible |= samePackage;
216             if ((modes1 & PUBLIC ) != 0)  {
217                 if (isModuleAccessible(c2))
218                     accessible |= (c2.getModifiers() & PUBLIC) != 0;
219                 else
220                     accessible = false;
221             }
222             if ((modes1 & UNCONDITIONAL) != 0) {
223                 if (m2.isExported(c2.getPackageName()))
224                     accessible |= (c2.getModifiers() & PUBLIC) != 0;
225                 else
226                     accessible = false;
227             }
228             if (!accessible) {
229                 // no access to c2; lose all access.
230                 changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|UNCONDITIONAL);  // [A6]
231             }
232             if (m2 != m1 && m0 != m1) {
233                 // hop to a third module; lose all access
234                 changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED);  // [A7]
235             }
236             if (!sameModule) {
237                 changed |= MODULE;  // [A3]
238             }
239             if (!samePackage) {
240                 // Different package; loose PACKAGE and lower access.
241                 changed |= (PACKAGE|PRIVATE|PROTECTED);  // [A4]
242             }
243             if (!sameTopLevel) {
244                 // Different top-level class.  Lose PRIVATE and PROTECTED access.
245                 changed |= (PRIVATE|PROTECTED);  // [A5]
246             }
247             if (sameClass) {
248                 assert(changed == 0);       // [A11] (no deprivation if same class)
249             }
250 
251             if (accessible)  assert((changed & PUBLIC) == 0);
252             int modes2 = modes1 & ~changed;
253             Class<?> plc = (m1 == m2) ? prevLookupClass() : c1; // [A9] [A10]
254             if ((modes1 & UNCONDITIONAL) != 0) plc = null;      // [A8]
255             LookupCase l2 = new LookupCase(c2, plc, modes2);
256             assert(l2.lookupClass() == c2);         // [A1]
257             assert((modes1 | modes2) == modes1);    // [A2] (no elevation of access)
258             assert(l2.prevLookupClass() == null || (modes2 & MODULE) == 0);
259             return l2;
260         }
261 
dropLookupMode(int modeToDrop)262         LookupCase dropLookupMode(int modeToDrop) {
263             int oldModes = lookupModes();
264             int newModes = oldModes & ~(modeToDrop | PROTECTED);
265             switch (modeToDrop) {
266                 case PUBLIC: newModes &= ~(MODULE|PACKAGE|PROTECTED|PRIVATE); break;
267                 case MODULE: newModes &= ~(PACKAGE|PRIVATE); break;
268                 case PACKAGE: newModes &= ~(PRIVATE); break;
269                 case PROTECTED:
270                 case PRIVATE:
271                 case UNCONDITIONAL: break;
272                 default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");
273             }
274             if (newModes == oldModes) return this;  // return self if no change
275             LookupCase l2 = new LookupCase(lookupClass(), prevLookupClass(), newModes);
276             assert((oldModes | newModes) == oldModes);    // [A2] (no elevation of access)
277             assert(l2.prevLookupClass() == null || (newModes & MODULE) == 0);
278             return l2;
279         }
280 
isModuleAccessible(Class<?> c)281         boolean isModuleAccessible(Class<?> c) {
282             Module m1 = lookupClass().getModule();
283             Module m2 = c.getModule();
284             Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : m1;
285             String pn = c.getPackageName();
286             boolean accessible = m1.canRead(m2) && m2.isExported(pn, m1);
287             if (m1 != m0) {
288                 accessible = accessible && m0.canRead(m2) && m2.isExported(pn, m0);
289             }
290             return accessible;
291         }
292 
293         @Override
toString()294         public String toString() {
295             String s = lookupClass().getSimpleName();
296             String lstr = lookupString();
297             int sl = lstr.indexOf('/');
298             if (sl >= 0)  s += lstr.substring(sl);
299             ClassLoader cld = lookupClass().getClassLoader();
300             if (cld != THIS_LOADER)  s += "/loader#"+numberOf(cld);
301             return s;
302         }
303 
304         /** Predict the success or failure of accessing this method. */
willAccess(Method m)305         public boolean willAccess(Method m) {
306             Class<?> c1 = lookupClass();
307             Class<?> c2 = m.getDeclaringClass();
308             Module m1 = c1.getModule();
309             Module m2 = c2.getModule();
310             Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;
311             // unconditional has access to all public types/members of types that is in a package
312             // are unconditionally exported
313             if ((lookupModes & UNCONDITIONAL) != 0) {
314                 return m2.isExported(c2.getPackageName())
315                        && Modifier.isPublic(c2.getModifiers())
316                        && Modifier.isPublic(m.getModifiers());
317             }
318 
319             // c1 and c2 are in different module
320             if (m1 != m2 || m0 != m2) {
321                 return (lookupModes & PUBLIC) != 0
322                        && isModuleAccessible(c2)
323                        && Modifier.isPublic(c2.getModifiers())
324                        && Modifier.isPublic(m.getModifiers());
325             }
326 
327             assert(m1 == m2 && prevLookupClass == null);
328 
329             if (!willAccessClass(c2, false))
330                 return false;
331 
332             LookupCase lc = this.in(c2);
333             int modes1 = lc.lookupModes();
334             int modes2 = fixMods(m.getModifiers());
335             // allow private lookup on nestmates. Otherwise, privacy is strictly enforced
336             if (c1 != c2 && ((modes2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {
337                 modes1 &= ~PRIVATE;
338             }
339             // protected access is sometimes allowed
340             if ((modes2 & PROTECTED) != 0) {
341                 int prev = modes2;
342                 modes2 |= PACKAGE;  // it acts like a package method also
343                 if ((lookupModes() & PROTECTED) != 0 &&
344                     c2.isAssignableFrom(c1))
345                     modes2 |= PUBLIC;  // from a subclass, it acts like a public method also
346             }
347             if (verbosity >= 2)
348                 System.out.format("%s willAccess %s modes1=0x%h modes2=0x%h => %s%n", lookupString(), lc.lookupString(), modes1, modes2, (modes2 & modes1) != 0);
349             return (modes2 & modes1) != 0;
350         }
351 
352         /** Predict the success or failure of accessing this class. */
willAccessClass(Class<?> c2, boolean load)353         public boolean willAccessClass(Class<?> c2, boolean load) {
354             Class<?> c1 = lookupClass();
355             if (load && c2.getClassLoader() != null) {
356                 if (c1.getClassLoader() == null) {
357                     // not visible
358                     return false;
359                 }
360             }
361 
362             Module m1 = c1.getModule();
363             Module m2 = c2.getModule();
364             Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;
365             // unconditional has access to all public types that is in an unconditionally exported package
366             if ((lookupModes & UNCONDITIONAL) != 0) {
367                 return m2.isExported(c2.getPackageName()) && Modifier.isPublic(c2.getModifiers());
368             }
369             // c1 and c2 are in different module
370             if (m1 != m2 || m0 != m2) {
371                 return (lookupModes & PUBLIC) != 0
372                     && isModuleAccessible(c2)
373                     && Modifier.isPublic(c2.getModifiers());
374             }
375 
376             assert(m1 == m2 && prevLookupClass == null);
377 
378             LookupCase lc = this.in(c2);
379             int modes1 = lc.lookupModes();
380             boolean r = false;
381             if (modes1 == 0) {
382                 r = false;
383             } else {
384                 if (Modifier.isPublic(c2.getModifiers())) {
385                     if ((modes1 & MODULE) != 0)
386                         r = true;
387                     else if ((modes1 & PUBLIC) != 0)
388                         r = m1.isExported(c2.getPackageName());
389                 } else {
390                     if ((modes1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage())
391                         r = true;
392                 }
393             }
394             if (verbosity >= 2) {
395                 System.out.println(this+" willAccessClass "+lc+" c1="+c1+" c2="+c2+" => "+r);
396             }
397             return r;
398         }
399     }
400 
topLevelClass(Class<?> cls)401     private static Class<?> topLevelClass(Class<?> cls) {
402         Class<?> c = cls;
403         for (Class<?> ec; (ec = c.getEnclosingClass()) != null; )
404             c = ec;
405         assert(c.getEnclosingClass() == null);
406         assert(c == cls || cls.getEnclosingClass() != null);
407         return c;
408     }
409 
410     private final TreeSet<LookupCase> CASES = new TreeSet<>();
411     private final TreeMap<LookupCase,TreeSet<LookupCase>> CASE_EDGES = new TreeMap<>();
412     private final ArrayList<ClassLoader> LOADERS = new ArrayList<>();
413     private final ClassLoader THIS_LOADER = this.getClass().getClassLoader();
414     { if (THIS_LOADER != null)  LOADERS.add(THIS_LOADER); }  // #1
415 
lookupCase(String name)416     private LookupCase lookupCase(String name) {
417         for (LookupCase lc : CASES) {
418             if (lc.toString().equals(name))
419                 return lc;
420         }
421         throw new AssertionError(name);
422     }
423 
numberOf(ClassLoader cl)424     private int numberOf(ClassLoader cl) {
425         if (cl == null)  return 0;
426         int i = LOADERS.indexOf(cl);
427         if (i < 0) {
428             i = LOADERS.size();
429             LOADERS.add(cl);
430         }
431         return i+1;
432     }
433 
addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2, int dropAccess)434     private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2, int dropAccess) {
435         TreeSet<LookupCase> edges = CASE_EDGES.get(l2);
436         if (edges == null)  CASE_EDGES.put(l2, edges = new TreeSet<>());
437         if (edges.add(l1)) {
438             Class<?> c1 = l1.lookupClass();
439             assert(l2.lookupClass() == c2); // [A1]
440             int m1 = l1.lookupModes();
441             int m2 = l2.lookupModes();
442             assert((m1 | m2) == m1);        // [A2] (no elevation of access)
443             LookupCase expect = dropAccess == 0 ? l1.in(c2) : l1.in(c2).dropLookupMode(dropAccess);
444             if (!expect.equals(l2))
445                 System.out.println("*** expect "+l1+" => "+expect+" but got "+l2);
446             assertEquals(l2, expect);
447         }
448     }
449 
makeCases(Lookup[] originalLookups)450     private void makeCases(Lookup[] originalLookups) {
451         // make initial set of lookup test cases
452         CASES.clear(); LOADERS.clear(); CASE_EDGES.clear();
453         ArrayList<Class<?>> classes = new ArrayList<>();
454         for (Lookup l : originalLookups) {
455             CASES.add(new LookupCase(l));
456             classes.remove(l.lookupClass());  // no dups please
457             classes.add(l.lookupClass());
458         }
459         System.out.println("loaders = "+LOADERS);
460         int rounds = 0;
461         for (int lastCount = -1; lastCount != CASES.size(); ) {
462             lastCount = CASES.size();  // if CASES grow in the loop we go round again
463             for (LookupCase lc1 : CASES.toArray(new LookupCase[0])) {
464                 for (int mode : ACCESS_CASES) {
465                     LookupCase lc2 = new LookupCase(lc1.lookup().dropLookupMode(mode));
466                     addLookupEdge(lc1, lc1.lookupClass(), lc2, mode);
467                     CASES.add(lc2);
468                 }
469                 for (Class<?> c2 : classes) {
470                     LookupCase lc2 = new LookupCase(lc1.lookup().in(c2));
471                     addLookupEdge(lc1, c2, lc2, 0);
472                     CASES.add(lc2);
473                 }
474             }
475             rounds++;
476         }
477         System.out.println("filled in "+CASES.size()+" cases from "+originalLookups.length+" original cases in "+rounds+" rounds");
478         if (false) {
479             System.out.println("CASES: {");
480             for (LookupCase lc : CASES) {
481                 System.out.println(lc);
482                 Set<LookupCase> edges = CASE_EDGES.get(lc);
483                 if (edges != null)
484                     for (LookupCase prev : edges) {
485                         System.out.println("\t"+prev);
486                     }
487             }
488             System.out.println("}");
489         }
490     }
491 
test()492     @Test public void test() {
493         makeCases(lookups());
494         if (verbosity > 0) {
495             verbosity += 9;
496             Method pro_in_self = targetMethod(THIS_CLASS, PROTECTED, methodType(void.class));
497             testOneAccess(lookupCase("AccessControlTest/module"),  pro_in_self, "find");
498             testOneAccess(lookupCase("Remote_subclass/module"),    pro_in_self, "find");
499             testOneAccess(lookupCase("Remote_subclass"),           pro_in_self, "find");
500             verbosity -= 9;
501         }
502         Set<Class<?>> targetClassesDone = new HashSet<>();
503         for (LookupCase targetCase : CASES) {
504             Class<?> targetClass = targetCase.lookupClass();
505             if (!targetClassesDone.add(targetClass))  continue;  // already saw this one
506             String targetPlace = placeName(targetClass);
507             if (targetPlace == null)  continue;  // Object, String, not a target
508             for (int targetAccess : ACCESS_CASES) {
509                 if (targetAccess == MODULE || targetAccess == UNCONDITIONAL)
510                     continue;
511                 MethodType methodType = methodType(void.class);
512                 Method method = targetMethod(targetClass, targetAccess, methodType);
513                 // Try to access target method from various contexts.
514                 for (LookupCase sourceCase : CASES) {
515                     testOneAccess(sourceCase, method, "findClass");
516                     testOneAccess(sourceCase, method, "accessClass");
517                     testOneAccess(sourceCase, method, "find");
518                     testOneAccess(sourceCase, method, "unreflect");
519                 }
520             }
521         }
522         System.out.println("tested "+testCount+" access scenarios; "+testCountFails+" accesses were denied");
523     }
524 
525     private int testCount, testCountFails;
526 
testOneAccess(LookupCase sourceCase, Method method, String kind)527     private void testOneAccess(LookupCase sourceCase, Method method, String kind) {
528         Class<?> targetClass = method.getDeclaringClass();
529         String methodName = method.getName();
530         MethodType methodType = methodType(method.getReturnType(), method.getParameterTypes());
531         boolean isFindOrAccessClass = "findClass".equals(kind) || "accessClass".equals(kind);
532         boolean willAccess = isFindOrAccessClass ?
533                 sourceCase.willAccessClass(targetClass, "findClass".equals(kind)) : sourceCase.willAccess(method);
534         boolean didAccess = false;
535         ReflectiveOperationException accessError = null;
536         try {
537             switch (kind) {
538             case "accessClass":
539                 sourceCase.lookup().accessClass(targetClass);
540                 break;
541             case "findClass":
542                 sourceCase.lookup().findClass(targetClass.getName());
543                 break;
544             case "find":
545                 if ((method.getModifiers() & Modifier.STATIC) != 0)
546                     sourceCase.lookup().findStatic(targetClass, methodName, methodType);
547                 else
548                     sourceCase.lookup().findVirtual(targetClass, methodName, methodType);
549                 break;
550             case "unreflect":
551                 sourceCase.lookup().unreflect(method);
552                 break;
553             default:
554                 throw new AssertionError(kind);
555             }
556             didAccess = true;
557         } catch (ReflectiveOperationException ex) {
558             accessError = ex;
559         }
560         if (willAccess != didAccess) {
561             System.out.println(sourceCase+" => "+targetClass.getSimpleName()+(isFindOrAccessClass?"":"."+methodName+methodType));
562             System.out.println("fail "+(isFindOrAccessClass?kind:"on "+method)+" ex="+accessError);
563             assertEquals(willAccess, didAccess);
564         }
565         testCount++;
566         if (!didAccess)  testCountFails++;
567     }
568 
targetMethod(Class<?> targetClass, int targetAccess, MethodType methodType)569     static Method targetMethod(Class<?> targetClass, int targetAccess, MethodType methodType) {
570         String methodName = accessName(targetAccess)+placeName(targetClass);
571         if (verbosity >= 2)
572             System.out.println(targetClass.getSimpleName()+"."+methodName+methodType);
573         try {
574             Method method = targetClass.getDeclaredMethod(methodName, methodType.parameterArray());
575             assertEquals(method.getReturnType(), methodType.returnType());
576             int haveMods = method.getModifiers();
577             assert(Modifier.isStatic(haveMods));
578             assert(targetAccess == fixMods(haveMods));
579             return method;
580         } catch (NoSuchMethodException ex) {
581             throw new AssertionError(methodName, ex);
582         }
583     }
584 
placeName(Class<?> cls)585     static String placeName(Class<?> cls) {
586         // return "self", "sibling", "nestmate", etc.
587         if (cls == AccessControlTest.class)  return "self";
588         String cln = cls.getSimpleName();
589         int under = cln.lastIndexOf('_');
590         if (under < 0)  return null;
591         return cln.substring(under+1);
592     }
accessName(int acc)593     static String accessName(int acc) {
594         switch (acc) {
595         case PUBLIC:     return "pub_in_";
596         case PROTECTED:  return "pro_in_";
597         case PACKAGE:    return "pkg_in_";
598         case PRIVATE:    return "pri_in_";
599         }
600         assert(false);
601         return "?";
602     }
603     private static final int[] ACCESS_CASES = {
604         PUBLIC, PACKAGE, PRIVATE, PROTECTED, MODULE, UNCONDITIONAL
605     };
606     /*
607      * Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL
608      * Adjust 0 => PACKAGE
609      */
610     /** Return one of the ACCESS_CASES. */
fixMods(int mods)611     static int fixMods(int mods) {
612         mods &= (PUBLIC|PRIVATE|PROTECTED);
613         switch (mods) {
614         case PUBLIC: case PRIVATE: case PROTECTED: return mods;
615         case 0:  return PACKAGE;
616         }
617         throw new AssertionError(mods);
618     }
619 
lookups()620     static Lookup[] lookups() {
621         ArrayList<Lookup> tem = new ArrayList<>();
622         Collections.addAll(tem,
623                            AccessControlTest.lookup_in_self(),
624                            Inner_nestmate.lookup_in_nestmate(),
625                            AccessControlTest_sibling.lookup_in_sibling());
626         if (true) {
627             Collections.addAll(tem,Acquaintance_remote.lookups());
628         } else {
629             try {
630                 Class<?> remc = Class.forName("test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote");
631                 Lookup[] remls = (Lookup[]) remc.getMethod("lookups").invoke(null);
632                 Collections.addAll(tem, remls);
633             } catch (ReflectiveOperationException ex) {
634                 throw new LinkageError("reflection failed", ex);
635             }
636         }
637         tem.add(publicLookup());
638         tem.add(publicLookup().in(String.class));
639         tem.add(publicLookup().in(List.class));
640         return tem.toArray(new Lookup[0]);
641     }
642 
lookup_in_self()643     static Lookup lookup_in_self() {
644         return MethodHandles.lookup();
645     }
pub_in_self()646     public static      void pub_in_self() { }
pro_in_self()647     protected static   void pro_in_self() { }
pkg_in_self()648     static /*package*/ void pkg_in_self() { }
pri_in_self()649     private static     void pri_in_self() { }
650 
651     static class Inner_nestmate {
lookup_in_nestmate()652         static Lookup lookup_in_nestmate() {
653             return MethodHandles.lookup();
654         }
pub_in_nestmate()655         public static      void pub_in_nestmate() { }
pro_in_nestmate()656         protected static   void pro_in_nestmate() { }
pkg_in_nestmate()657         static /*package*/ void pkg_in_nestmate() { }
pri_in_nestmate()658         private static     void pri_in_nestmate() { }
659     }
660 }
661 class AccessControlTest_sibling {
lookup_in_sibling()662     static Lookup lookup_in_sibling() {
663         return MethodHandles.lookup();
664     }
pub_in_sibling()665     public static      void pub_in_sibling() { }
pro_in_sibling()666     protected static   void pro_in_sibling() { }
pkg_in_sibling()667     static /*package*/ void pkg_in_sibling() { }
pri_in_sibling()668     private static     void pri_in_sibling() { }
669 }
670 
671 // This guy tests access from outside the package:
672 /*
673 package test.java.lang.invoke.AccessControlTest_subpkg;
674 public class Acquaintance_remote {
675     public static Lookup[] lookups() { ...
676     }
677     ...
678 }
679 */
680