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