1 /*
   2  * Copyright (c) 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 package jdk.test;
  25 
  26 import org.testng.annotations.BeforeTest;
  27 import org.testng.annotations.DataProvider;
  28 import org.testng.annotations.Test;
  29 
  30 import java.lang.invoke.MethodHandle;
  31 import java.lang.invoke.MethodHandles;
  32 import java.lang.invoke.MethodHandles.Lookup;
  33 
  34 import java.lang.invoke.MethodType;
  35 import java.lang.reflect.Method;
  36 import java.util.HashMap;
  37 import java.util.Map;
  38 import java.util.Set;
  39 import java.util.stream.Collectors;
  40 import java.util.stream.Stream;
  41 
  42 import e1.CrackM5Access;
  43 
  44 import static java.lang.invoke.MethodHandles.Lookup.*;
  45 import static org.testng.Assert.*;
  46 
  47 public class ModuleAccessTest {
  48     static ModuleLookup m3;
  49     static ModuleLookup m4;
  50     static ModuleLookup m5;
  51     static Map<String, ModuleLookup> moduleLookupMap = new HashMap<>();
  52     static Lookup privLookupIn;
  53     static Lookup privLookupIn2;
  54     static Lookup unnamedLookup;
  55     static Class<?> unnamed;
  56     static Class<?> unnamed1;
  57 
  58     @BeforeTest
  59     public void setup() throws Exception {
  60         m3 = new ModuleLookup("m3", 'C');
  61         m4 = new ModuleLookup("m4", 'D');
  62         m5 = new ModuleLookup("m5", 'E');
  63         moduleLookupMap.put(m3.name(), m3);
  64         moduleLookupMap.put(m4.name(), m4);
  65         moduleLookupMap.put(m5.name(), m5);
  66 
  67         privLookupIn = MethodHandles.privateLookupIn(m3.type2, m3.lookup);
  68         privLookupIn2 = MethodHandles.privateLookupIn(m4.type1, m3.lookup);
  69 
  70         unnamed = Class.forName("Unnamed");
  71         unnamed1 = Class.forName("Unnamed1");
  72         unnamedLookup = (Lookup)unnamed.getMethod("lookup").invoke(null);
  73 
  74         // m5 reads m3
  75         CrackM5Access.addReads(m3.module);
  76         CrackM5Access.addReads(unnamed.getModule());
  77     }
  78 
  79     @DataProvider(name = "samePackage")
  80     public Object[][] samePackage() throws Exception {
  81         return new Object[][] {
  82             { m3.lookup,     m3.type2 },
  83             { privLookupIn,  m3.type1 },
  84             { privLookupIn2, m4.type2 },
  85             { unnamedLookup, unnamed1 }
  86         };
  87     }
  88 
  89     /**
  90      * Test lookup.in(T) where T is in the same package of the lookup class.
  91      *
  92      * [A0] targetClass becomes the lookup class
  93      * [A1] no change in previous lookup class
  94      * [A2] PROTECTED and PRIVATE are dropped
  95      */
  96     @Test(dataProvider = "samePackage")
  97     public void testLookupInSamePackage(Lookup lookup, Class<?> targetClass) throws Exception {
  98         Class<?> lookupClass = lookup.lookupClass();
  99         Lookup lookup2 = lookup.in(targetClass);
 100 
 101         assertTrue(lookupClass.getPackage() == targetClass.getPackage());
 102         assertTrue(lookupClass.getModule() == targetClass.getModule());
 103         assertTrue(lookup2.lookupClass() == targetClass);   // [A0]
 104         assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass());  // [A1]
 105         assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE)));  // [A2]
 106     }
 107 
 108     @DataProvider(name = "sameModule")
 109     public Object[][] sameModule() throws Exception {
 110         return new Object[][] {
 111             { m3.lookup,     m3.type3},
 112             { privLookupIn,  m3.type3},
 113             { privLookupIn2, m4.type3}
 114         };
 115     }
 116 
 117     /**
 118      * Test lookup.in(T) where T is in the same module but different package from the lookup class.
 119      *
 120      * [A0] targetClass becomes the lookup class
 121      * [A1] no change in previous lookup class
 122      * [A2] PROTECTED, PRIVATE and PACKAGE are dropped
 123      */
 124     @Test(dataProvider = "sameModule")
 125     public void testLookupInSameModule(Lookup lookup, Class<?> targetClass) throws Exception {
 126         Class<?> lookupClass = lookup.lookupClass();
 127         Lookup lookup2 = lookup.in(targetClass);
 128 
 129         assertTrue(lookupClass.getPackage() != targetClass.getPackage());
 130         assertTrue(lookupClass.getModule() == targetClass.getModule());
 131         assertTrue(lookup2.lookupClass() == targetClass);   // [A0]
 132         assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass());  // [A1]
 133         assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE))); // [A2]
 134     }
 135 
 136     @DataProvider(name = "anotherModule")
 137     public Object[][] anotherModule() throws Exception {
 138         return new Object[][] {
 139             { m3.lookup, m4.type1, m5, m5.accessibleTypesTo(m3.module, m4.module) },
 140             { m4.lookup, m5.type2, m3, m3.accessibleTypesTo(m4.module, m5.module) },
 141             { m3.lookup, m5.type1, m4, m4.accessibleTypesTo(m3.module, m5.module) },
 142             { m5.lookup, unnamed,  m3, m3.accessibleTypesTo(m5.module, unnamed.getModule()) },
 143         };
 144     }
 145 
 146     /**
 147      * Test lookup.in(T) where T is in a different module from the lookup class.
 148      *
 149      * [A0] targetClass becomes the lookup class
 150      * [A1] lookup class becomes previous lookup class
 151      * [A2] PROTECTED, PRIVATE, PACKAGE, and MODULE are dropped
 152      * [A3] no access to module internal types in m0 and m1
 153      * [A4] if m1 reads m0, can access public types in m0; otherwise no access.
 154      * [A5] can access public types in m1 exported to m0
 155      * [A6] can access public types in m2 exported to m0 and m1
 156      */
 157     @Test(dataProvider = "anotherModule")
 158     public void testLookupInAnotherModule(Lookup lookup, Class<?> targetClass,
 159                                           ModuleLookup m2, Set<Class<?>> otherTypes) throws Exception {
 160         Class<?> lookupClass = lookup.lookupClass();
 161         Module m0 = lookupClass.getModule();
 162         Module m1 = targetClass.getModule();
 163 
 164         assertTrue(m0 != m1);
 165         assertTrue(m0.canRead(m1));
 166         assertTrue(m1.isExported(targetClass.getPackageName(), m0));
 167 
 168         Lookup lookup2 = lookup.in(targetClass);
 169         assertTrue(lookup2.lookupClass() == targetClass);   // [A0]
 170         assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());  // [A1]
 171         assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE)));  // [A2]
 172 
 173         // [A3] no access to module internal type in m0
 174         // [A4] if m1 reads m0,
 175         // [A4]   no access to public types exported from m0 unconditionally
 176         // [A4]   no access to public types exported from m0
 177         ModuleLookup ml0 = moduleLookupMap.get(m0.getName());
 178         if (m1.canRead(m0)) {
 179             for (Class<?> type : ml0.unconditionalExports()) {
 180                 testAccess(lookup2, type);
 181             }
 182             for (Class<?> type : ml0.qualifiedExportsTo(m1)) {
 183                 testAccess(lookup2, type);
 184             }
 185         } else {
 186             findConstructorExpectingIAE(lookup2, ml0.type1, void.class);
 187             findConstructorExpectingIAE(lookup2, ml0.type2, void.class);
 188             findConstructorExpectingIAE(lookup2, ml0.type3, void.class);
 189         }
 190 
 191         // [A5] can access public types exported from m1 unconditionally
 192         // [A5] can access public types exported from m1 to m0
 193         if (m1.isNamed()) {
 194             ModuleLookup ml1 = moduleLookupMap.get(m1.getName());
 195             assertTrue(ml1.unconditionalExports().size() + ml1.qualifiedExportsTo(m0).size() > 0);
 196             for (Class<?> type : ml1.unconditionalExports()) {
 197                 testAccess(lookup2, type);
 198             }
 199             for (Class<?> type : ml1.qualifiedExportsTo(m0)) {
 200                 testAccess(lookup2, type);
 201             }
 202         } else {
 203             // unnamed module
 204             testAccess(lookup2, unnamed1);
 205         }
 206 
 207         // [A5] can access public types exported from m2 unconditionally
 208         // [A5] can access public types exported from m2 to m0 and m1
 209         for (Class<?> type : otherTypes) {
 210             assertTrue(type.getModule() == m2.module);
 211             testAccess(lookup2, type);
 212         }
 213 
 214         // test inaccessible types
 215         for (Class<?> type : Set.of(m2.type1, m2.type2, m2.type3)) {
 216             if (!otherTypes.contains(type)) {
 217                 // type is accessible to this lookup
 218                 try {
 219                     lookup2.accessClass(type);
 220                     assertTrue(false);
 221                 } catch (IllegalAccessException e) {}
 222 
 223                 findConstructorExpectingIAE(lookup2, type, void.class);
 224             }
 225         }
 226     }
 227 
 228     public void testAccess(Lookup lookup, Class<?> type) throws Exception {
 229         // type is accessible to this lookup
 230         assertTrue(lookup.accessClass(type) == type);
 231 
 232         // can find constructor
 233         findConstructor(lookup, type, void.class);
 234 
 235         Module m0 = lookup.previousLookupClass().getModule();
 236         Module m1 = lookup.lookupClass().getModule();
 237         Module m2 = type.getModule();
 238 
 239         assertTrue(m0 != m1 && m0 != null);
 240         assertTrue((lookup.lookupModes() & MODULE) == 0);
 241         assertTrue(m0 != m2 || m1 != m2);
 242 
 243         MethodHandles.Lookup lookup2 = lookup.in(type);
 244         if (m2 == m1) {
 245             // the same module of the lookup class
 246             assertTrue(lookup2.lookupClass() == type);
 247             assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass());
 248         } else if (m2 == m0) {
 249             // hop back to the module of the previous lookup class
 250             assertTrue(lookup2.lookupClass() == type);
 251             assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());
 252         } else {
 253             // hop to a third module
 254             assertTrue(lookup2.lookupClass() == type);
 255             assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());
 256             assertTrue(lookup2.lookupModes() == 0);
 257         }
 258     }
 259 
 260     @DataProvider(name = "thirdModule")
 261     public Object[][] thirdModule() throws Exception {
 262         return new Object[][] {
 263             { m3.lookup, m4.type1, m5.type1},
 264             { m3.lookup, m4.type2, m5.type1},
 265             { unnamedLookup, m3.type1, m4.type1 },
 266         };
 267     }
 268 
 269     /**
 270      * Test lookup.in(c1).in(c2) where c1 is in second module and c2 is in a third module.
 271      *
 272      * [A0] c2 becomes the lookup class
 273      * [A1] c1 becomes previous lookup class
 274      * [A2] all access bits are dropped
 275      */
 276     @Test(dataProvider = "thirdModule")
 277     public void testLookupInThirdModule(Lookup lookup, Class<?> c1, Class<?> c2) throws Exception {
 278         Class<?> c0 = lookup.lookupClass();
 279         Module m0 = c0.getModule();
 280         Module m1 = c1.getModule();
 281         Module m2 = c2.getModule();
 282 
 283         assertTrue(m0 != m1 && m0 != m2 && m1 != m2);
 284         assertTrue(m0.canRead(m1) && m0.canRead(m2));
 285         assertTrue(m1.canRead(m2));
 286         assertTrue(m1.isExported(c1.getPackageName(), m0));
 287         assertTrue(m2.isExported(c2.getPackageName(), m0) && m2.isExported(c2.getPackageName(), m1));
 288 
 289         Lookup lookup1 = lookup.in(c1);
 290         assertTrue(lookup1.lookupClass() == c1);
 291         assertTrue(lookup1.previousLookupClass() == c0);
 292         assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE)));
 293 
 294         Lookup lookup2 = lookup1.in(c2);
 295         assertTrue(lookup2.lookupClass() == c2);                    // [A0]
 296         assertTrue(lookup2.previousLookupClass() == c1);            // [A1]
 297         assertTrue(lookup2.lookupModes() == 0, lookup2.toString()); // [A2]
 298     }
 299 
 300     @DataProvider(name = "privLookupIn")
 301     public Object[][] privLookupIn() throws Exception {
 302         return new Object[][] {
 303             { m3.lookup,  m4.type1 },
 304             { m3.lookup,  m5.type1 },
 305             { m4.lookup,  m5.type2 },
 306             { m5.lookup,  m3.type3 },
 307             { m5.lookup,  unnamed  }
 308         };
 309     }
 310 
 311     /**
 312      * Test privateLookupIn(T, lookup) where T is in another module
 313      *
 314      * [A0] full capabilities except MODULE bit
 315      * [A1] target class becomes the lookup class
 316      * [A2] the lookup class becomes previous lookup class
 317      * [A3] IAE thrown if lookup has no MODULE access
 318      */
 319     @Test(dataProvider = "privLookupIn")
 320     public void testPrivateLookupIn(Lookup lookup, Class<?> targetClass) throws Exception {
 321         Module m0 = lookup.lookupClass().getModule();
 322         Module m1 = targetClass.getModule();
 323 
 324         // privateLookupIn from m0 to m1
 325         assertTrue(m0 != m1);
 326         assertTrue(m1.isOpen(targetClass.getPackageName(), m0));
 327         Lookup privLookup1 = MethodHandles.privateLookupIn(targetClass, lookup);
 328         assertTrue(privLookup1.lookupModes() == (PROTECTED|PRIVATE|PACKAGE|PUBLIC));  // [A0]
 329         assertTrue(privLookup1.lookupClass() == targetClass);                    // [A1]
 330         assertTrue(privLookup1.previousLookupClass() == lookup.lookupClass());   // [A2]
 331 
 332         // privLookup1 has no MODULE access; can't do privateLookupIn
 333         try {
 334             Lookup privLookup2 = MethodHandles.privateLookupIn(targetClass, privLookup1); // [A3]
 335             assertFalse(privLookup2 != null);
 336         } catch (IllegalAccessException e) {}
 337     }
 338 
 339     /**
 340      * Test member access from the Lookup returned from privateLookupIn
 341      */
 342     @Test
 343     public void testPrivateLookupAccess() throws Exception {
 344         Class<?> staticsClass = e1.Statics.class;
 345         Lookup privLookup1 = MethodHandles.privateLookupIn(staticsClass, m4.lookup);
 346         assertTrue((privLookup1.lookupModes() & MODULE) == 0);
 347         assertTrue(privLookup1.lookupClass() == staticsClass);
 348         assertTrue(privLookup1.previousLookupClass() == m4.lookup.lookupClass());
 349 
 350         // access private member and default package member in m5
 351         MethodType mtype = MethodType.methodType(void.class);
 352         MethodHandle mh1 = privLookup1.findStatic(staticsClass, "privateMethod", mtype);
 353         MethodHandle mh2 = privLookup1.findStatic(staticsClass, "packageMethod", mtype);
 354 
 355         // access public member in exported types from m5 to m4
 356         findConstructor(privLookup1, m5.type1, void.class);
 357         // no access to public member in non-exported types to m5
 358         findConstructorExpectingIAE(privLookup1, m5.type3, void.class);
 359 
 360         // no access to public types in m4 since m5 does not read m4
 361         assertFalse(m5.module.canRead(m4.module));
 362         findConstructorExpectingIAE(privLookup1, m4.type1, void.class);
 363 
 364         // teleport from a privateLookup to another class in the same package
 365         // lose private access
 366         Lookup privLookup2 = MethodHandles.privateLookupIn(m5.type1, m4.lookup);
 367         Lookup lookup = privLookup2.in(staticsClass);
 368         assertTrue((lookup.lookupModes() & PRIVATE) == 0);
 369         MethodHandle mh3 = lookup.findStatic(staticsClass, "packageMethod", mtype);
 370         try {
 371             lookup.findStatic(staticsClass, "privateMethod", mtype);
 372             assertTrue(false);
 373         } catch (IllegalAccessException e) {}
 374     }
 375 
 376     /**
 377      * Test member access from the Lookup returned from privateLookupIn and
 378      * the lookup mode after dropLookupMode
 379      */
 380     @Test
 381     public void testDropLookupMode() throws Exception {
 382         Lookup lookup = MethodHandles.privateLookupIn(m5.type1, m4.lookup);
 383         assertTrue((lookup.lookupModes() & MODULE) == 0);
 384 
 385         Lookup lookup1 = lookup.dropLookupMode(PRIVATE);
 386         assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE)));
 387         Lookup lookup2 = lookup.dropLookupMode(PACKAGE);
 388         assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE)));
 389         Lookup lookup3 = lookup.dropLookupMode(MODULE);
 390         assertTrue(lookup3.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE)));
 391         Lookup lookup4 = lookup.dropLookupMode(PUBLIC);
 392         assertTrue(lookup4.lookupModes() == 0);
 393 
 394     }
 395 
 396     /**
 397      * Test no access to a public member on a non-public class
 398      */
 399     @Test
 400     public void testPrivateLookupOnNonPublicType() throws Exception {
 401         // privateLookup in a non-public type
 402         Class<?> nonPUblicType = Class.forName("e1.NonPublic");
 403         Lookup privLookup = MethodHandles.privateLookupIn(nonPUblicType, m4.lookup);
 404         MethodType mtype = MethodType.methodType(void.class);
 405         MethodHandle mh1 = privLookup.findStatic(nonPUblicType, "publicStatic", mtype);
 406 
 407         // drop MODULE access i.e. only PUBLIC access
 408         Lookup lookup = privLookup.dropLookupMode(MODULE);
 409         assertTrue(lookup.lookupModes() == PUBLIC);
 410         try {
 411             MethodHandle mh2 = lookup.findStatic(nonPUblicType, "publicStatic", mtype);
 412             assertFalse(mh2 != null);
 413         } catch (IllegalAccessException e) {}
 414     }
 415 
 416     @Test
 417     public void testPublicLookup() {
 418         Lookup publicLookup = MethodHandles.publicLookup();
 419         Lookup pub1 = publicLookup.in(m3.type1);
 420         Lookup pub2 = pub1.in(java.lang.String.class);
 421         Lookup pub3 = pub2.in(java.lang.management.ThreadMXBean.class);
 422         Lookup pub4 = pub3.dropLookupMode(UNCONDITIONAL);
 423 
 424         assertTrue(publicLookup.lookupClass() == Object.class);
 425         assertTrue(publicLookup.lookupModes() == UNCONDITIONAL);
 426         assertTrue(pub1.lookupClass() == m3.type1);
 427         assertTrue(pub1.lookupModes() == UNCONDITIONAL);
 428         assertTrue(pub2.lookupClass() == String.class);
 429         assertTrue(pub2.lookupModes() == UNCONDITIONAL);
 430         assertTrue(pub3.lookupClass() == java.lang.management.ThreadMXBean.class);
 431         assertTrue(pub3.lookupModes() == UNCONDITIONAL);
 432         assertTrue(pub4.lookupModes() == 0);
 433 
 434         // publicLookup has no MODULE access; can't do privateLookupIn
 435         try {
 436             Lookup pub5 = MethodHandles.privateLookupIn(m4.type1, pub1);
 437             assertFalse(pub5 != null);
 438         } catch (IllegalAccessException e) {}
 439     }
 440 
 441     static class ModuleLookup {
 442         private final Module module;
 443         private final Set<String> packages;
 444         private final Lookup lookup;
 445         private final Class<?> type1;
 446         private final Class<?> type2;
 447         private final Class<?> type3;
 448 
 449         ModuleLookup(String mn, char c) throws Exception {
 450             this.module = ModuleLayer.boot().findModule(mn).orElse(null);
 451             assertNotNull(this.module);
 452             this.packages = module.getDescriptor().packages();
 453             assertTrue(packages.size() <= 3);
 454             Lookup lookup = null;
 455             Class<?> type1 = null;
 456             Class<?> type2 = null;
 457             Class<?> type3 = null;
 458             for (String pn : packages) {
 459                 char n = pn.charAt(pn.length() - 1);
 460                 switch (n) {
 461                     case '1':
 462                         type1 = Class.forName(pn + "." + c + "1");
 463                         type2 = Class.forName(pn + "." + c + "2");
 464                         Method m = type1.getMethod("lookup");
 465                         lookup = (Lookup) m.invoke(null);
 466                         break;
 467                     case '2':
 468                         type3 = Class.forName(pn + "." + c + "3");
 469                         break;
 470 
 471                     default:
 472                 }
 473             }
 474             this.lookup = lookup;
 475             this.type1 = type1;
 476             this.type2 = type2;
 477             this.type3 = type3;
 478         }
 479 
 480         String name() {
 481             return module.getName();
 482         }
 483 
 484         /*
 485          * Returns the set of types that are unconditionally exported.
 486          */
 487         Set<Class<?>> unconditionalExports() {
 488             return Stream.of(type1, type2, type3)
 489                          .filter(c -> module.isExported(c.getPackageName()))
 490                          .collect(Collectors.toSet());
 491         }
 492 
 493         /*
 494          * Returns the set of types that are qualifiedly exported to the specified
 495          * caller module
 496          */
 497         Set<Class<?>> qualifiedExportsTo(Module caller) {
 498             if (caller.canRead(this.module)) {
 499                 return Stream.of(type1, type2, type3)
 500                              .filter(c -> !module.isExported(c.getPackageName())
 501                                           && module.isExported(c.getPackageName(), caller))
 502                              .collect(Collectors.toSet());
 503             } else {
 504                 return Set.of();
 505             }
 506         }
 507 
 508         /*
 509          * Returns the set of types that are qualifiedly exported to the specified
 510          * caller module
 511          */
 512         Set<Class<?>> accessibleTypesTo(Module m0, Module m1) {
 513             if (m0.canRead(this.module) && m1.canRead(this.module)) {
 514                 return Stream.of(type1, type2, type3)
 515                              .filter(c -> module.isExported(c.getPackageName(), m0)
 516                                           && module.isExported(c.getPackageName(), m1))
 517                              .collect(Collectors.toSet());
 518             } else {
 519                 return Set.of();
 520             }
 521         }
 522 
 523         /*
 524          * Returns the set of types that are open to the specified caller
 525          * unconditionally or qualifiedly.
 526          */
 527         Set<Class<?>> opensTo(Module caller) {
 528             if (caller.canRead(this.module)) {
 529                 return Stream.of(type1, type2, type3)
 530                              .filter(c -> module.isOpen(c.getPackageName(), caller))
 531                              .collect(Collectors.toSet());
 532             } else {
 533                 return Set.of();
 534             }
 535         }
 536 
 537         public String toString() {
 538             return module.toString();
 539         }
 540     }
 541 
 542     /**
 543      * Invokes Lookup findConstructor with a method type constructed from the
 544      * given return and parameter types, expecting IllegalAccessException to be
 545      * thrown.
 546      */
 547     static void findConstructorExpectingIAE(Lookup lookup,
 548                                             Class<?> clazz,
 549                                             Class<?> rtype,
 550                                             Class<?>... ptypes) throws Exception {
 551         try {
 552             MethodHandle mh = findConstructor(lookup, clazz, rtype, ptypes);
 553             assertTrue(false);
 554         } catch (IllegalAccessException expected) { }
 555     }
 556 
 557     /**
 558      * Invokes Lookup findConstructor with a method type constructored from the
 559      * given return and parameter types.
 560      */
 561     static MethodHandle findConstructor(Lookup lookup,
 562                                         Class<?> clazz,
 563                                         Class<?> rtype,
 564                                         Class<?>... ptypes) throws Exception {
 565         MethodType mt = MethodType.methodType(rtype, ptypes);
 566         return lookup.findConstructor(clazz, mt);
 567     }
 568 }