1 /*
   2  * Copyright (c) 2017, 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  * @bug 8046171
  27  * @summary Test the new nestmate reflection API
  28  * @compile TestReflectionAPI.java
  29  *          PackagedNestHost.java
  30  *          PackagedNestHost2.java
  31  *          SampleNest.java
  32  *          Hosts.java
  33  *
  34  * @compile MemberNoHost.jcod
  35  *          MemberMissingHost.jcod
  36  *          MemberNotInstanceHost.jcod
  37  *          MemberNotOurHost.jcod
  38  *          MemberMalformedHost.jcod
  39  *          MalformedHost.jcod
  40  *          PackagedNestHost.jcod
  41  *          PackagedNestHost2Member.jcod
  42  *          PackagedNestHostMember.jcod
  43  *          HostOfMemberNoHost.jcod
  44  *          HostOfMemberMissingHost.jcod
  45  *          HostOfMemberNotInstanceHost.jcod
  46  *          HostOfMemberNotOurHost.jcod
  47  *          HostOfMemberMalformedHost.jcod
  48  *
  49  * @run main/othervm TestReflectionAPI
  50  */
  51 
  52 // We need a nest member class that is invalid for each of the possible reasons,
  53 // plus we need some external classes to test other failure modes.
  54 // For each nested class below there is a corresponding .jcod file which breaks one
  55 // of the rules regarding nest membership. For the package related tests we have
  56 // additional PackageNestHost*.java sources.
  57 // For testing getNestMembers we need an external host class that has a nested class
  58 // which we can form a jcod file from such that we get all the expected failure modes.
  59 // Note that all the .java files must be compiled in the same step, while all
  60 // .jcod files must be compiled in a later step.
  61 
  62 import java.util.Arrays;
  63 import java.util.Comparator;
  64 
  65 public class TestReflectionAPI {
  66 
  67     // Valid nest member
  68     static class Member {}
  69 
  70     // Missing NestHost attribute
  71     static class MemberNoHost {}
  72 
  73     // Missing NestHost class
  74     static class MemberMissingHost {}
  75 
  76     // Invalid NestHost class (not instance class)
  77     static class MemberNotInstanceHost {
  78         Object[] oa; // create CP entry to use in jcod change
  79     }
  80 
  81     // Valid but different NestHost class
  82     static class MemberNotOurHost {}
  83 
  84     // Malformed NestHost class
  85     static class MemberMalformedHost {}
  86 
  87     public static void main(String[] args) throws Throwable {
  88         test_getNestHost();
  89         test_isNestmateOf();
  90         test_getNestMembers();
  91     }
  92 
  93     static void test_getNestHost() {
  94         Class<?> host = TestReflectionAPI.class;
  95 
  96         // sampling of "good" checks
  97 
  98         checkHost(host, host);
  99         checkHost(Member.class, host);
 100         Runnable r = new Runnable() { public void run() {}};
 101         checkHost(r.getClass(), host);
 102 
 103         // all the "bad" classes should report themselves as their
 104         // own nest host - no exceptions should be thrown
 105         Class<?>[] allClasses = host.getDeclaredClasses();
 106         for (Class<?> c : allClasses) {
 107             if (c == Member.class)
 108                 continue;
 109             checkHost(c, c);
 110         }
 111         checkHost(P1.PackagedNestHost.Member.class,
 112                   P1.PackagedNestHost.Member.class);
 113         checkHost(P2.PackagedNestHost2.Member.class,
 114                   P2.PackagedNestHost2.Member.class);
 115 
 116         // test some 'special' classes
 117         checkHost(int.class, int.class);                   // primitive
 118         checkHost(Object[].class, Object[].class);         // array
 119         checkHost(Thread.State.class, Thread.class);       // enum
 120         checkHost(java.lang.annotation.Documented.class,   // annotation
 121                   java.lang.annotation.Documented.class);
 122     }
 123 
 124     static void test_isNestmateOf() {
 125         Class<?> host = TestReflectionAPI.class;
 126         checkNestmates(host, host, true);
 127         checkNestmates(Member.class, host, true);
 128         Runnable r = new Runnable() { public void run() {}};
 129         checkNestmates(r.getClass(), host, true);
 130 
 131         // all the "bad" classes should report themselves as their
 132         // own nest host - no exceptions should be thrown - so not
 133         // nestmates
 134         Class<?>[] allClasses = host.getDeclaredClasses();
 135         for (Class<?> c : allClasses) {
 136             if (c == Member.class)
 137                 continue;
 138             checkNestmates(host, c, false);
 139         }
 140 
 141         // 'special' classes
 142         checkNestmates(int.class, int.class, true);             // primitive
 143         checkNestmates(int.class, long.class, false);           // primitive
 144         checkNestmates(Object[].class, Object[].class, true);   // array
 145         checkNestmates(Object[].class, int[].class, false);     // array
 146         checkNestmates(Thread.State.class, Thread.class, true); // enum
 147         checkNestmates(java.lang.annotation.Documented.class,   // annotation
 148                        java.lang.annotation.Documented.class, true);
 149     }
 150 
 151     static void test_getNestMembers() {
 152         // Sampling of "good" checks
 153         Class<?>[] good = { Object.class, Object[].class, int.class};
 154         checkSingletonNests(good);
 155 
 156         // More thorough correctness check
 157         checkNest(SampleNest.class, SampleNest.nestedTypes());
 158 
 159         // Hosts with "bad" members
 160         Class<?>[] bad = {
 161             HostOfMemberNoHost.class,
 162             HostOfMemberMissingHost.class,
 163             HostOfMemberNotOurHost.class,
 164             HostOfMemberNotInstanceHost.class,
 165             HostOfMemberMalformedHost.class,
 166         };
 167         Class<?>[] exceptions = {
 168             IncompatibleClassChangeError.class,
 169             NoClassDefFoundError.class,
 170             IncompatibleClassChangeError.class,
 171             IncompatibleClassChangeError.class,
 172             ClassFormatError.class,
 173         };
 174         String[] messages = {
 175             "Nest member HostOfMemberNoHost$MemberNoHost in HostOfMemberNoHost " +
 176             "declares a different nest host of HostOfMemberNoHost$MemberNoHost",
 177             "Unable to load nest-host class (NestHost) of " +
 178             "HostOfMemberMissingHost$MemberMissingHost",
 179             "Type HostOfMemberNotOurHost$MemberNotOurHost is not a nest member " +
 180             "of java.lang.Object: current type is not listed as a nest member",
 181             "Type HostOfMemberNotInstanceHost$MemberNotInstanceHost is not a nest " +
 182             "member of [Ljava.lang.Object;: nest-host is not an instance class!",
 183             "Incompatible magic value 3735928559 in class file MalformedHost",
 184         };
 185         for (int i = 0; i < bad.length; i++) {
 186             try {
 187                 bad[i].getNestMembers();
 188                 throw new Error("getNestMembers() succeeded for class " +
 189                                bad[i].getName());
 190             } catch (LinkageError e) {
 191                 checkException(e, messages[i], exceptions[i]);
 192             }
 193         }
 194     }
 195 
 196     static void checkException(Throwable actual, String msg, Class<?> expected) {
 197         if (!actual.getClass().equals(expected))
 198             throw new Error("Unexpected exception: got " + actual.getClass().getName()
 199                             + " but expected " + expected.getName());
 200         if (!actual.getMessage().contains(msg))
 201             throw new Error("Wrong " + actual.getClass().getSimpleName() +": \"" +
 202                             actual.getMessage() + "\" does not contain \"" +
 203                             msg + "\"");
 204         System.out.println("OK - got expected exception: " + actual);
 205     }
 206 
 207     static void checkHost(Class<?> target, Class<?> expected) {
 208         System.out.println("Checking nest host of " + target.getName());
 209         Class<?> host = target.getNestHost();
 210         if (host != expected)
 211             throw new Error("Class " + target.getName() +
 212                             " has nest host " + host.getName() +
 213                             " but expected " + expected.getName());
 214     }
 215 
 216     static void checkNestmates(Class<?> a, Class<?> b, boolean mates) {
 217         System.out.println("Checking if " + a.getName() +
 218                            " isNestmateOf " + b.getName());
 219 
 220         if (a.isNestmateOf(b) != mates)
 221             throw new Error("Class " + a.getName() + " is " +
 222                             (mates ? "not " : "") +
 223                             "a nestmate of " + b.getName() + " but should " +
 224                             (mates ? "" : "not ") + "be");
 225     }
 226 
 227     static Comparator<Class<?>> cmp = Comparator.comparing(Class::getName);
 228 
 229     static void checkNest(Class<?> host, Class<?>[] unsortedTypes) {
 230         Class<?>[] members = host.getNestMembers();
 231         Arrays.sort(members, cmp);
 232         Class<?>[] nestedTypes = unsortedTypes.clone();
 233         Arrays.sort(nestedTypes, cmp);
 234         printMembers(host, members);
 235         printDeclared(host, nestedTypes);
 236         if (!Arrays.equals(members, nestedTypes)) {
 237             throw new Error("Class " + host.getName() + " has different members " +
 238                             "compared to declared classes");
 239         }
 240         // verify all the relationships that must hold for nest members
 241         for (Class<?> a : members) {
 242             checkHost(a, host);
 243             checkNestmates(a, host, true);
 244             Class<?>[] aMembers = a.getNestMembers();
 245             if (aMembers[0] != host) {
 246                 throw new Error("Class " + a.getName() + " getNestMembers()[0] = " +
 247                                 aMembers[0].getName() + " not " + host.getName());
 248 
 249             }
 250             Arrays.sort(aMembers, cmp);
 251             if (!Arrays.equals(members, aMembers)) {
 252                 throw new Error("Class " + a.getName() + " has different members " +
 253                                 "compared to host " + host.getName());
 254             }
 255             for (Class<?> b : members) {
 256                 checkNestmates(a, b, true);
 257             }
 258         }
 259     }
 260 
 261     static void checkSingletonNests(Class<?>[] classes) {
 262         for (Class<?> host : classes) {
 263             Class<?>[] members = host.getNestMembers();
 264             if (members.length != 1) {
 265                 printMembers(host, members);
 266                 throw new Error("Class " + host.getName() + " lists " + members.length
 267                                 + " members instead of 1 (itself)");
 268             }
 269             if (members[0] != host) {
 270                 printMembers(host, members);
 271                 throw new Error("Class " + host.getName() + " lists " +
 272                                 members[0].getName() + " as member instead of itself");
 273             }
 274         }
 275     }
 276 
 277     static void printMembers(Class<?> host, Class<?>[] members) {
 278         System.out.println("Class " + host.getName() + " has members: ");
 279         for (Class<?> c : members) {
 280             System.out.println(" - " + c.getName());
 281         }
 282     }
 283 
 284     static void printDeclared(Class<?> host, Class<?>[] declared) {
 285         System.out.println("Class " + host.getName() + " has declared types: ");
 286         for (Class<?> c : declared) {
 287             System.out.println(" - " + c.getName());
 288         }
 289     }
 290 
 291 }