1 /* 2 * Copyright (c) 2019, 2020, 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 /* 25 * @test 26 * @library /test/lib 27 * @build jdk.test.lib.Utils 28 * jdk.test.lib.compiler.CompilerUtils 29 * BasicTest 30 * @run testng/othervm BasicTest 31 * @run testng/othervm -Xcomp BasicTest 32 */ 33 34 import java.io.File; 35 import java.io.IOException; 36 import java.lang.invoke.MethodHandles.Lookup; 37 38 import static java.lang.invoke.MethodHandles.lookup; 39 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*; 40 41 import java.lang.reflect.Array; 42 import java.lang.reflect.Method; 43 import java.nio.charset.StandardCharsets; 44 import java.nio.file.Files; 45 import java.nio.file.Path; 46 import java.nio.file.Paths; 47 import java.util.Arrays; 48 import java.util.List; 49 import java.util.stream.Stream; 50 51 import jdk.test.lib.compiler.CompilerUtils; 52 import jdk.test.lib.Utils; 53 54 import org.testng.annotations.BeforeTest; 55 import org.testng.annotations.DataProvider; 56 import org.testng.annotations.Test; 57 import static org.testng.Assert.*; 58 59 interface HiddenTest { 60 void test(); 61 } 62 63 public class BasicTest { 64 65 private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src"); 66 private static final Path CLASSES_DIR = Paths.get("classes"); 67 private static final Path CLASSES_10_DIR = Paths.get("classes_10"); 68 69 private static byte[] hiddenClassBytes; 70 71 @BeforeTest 72 static void setup() throws IOException { 73 compileSources(SRC_DIR, CLASSES_DIR); 74 75 hiddenClassBytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenClass.class")); 76 77 // compile with --release 10 with no NestHost and NestMembers attribute 78 compileSources(SRC_DIR.resolve("Outer.java"), CLASSES_10_DIR, "--release", "10"); 79 compileSources(SRC_DIR.resolve("EnclosingClass.java"), CLASSES_10_DIR, "--release", "10"); 80 } 81 82 static void compileSources(Path sourceFile, Path dest, String... options) throws IOException { 83 Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR); 84 if (options != null && options.length > 0) { 85 ops = Stream.concat(ops, Arrays.stream(options)); 86 } 87 if (!CompilerUtils.compile(sourceFile, dest, ops.toArray(String[]::new))) { 88 throw new RuntimeException("Compilation of the test failed: " + sourceFile); 89 } 90 } 91 92 static Class<?> defineHiddenClass(String name) throws Exception { 93 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class")); 94 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass(); 95 assertHiddenClass(hc); 96 singletonNest(hc); 97 return hc; 98 } 99 100 // basic test on a hidden class 101 @Test 102 public void hiddenClass() throws Throwable { 103 HiddenTest t = (HiddenTest)defineHiddenClass("HiddenClass").newInstance(); 104 t.test(); 105 106 // sanity check 107 Class<?> c = t.getClass(); 108 Class<?>[] intfs = c.getInterfaces(); 109 assertTrue(c.isHiddenClass()); 110 assertFalse(c.isPrimitive()); 111 assertTrue(intfs.length == 1); 112 assertTrue(intfs[0] == HiddenTest.class); 113 assertTrue(c.getCanonicalName() == null); 114 assertTrue(c.getName().startsWith("HiddenClass/")); 115 116 // test array of hidden class 117 testHiddenArray(c); 118 119 // test setAccessible 120 checkSetAccessible(c, "realTest"); 121 checkSetAccessible(c, "test"); 122 } 123 124 @Test 125 public void primitiveClass() { 126 assertFalse(int.class.isHiddenClass()); 127 assertFalse(String.class.isHiddenClass()); 128 } 129 130 private void testHiddenArray(Class<?> type) throws Exception { 131 // array of hidden class 132 Object array = Array.newInstance(type, 2); 133 Class<?> arrayType = array.getClass(); 134 assertTrue(arrayType.isArray()); 135 assertTrue(Array.getLength(array) == 2); 136 assertFalse(arrayType.isHiddenClass()); 137 assertTrue(arrayType.getName().startsWith("[LHiddenClass/"), "unexpected name: " + arrayType.getName()); 138 139 assertTrue(arrayType.getComponentType().isHiddenClass()); 140 assertTrue(arrayType.getComponentType() == type); 141 Object t = type.newInstance(); 142 Array.set(array, 0, t); 143 Object o = Array.get(array, 0); 144 assertTrue(o == t); 145 } 146 147 private void checkSetAccessible(Class<?> c, String name, Class<?>... ptypes) throws Exception { 148 Method m = c.getDeclaredMethod(name, ptypes); 149 assertTrue(m.trySetAccessible()); 150 m.setAccessible(true); 151 } 152 153 // Define a hidden class that uses lambda 154 // This verifies LambdaMetaFactory supports the caller which is a hidden class 155 @Test 156 public void testLambda() throws Throwable { 157 HiddenTest t = (HiddenTest)defineHiddenClass("Lambda").newInstance(); 158 try { 159 t.test(); 160 } catch (Error e) { 161 if (!e.getMessage().equals("thrown by " + t.getClass().getName())) { 162 throw e; 163 } 164 } 165 } 166 167 // Verify the nest host and nest members of a hidden class and hidden nestmate class 168 @Test 169 public void testHiddenNestHost() throws Throwable { 170 byte[] hc1 = hiddenClassBytes; 171 Lookup lookup1 = lookup().defineHiddenClass(hc1, false); 172 Class<?> host = lookup1.lookupClass(); 173 174 byte[] hc2 = Files.readAllBytes(CLASSES_DIR.resolve("Lambda.class")); 175 Lookup lookup2 = lookup1.defineHiddenClass(hc2, false, NESTMATE); 176 Class<?> member = lookup2.lookupClass(); 177 178 // test nest membership and reflection API 179 assertTrue(host.isNestmateOf(member)); 180 assertTrue(host.getNestHost() == host); 181 // getNestHost and getNestMembers return the same value when calling 182 // on a nest member and the nest host 183 assertTrue(member.getNestHost() == host.getNestHost()); 184 assertTrue(Arrays.equals(member.getNestMembers(), host.getNestMembers())); 185 // getNestMembers includes the nest host that can be a hidden class but 186 // only includes static nest members 187 assertTrue(host.getNestMembers().length == 1); 188 assertTrue(host.getNestMembers()[0] == host); 189 } 190 191 @Test 192 public void hiddenCantReflect() throws Throwable { 193 HiddenTest t = (HiddenTest)defineHiddenClass("HiddenCantReflect").newInstance(); 194 t.test(); 195 196 Class<?> c = t.getClass(); 197 Class<?>[] intfs = c.getInterfaces(); 198 assertTrue(intfs.length == 1); 199 assertTrue(intfs[0] == HiddenTest.class); 200 201 try { 202 // this would cause loading of class HiddenCantReflect and NCDFE due 203 // to error during verification 204 c.getDeclaredMethods(); 205 } catch (NoClassDefFoundError e) { 206 Throwable x = e.getCause(); 207 if (x == null || !(x instanceof ClassNotFoundException && x.getMessage().contains("HiddenCantReflect"))) { 208 throw e; 209 } 210 } 211 } 212 213 @DataProvider(name = "hiddenClasses") 214 private Object[][] hiddenClasses() { 215 return new Object[][] { 216 new Object[] { "HiddenInterface", false }, 217 new Object[] { "AbstractClass", false }, 218 // class file with bad NestHost, NestMembers and InnerClasses or EnclosingMethod attribute 219 // define them as nestmate to verify Class::getNestHost and getNestMembers 220 new Object[] { "Outer", true }, 221 new Object[] { "Outer$Inner", true }, 222 new Object[] { "EnclosingClass", true }, 223 new Object[] { "EnclosingClass$1", true }, 224 }; 225 } 226 227 @Test(dataProvider = "hiddenClasses") 228 public void defineHiddenClass(String name, boolean nestmate) throws Exception { 229 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class")); 230 Class<?> hc; 231 Class<?> host; 232 if (nestmate) { 233 hc = lookup().defineHiddenClass(bytes, false, NESTMATE).lookupClass(); 234 host = lookup().lookupClass().getNestHost(); 235 } else { 236 hc = lookup().defineHiddenClass(bytes, false).lookupClass(); 237 host = hc; 238 } 239 assertTrue(hc.getNestHost() == host); 240 assertTrue(hc.getNestMembers().length == 1); 241 assertTrue(hc.getNestMembers()[0] == host); 242 } 243 244 @Test(expectedExceptions = NoClassDefFoundError.class) 245 public void hiddenSuperClass() throws Exception { 246 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenSuper.class")); 247 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass(); 248 } 249 250 @Test(expectedExceptions = {IllegalArgumentException.class}) 251 public void cantDefineModule() throws Throwable { 252 Path src = Paths.get("module-info.java"); 253 Path dir = CLASSES_DIR.resolve("m"); 254 Files.write(src, List.of("module m {}"), StandardCharsets.UTF_8); 255 compileSources(src, dir); 256 257 byte[] bytes = Files.readAllBytes(dir.resolve("module-info.class")); 258 lookup().defineHiddenClass(bytes, false); 259 } 260 261 @Test(expectedExceptions = {IllegalArgumentException.class}) 262 public void cantDefineClassInAnotherPackage() throws Throwable { 263 Path src = Paths.get("ClassInAnotherPackage.java"); 264 Files.write(src, List.of("package p;", "public class ClassInAnotherPackage {}"), StandardCharsets.UTF_8); 265 compileSources(src, CLASSES_DIR); 266 267 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("p").resolve("ClassInAnotherPackage.class")); 268 lookup().defineHiddenClass(bytes, false); 269 } 270 271 @Test(expectedExceptions = {IllegalAccessException.class}) 272 public void lessPrivilegedLookup() throws Throwable { 273 Lookup lookup = lookup().dropLookupMode(Lookup.PRIVATE); 274 lookup.defineHiddenClass(hiddenClassBytes, false); 275 } 276 277 @DataProvider(name = "nestedTypesOrAnonymousClass") 278 private Object[][] nestedTypesOrAnonymousClass() { 279 return new Object[][] { 280 // class file with bad InnerClasses or EnclosingMethod attribute 281 new Object[] { "Outer", null }, 282 new Object[] { "Outer$Inner", "Outer" }, 283 new Object[] { "EnclosingClass", null }, 284 new Object[] { "EnclosingClass$1", "EnclosingClass" }, 285 }; 286 } 287 288 @Test(dataProvider = "nestedTypesOrAnonymousClass") 289 public void hasInnerClassesOrEnclosingMethodAttribute(String className, String badDeclaringClassName) throws Throwable { 290 byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve(className + ".class")); 291 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass(); 292 hiddenClassWithBadAttribute(hc, badDeclaringClassName); 293 } 294 295 // define a hidden class with static nest membership 296 @Test 297 public void hasStaticNestHost() throws Exception { 298 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer$Inner.class")); 299 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass(); 300 hiddenClassWithBadAttribute(hc, "Outer"); 301 } 302 303 @Test 304 public void hasStaticNestMembers() throws Throwable { 305 byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer.class")); 306 Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass(); 307 assertHiddenClass(hc); 308 assertTrue(hc.getNestHost() == hc); 309 Class<?>[] members = hc.getNestMembers(); 310 assertTrue(members.length == 1 && members[0] == hc); 311 } 312 313 // a hidden class with bad InnerClasses or EnclosingMethod attribute 314 private void hiddenClassWithBadAttribute(Class<?> hc, String badDeclaringClassName) { 315 assertTrue(hc.isHiddenClass()); 316 assertTrue(hc.getCanonicalName() == null); 317 assertTrue(hc.getName().contains("/")); 318 319 if (badDeclaringClassName == null) { 320 // the following reflection API assumes a good name in InnerClasses 321 // or EnclosingMethod attribute can successfully be resolved. 322 assertTrue(hc.getSimpleName().length() > 0); 323 assertFalse(hc.isAnonymousClass()); 324 assertFalse(hc.isLocalClass()); 325 assertFalse(hc.isMemberClass()); 326 } else { 327 declaringClassNotFound(hc, badDeclaringClassName); 328 } 329 330 // validation of nest membership 331 assertTrue(hc.getNestHost() == hc); 332 // validate the static nest membership 333 Class<?>[] members = hc.getNestMembers(); 334 assertTrue(members.length == 1 && members[0] == hc); 335 } 336 337 // Class::getSimpleName, Class::isMemberClass 338 private void declaringClassNotFound(Class<?> c, String cn) { 339 try { 340 // fail to find declaring/enclosing class 341 c.isMemberClass(); 342 assertTrue(false); 343 } catch (NoClassDefFoundError e) { 344 if (!e.getMessage().equals(cn)) { 345 throw e; 346 } 347 } 348 try { 349 // fail to find declaring/enclosing class 350 c.getSimpleName(); 351 assertTrue(false); 352 } catch (NoClassDefFoundError e) { 353 if (!e.getMessage().equals(cn)) { 354 throw e; 355 } 356 } 357 } 358 359 private static void singletonNest(Class<?> hc) { 360 assertTrue(hc.getNestHost() == hc); 361 assertTrue(hc.getNestMembers().length == 1); 362 assertTrue(hc.getNestMembers()[0] == hc); 363 } 364 365 private static void assertHiddenClass(Class<?> hc) { 366 assertTrue(hc.isHiddenClass()); 367 assertTrue(hc.getCanonicalName() == null); 368 assertTrue(hc.getName().contains("/")); 369 assertFalse(hc.isAnonymousClass()); 370 assertFalse(hc.isLocalClass()); 371 assertFalse(hc.isMemberClass()); 372 assertFalse(hc.getSimpleName().isEmpty()); // sanity check 373 } 374 } --- EOF ---