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  * @run testng/othervm -Xcomp BasicTest
  32  */
  33 
  34 import java.io.File;
  35 import java.io.IOException;
  36 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
  37 import static java.lang.invoke.MethodHandles.lookup;
  38 
  39 import java.lang.reflect.Array;
  40 import java.lang.reflect.InaccessibleObjectException;
  41 import java.lang.reflect.Method;
  42 import java.nio.file.Files;
  43 import java.nio.file.Path;
  44 import java.nio.file.Paths;
  45 import java.util.Arrays;
  46 import java.util.stream.Stream;
  47 
  48 import jdk.test.lib.compiler.CompilerUtils;
  49 
  50 import jdk.test.lib.Utils;
  51 
  52 import org.testng.annotations.BeforeTest;
  53 import org.testng.annotations.Test;
  54 import static org.testng.Assert.*;
  55 
  56 /* package-private */ interface HiddenTest {
  57   void test();
  58 }
  59 
  60 public class BasicTest {
  61 
  62     private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src");
  63     private static final Path CLASSES_DIR = Paths.get("classes");
  64     private static final Path CLASSES_10_DIR = Paths.get("classes_10");
  65 
  66     @BeforeTest
  67     static void setup() throws IOException {
  68         if (!CompilerUtils.compile(SRC_DIR, CLASSES_DIR, false, "-cp", Utils.TEST_CLASSES)) {
  69             throw new RuntimeException("Compilation of the test failed");
  70         }
  71     }
  72 
  73     static void compileSources(Path sourceFile, Path dest, String... options) throws IOException {
  74         Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR);
  75         if (options != null && options.length > 0) {
  76             ops = Stream.concat(ops, Arrays.stream(options));
  77         }
  78         if (!CompilerUtils.compile(sourceFile, dest, ops.toArray(String[]::new))) {
  79             throw new RuntimeException("Compilation of the test failed: " + sourceFile);
  80         }
  81     }
  82 
  83     static byte[] readClassFile(String classFileName) throws IOException {
  84         return Files.readAllBytes(CLASSES_DIR.resolve(classFileName));
  85     }
  86 
  87     static Class<HiddenTest> defineHiddenClass(String name) throws Exception {
  88         byte[] bytes = readClassFile(name + ".class");
  89         return (Class<HiddenTest>)lookup().defineHiddenClass(bytes, false, NESTMATE);
  90     }
  91 
  92     @Test
  93     public void hiddenClass() throws Throwable {
  94         HiddenTest t = defineHiddenClass("HiddenClass").newInstance();
  95         t.test();
  96 
  97         Class<?> c = t.getClass();
  98         Class<?>[] intfs = c.getInterfaces();
  99         assertTrue(c.isHiddenClass());
 100         assertTrue(intfs.length == 1);
 101         assertTrue(intfs[0] == HiddenTest.class);
 102         assertTrue(c.getCanonicalName() == null);
 103 
 104         // test array of hidden class
 105         testHiddenArray(c);
 106 
 107         // test setAccessible
 108         checkSetAccessible(c, "realTest");
 109         checkSetAccessible(c, "test");
 110     }
 111 
 112     @Test
 113     public void testIsHiddenClass() {
 114         assertFalse(int.class.isHiddenClass());
 115         assertFalse(String.class.isHiddenClass());
 116     }
 117 
 118     private void testHiddenArray(Class<?> type) throws Exception {
 119         // array of hidden class
 120         Object array = Array.newInstance(type, 2);
 121         Class<?> arrayType = array.getClass();
 122         assertTrue(arrayType.isArray());
 123         assertTrue(Array.getLength(array) == 2);
 124         assertFalse(arrayType.isHiddenClass());
 125 
 126         assertTrue(arrayType.getComponentType().isHiddenClass());
 127         assertTrue(arrayType.getComponentType() == type);
 128         Object t = type.newInstance();
 129         Array.set(array, 0, t);
 130         Object o = Array.get(array, 0);
 131         assertTrue(o == t);
 132     }
 133 
 134     private void checkSetAccessible(Class<?> c, String name, Class<?>... ptypes) throws Exception {
 135         Method m = c.getDeclaredMethod(name, ptypes);
 136         assertFalse(m.trySetAccessible());
 137         try {
 138             m.setAccessible(true);
 139         } catch (InaccessibleObjectException e) {
 140             e.printStackTrace();
 141         }
 142     }
 143 
 144     @Test
 145     public void testLambda() throws Throwable {
 146         HiddenTest t = defineHiddenClass("Lambda").newInstance();
 147         try {
 148             t.test();
 149         } catch (Error e) {
 150             if (!e.getMessage().equals("thrown by " + t.getClass().getName())) {
 151                 throw e;
 152             }
 153         }
 154     }
 155 
 156     @Test
 157     public void hiddenCantReflect() throws Throwable {
 158         HiddenTest t = defineHiddenClass("HiddenCantReflect").newInstance();
 159         t.test();
 160 
 161         Class<?> c = t.getClass();
 162         Class<?>[] intfs = c.getInterfaces();
 163         assertTrue(intfs.length == 1);
 164         assertTrue(intfs[0] == HiddenTest.class);
 165 
 166         try {
 167             // this will cause class loading of HiddenCantReflect and fail
 168             c.getDeclaredMethods();
 169         } catch (NoClassDefFoundError e) {
 170             Throwable x = e.getCause();
 171             if (x == null || !(x instanceof ClassNotFoundException && x.getMessage().contains("HiddenCantReflect"))) {
 172                 throw e;
 173             }
 174         }
 175     }
 176 
 177     @Test(expectedExceptions = IllegalArgumentException.class)
 178     public void hiddenInterface() throws Exception {
 179         byte[] bytes = readClassFile("HiddenInterface.class");
 180         Class<?> c = lookup().defineHiddenClass(bytes, false);
 181     }
 182 
 183 
 184     @Test(expectedExceptions = IllegalArgumentException.class)
 185     public void abstractHiddenClass() throws Exception {
 186         byte[] bytes = readClassFile("AbstractClass.class");
 187         Class<?> c = lookup().defineHiddenClass(bytes, false);
 188     }
 189 
 190     @Test(expectedExceptions = NoClassDefFoundError.class)
 191     public void hiddenSuperClass() throws Exception {
 192         byte[] bytes = readClassFile("HiddenSuper.class");
 193         Class<?> c = lookup().defineHiddenClass(bytes, false);
 194     }
 195 
 196     @Test
 197     public void hiddenNestmates() throws Throwable {
 198         try {
 199             byte[] bytes = readClassFile("Outer.class");
 200             lookup().defineHiddenClass(bytes, false);
 201         } catch (IllegalArgumentException e) {
 202             if (!e.getMessage().contains("NestMembers attribute")) throw e;
 203         }
 204 
 205         try {
 206             byte[] bytes = readClassFile("Outer$Inner.class");
 207             lookup().defineHiddenClass(bytes, false);
 208         } catch (IllegalArgumentException e) {
 209             if (!e.getMessage().contains("NestHost attribute")) throw e;
 210         }
 211     }
 212 
 213     @Test
 214     public void hiddenNestedClass() throws Throwable {
 215         // compile with --release 10 with no NestHost and NestMembers attribute
 216         compileSources(SRC_DIR.resolve("Outer.java"), CLASSES_10_DIR, "--release", "10");
 217         try {
 218             byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("Outer.class"));
 219             lookup().defineHiddenClass(bytes, false);
 220         } catch (IllegalArgumentException e) {
 221             if (!e.getMessage().contains("Outer$Inner")) throw e;
 222         }
 223 
 224         try {
 225             byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("Outer$Inner.class"));
 226             lookup().defineHiddenClass(bytes, false);
 227         } catch (IllegalArgumentException e) {
 228             if (!e.getMessage().contains("Outer$Inner")) throw e;
 229         }
 230     }
 231 
 232     @Test
 233     public void hiddenAnonymous() throws Throwable {
 234         // compile with --release 10 with no NestHost and NestMembers attribute
 235         compileSources(SRC_DIR.resolve("EnclosingClass.java"), CLASSES_10_DIR, "--release", "10");
 236         try {
 237             byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("EnclosingClass.class"));
 238             lookup().defineHiddenClass(bytes, false);
 239         } catch (IllegalArgumentException e) {
 240             if (!e.getMessage().contains("EnclosingClass$1")) throw e;
 241         }
 242 
 243         try {
 244             byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("EnclosingClass$1.class"));
 245             lookup().defineHiddenClass(bytes, false);
 246         } catch (IllegalArgumentException e) {
 247             if (!e.getMessage().contains("EnclosingMethod attribute")) throw e;
 248         }
 249     }
 250 }