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