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 }