< prev index next >

test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java

Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com
rev 58567 : [mq]: rename-isHidden
rev 58568 : [mq]: hidden-class-4


   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();


 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"));


 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     }


 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 }


   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  * @modules java.base/jdk.internal.org.objectweb.asm
  27  *          jdk.compiler
  28  * @library /test/lib
  29  * @build jdk.test.lib.Utils
  30  *        jdk.test.lib.compiler.CompilerUtils
  31  * @run testng/othervm --enable-preview 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.internal.org.objectweb.asm.ClassWriter;
  52 import jdk.internal.org.objectweb.asm.Type;
  53 import jdk.test.lib.compiler.CompilerUtils;
  54 import jdk.test.lib.Utils;
  55 
  56 import org.testng.annotations.BeforeTest;
  57 import org.testng.annotations.DataProvider;
  58 import org.testng.annotations.Test;
  59 
  60 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  61 import static org.testng.Assert.*;
  62 
  63 interface HiddenTest {
  64     void test();
  65 }
  66 
  67 public class BasicTest {
  68 
  69     private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src");
  70     private static final Path CLASSES_DIR = Paths.get("classes");
  71     private static final Path CLASSES_10_DIR = Paths.get("classes_10");
  72 
  73     private static byte[] hiddenClassBytes;
  74 
  75     @BeforeTest
  76     static void setup() throws IOException {
  77         compileSources(SRC_DIR, CLASSES_DIR,
  78                 "--enable-preview", "-source", String.valueOf(Runtime.version().feature()));
  79         hiddenClassBytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenClass.class"));
  80 
  81         // compile with --release 10 with no NestHost and NestMembers attribute
  82         compileSources(SRC_DIR.resolve("Outer.java"), CLASSES_10_DIR, "--release", "10");
  83         compileSources(SRC_DIR.resolve("EnclosingClass.java"), CLASSES_10_DIR, "--release", "10");
  84     }
  85 
  86     static void compileSources(Path sourceFile, Path dest, String... options) throws IOException {
  87         Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR);
  88         if (options != null && options.length > 0) {
  89             ops = Stream.concat(ops, Arrays.stream(options));
  90         }
  91         if (!CompilerUtils.compile(sourceFile, dest, ops.toArray(String[]::new))) {
  92             throw new RuntimeException("Compilation of the test failed: " + sourceFile);
  93         }
  94     }
  95 
  96     static Class<?> defineHiddenClass(String name) throws Exception {
  97         byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
  98         Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
  99         assertHiddenClass(hc);
 100         singletonNest(hc);
 101         return hc;
 102     }
 103 
 104     // basic test on a hidden class
 105     @Test
 106     public void hiddenClass() throws Throwable {
 107         HiddenTest t = (HiddenTest)defineHiddenClass("HiddenClass").newInstance();
 108         t.test();
 109 
 110         // sanity check
 111         Class<?> c = t.getClass();
 112         Class<?>[] intfs = c.getInterfaces();
 113         assertTrue(c.isHidden());
 114         assertFalse(c.isPrimitive());
 115         assertTrue(intfs.length == 1);
 116         assertTrue(intfs[0] == HiddenTest.class);
 117         assertTrue(c.getCanonicalName() == null);
 118         assertTrue(c.getName().startsWith("HiddenClass/"));
 119 
 120         // test array of hidden class
 121         testHiddenArray(c);
 122 
 123         // test setAccessible
 124         checkSetAccessible(c, "realTest");
 125         checkSetAccessible(c, "test");
 126     }
 127 
 128     // primitive class is not a hidden class
 129     @Test
 130     public void primitiveClass() {
 131         assertFalse(int.class.isHidden());
 132         assertFalse(String.class.isHidden());
 133     }
 134 
 135     private void testHiddenArray(Class<?> type) throws Exception {
 136         // array of hidden class
 137         Object array = Array.newInstance(type, 2);
 138         Class<?> arrayType = array.getClass();
 139         assertTrue(arrayType.isArray());
 140         assertTrue(Array.getLength(array) == 2);
 141         assertFalse(arrayType.isHidden());
 142         assertTrue(arrayType.getName().startsWith("[LHiddenClass/"), "unexpected name: " + arrayType.getName());
 143 
 144         assertTrue(arrayType.getComponentType().isHidden());
 145         assertTrue(arrayType.getComponentType() == type);
 146         Object t = type.newInstance();
 147         Array.set(array, 0, t);
 148         Object o = Array.get(array, 0);
 149         assertTrue(o == t);
 150     }
 151 
 152     private void checkSetAccessible(Class<?> c, String name, Class<?>... ptypes) throws Exception {
 153         Method m = c.getDeclaredMethod(name, ptypes);
 154         assertTrue(m.trySetAccessible());
 155         m.setAccessible(true);
 156     }
 157 
 158     // Define a hidden class that uses lambda
 159     // This verifies LambdaMetaFactory supports the caller which is a hidden class
 160     @Test
 161     public void testLambda() throws Throwable {
 162         HiddenTest t = (HiddenTest)defineHiddenClass("Lambda").newInstance();
 163         try {
 164             t.test();


 176         Lookup lookup1 = lookup().defineHiddenClass(hc1, false);
 177         Class<?> host = lookup1.lookupClass();
 178 
 179         byte[] hc2 = Files.readAllBytes(CLASSES_DIR.resolve("Lambda.class"));
 180         Lookup lookup2 = lookup1.defineHiddenClass(hc2, false, NESTMATE);
 181         Class<?> member = lookup2.lookupClass();
 182 
 183         // test nest membership and reflection API
 184         assertTrue(host.isNestmateOf(member));
 185         assertTrue(host.getNestHost() == host);
 186         // getNestHost and getNestMembers return the same value when calling
 187         // on a nest member and the nest host
 188         assertTrue(member.getNestHost() == host.getNestHost());
 189         assertTrue(Arrays.equals(member.getNestMembers(), host.getNestMembers()));
 190         // getNestMembers includes the nest host that can be a hidden class but
 191         // only includes static nest members
 192         assertTrue(host.getNestMembers().length == 1);
 193         assertTrue(host.getNestMembers()[0] == host);
 194     }
 195 






















 196     @DataProvider(name = "hiddenClasses")
 197     private Object[][] hiddenClasses() {
 198         return new Object[][] {
 199                 new Object[] { "HiddenInterface", false },
 200                 new Object[] { "AbstractClass", false },
 201                 // a hidden annotation is useless because it cannot be referenced by any class
 202                 new Object[] { "HiddenAnnotation", false },
 203                 // class file with bad NestHost, NestMembers and InnerClasses or EnclosingMethod attribute
 204                 // define them as nestmate to verify Class::getNestHost and getNestMembers
 205                 new Object[] { "Outer", true },
 206                 new Object[] { "Outer$Inner", true },
 207                 new Object[] { "EnclosingClass", true },
 208                 new Object[] { "EnclosingClass$1", true },
 209         };
 210     }
 211 
 212     /*
 213      * Test that class file bytes that can be defined as a normal class
 214      * can be successfully created as a hidden class even it might not
 215      * make sense as a hidden class.  For example, a hidden annotation
 216      * is not useful as it cannot be referenced and an outer/inner class
 217      * when defined as a hidden effectively becomes a final top-level class.
 218      */
 219     @Test(dataProvider = "hiddenClasses")
 220     public void defineHiddenClass(String name, boolean nestmate) throws Exception {
 221         byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
 222         Class<?> hc;
 223         Class<?> host;
 224         if (nestmate) {
 225             hc = lookup().defineHiddenClass(bytes, false, NESTMATE).lookupClass();
 226             host = lookup().lookupClass().getNestHost();
 227         } else {
 228             hc = lookup().defineHiddenClass(bytes, false).lookupClass();
 229             host = hc;
 230         }
 231         assertTrue(hc.getNestHost() == host);
 232         assertTrue(hc.getNestMembers().length == 1);
 233         assertTrue(hc.getNestMembers()[0] == host);
 234     }
 235 
 236     @DataProvider(name = "emptyClasses")
 237     private Object[][] emptyClasses() {
 238         return new Object[][] {
 239                 new Object[] { "EmptyHiddenSynthetic", ACC_SYNTHETIC },
 240                 new Object[] { "EmptyHiddenEnum", ACC_ENUM },
 241                 new Object[] { "EmptyHiddenAbstractClass", ACC_ABSTRACT },
 242                 new Object[] { "EmptyHiddenInterface", ACC_ABSTRACT|ACC_INTERFACE },
 243                 new Object[] { "EmptyHiddenAnnotation", ACC_ANNOTATION|ACC_ABSTRACT|ACC_INTERFACE },
 244         };
 245     }
 246 
 247     /*
 248      * Test if an empty class with valid access flags can be created as a hidden class
 249      * as long as it does not violate the restriction of a hidden class.
 250      *
 251      * A meaningful enum type defines constants of that enum type.  So
 252      * enum class containing constants of its type should not be a hidden
 253      * class.
 254      */
 255     @Test(dataProvider = "emptyClasses")
 256     public void emptyHiddenClass(String name, int accessFlags) throws Exception {
 257         byte[] bytes = (accessFlags == ACC_ENUM) ? classBytes(name, Enum.class, accessFlags)
 258                                                  : classBytes(name, accessFlags);
 259         Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
 260         switch (accessFlags) {
 261             case ACC_SYNTHETIC:
 262                 assertTrue(hc.isSynthetic());
 263                 assertFalse(hc.isEnum());
 264                 assertFalse(hc.isAnnotation());
 265                 assertFalse(hc.isInterface());
 266                 break;
 267             case ACC_ENUM:
 268                 assertFalse(hc.isSynthetic());
 269                 assertTrue(hc.isEnum());
 270                 assertFalse(hc.isAnnotation());
 271                 assertFalse(hc.isInterface());
 272                 break;
 273             case ACC_ABSTRACT:
 274                 assertFalse(hc.isSynthetic());
 275                 assertFalse(hc.isEnum());
 276                 assertFalse(hc.isAnnotation());
 277                 assertFalse(hc.isInterface());
 278                 break;
 279             case ACC_ABSTRACT|ACC_INTERFACE:
 280                 assertFalse(hc.isSynthetic());
 281                 assertFalse(hc.isEnum());
 282                 assertFalse(hc.isAnnotation());
 283                 assertTrue(hc.isInterface());
 284                 break;
 285             case ACC_ANNOTATION|ACC_ABSTRACT|ACC_INTERFACE:
 286                 assertFalse(hc.isSynthetic());
 287                 assertFalse(hc.isEnum());
 288                 assertTrue(hc.isAnnotation());
 289                 assertTrue(hc.isInterface());
 290                 break;
 291             default:
 292                 throw new IllegalArgumentException("unexpected access flag: " + accessFlags);
 293         }
 294         assertTrue(hc.isHidden());
 295         assertTrue(hc.getModifiers() == (ACC_PUBLIC|accessFlags));
 296         assertFalse(hc.isLocalClass());
 297         assertFalse(hc.isMemberClass());
 298         assertFalse(hc.isAnonymousClass());
 299         assertFalse(hc.isArray());
 300     }
 301 
 302     // These class files can't be defined as hidden classes
 303     @DataProvider(name = "cantBeHiddenClasses")
 304     private Object[][] cantBeHiddenClasses() {
 305         return new Object[][] {
 306                 // a hidden class can't be a field's declaring type
 307                 // enum class with static final HiddenEnum[] $VALUES:
 308                 new Object[] { "HiddenEnum" },
 309                 // supertype of this class is a hidden class
 310                 new Object[] { "HiddenSuper" },
 311                 // a record class whose equals(HiddenRecord, Object) method
 312                 // refers to a hidden class in the parameter type and fails
 313                 // verification.  Perhaps this method signature should be reconsidered. 
 314                 new Object[] { "HiddenRecord" },
 315         };
 316     }
 317 
 318     /*
 319      * These class files
 320      */
 321     @Test(dataProvider = "cantBeHiddenClasses", expectedExceptions = NoClassDefFoundError.class)
 322     public void failToDeriveAsHiddenClass(String name) throws Exception {
 323         byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
 324         Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
 325     }
 326 
 327     /*
 328      * A hidden class can be successfully created but fails to be reflected
 329      * if it refers to its own type in the descriptor.
 330      * e.g. Class::getMethods resolves the declaring type of fields,
 331      * parameter types and return type.
 332      */
 333     @Test
 334     public void hiddenCantReflect() throws Throwable {
 335         HiddenTest t = (HiddenTest)defineHiddenClass("HiddenCantReflect").newInstance();
 336         t.test();
 337 
 338         Class<?> c = t.getClass();
 339         Class<?>[] intfs = c.getInterfaces();
 340         assertTrue(intfs.length == 1);
 341         assertTrue(intfs[0] == HiddenTest.class);
 342 
 343         try {
 344             // this would cause loading of class HiddenCantReflect and NCDFE due
 345             // to error during verification
 346             c.getDeclaredMethods();
 347         } catch (NoClassDefFoundError e) {
 348             Throwable x = e.getCause();
 349             if (x == null || !(x instanceof ClassNotFoundException && x.getMessage().contains("HiddenCantReflect"))) {
 350                 throw e;
 351             }
 352         }
 353     }
 354 
 355     @Test(expectedExceptions = {IllegalArgumentException.class})
 356     public void cantDefineModule() throws Throwable {
 357         Path src = Paths.get("module-info.java");
 358         Path dir = CLASSES_DIR.resolve("m");
 359         Files.write(src, List.of("module m {}"), StandardCharsets.UTF_8);
 360         compileSources(src, dir);
 361 
 362         byte[] bytes = Files.readAllBytes(dir.resolve("module-info.class"));
 363         lookup().defineHiddenClass(bytes, false);
 364     }
 365 
 366     @Test(expectedExceptions = {IllegalArgumentException.class})
 367     public void cantDefineClassInAnotherPackage() throws Throwable {
 368         Path src = Paths.get("ClassInAnotherPackage.java");
 369         Files.write(src, List.of("package p;", "public class ClassInAnotherPackage {}"), StandardCharsets.UTF_8);
 370         compileSources(src, CLASSES_DIR);
 371 
 372         byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("p").resolve("ClassInAnotherPackage.class"));


 400     // define a hidden class with static nest membership
 401     @Test
 402     public void hasStaticNestHost() throws Exception {
 403         byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer$Inner.class"));
 404         Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
 405         hiddenClassWithBadAttribute(hc, "Outer");
 406     }
 407 
 408     @Test
 409     public void hasStaticNestMembers() throws Throwable {
 410         byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer.class"));
 411         Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
 412         assertHiddenClass(hc);
 413         assertTrue(hc.getNestHost() == hc);
 414         Class<?>[] members = hc.getNestMembers();
 415         assertTrue(members.length == 1 && members[0] == hc);
 416     }
 417 
 418     // a hidden class with bad InnerClasses or EnclosingMethod attribute
 419     private void hiddenClassWithBadAttribute(Class<?> hc, String badDeclaringClassName) {
 420         assertTrue(hc.isHidden());
 421         assertTrue(hc.getCanonicalName() == null);
 422         assertTrue(hc.getName().contains("/"));
 423 
 424         if (badDeclaringClassName == null) {
 425             // the following reflection API assumes a good name in InnerClasses
 426             // or EnclosingMethod attribute can successfully be resolved.
 427             assertTrue(hc.getSimpleName().length() > 0);
 428             assertFalse(hc.isAnonymousClass());
 429             assertFalse(hc.isLocalClass());
 430             assertFalse(hc.isMemberClass());
 431         } else {
 432             declaringClassNotFound(hc, badDeclaringClassName);
 433         }
 434 
 435         // validation of nest membership
 436         assertTrue(hc.getNestHost() == hc);
 437         // validate the static nest membership
 438         Class<?>[] members = hc.getNestMembers();
 439         assertTrue(members.length == 1 && members[0] == hc);
 440     }


 451             }
 452         }
 453         try {
 454             // fail to find declaring/enclosing class
 455             c.getSimpleName();
 456             assertTrue(false);
 457         } catch (NoClassDefFoundError e) {
 458             if (!e.getMessage().equals(cn)) {
 459                 throw e;
 460             }
 461         }
 462     }
 463 
 464     private static void singletonNest(Class<?> hc) {
 465         assertTrue(hc.getNestHost() == hc);
 466         assertTrue(hc.getNestMembers().length == 1);
 467         assertTrue(hc.getNestMembers()[0] == hc);
 468     }
 469 
 470     private static void assertHiddenClass(Class<?> hc) {
 471         assertTrue(hc.isHidden());
 472         assertTrue(hc.getCanonicalName() == null);
 473         assertTrue(hc.getName().contains("/"));
 474         assertFalse(hc.isAnonymousClass());
 475         assertFalse(hc.isLocalClass());
 476         assertFalse(hc.isMemberClass());
 477         assertFalse(hc.getSimpleName().isEmpty()); // sanity check
 478     }
 479 
 480     private static byte[] classBytes(String classname, int accessFlags) {
 481         return classBytes(classname, Object.class, accessFlags);
 482     }
 483 
 484     private static byte[] classBytes(String classname, Class<?> supertType, int accessFlags) {
 485         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 486         cw.visit(V14, ACC_PUBLIC|accessFlags, classname, null, Type.getInternalName(supertType), null);
 487         cw.visitEnd();
 488 
 489         return cw.toByteArray();
 490     }
 491 }
< prev index next >