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