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         test_getNestHost();
  94         test_isNestmateOf();
  95         test_getNestMembers();
  96     }
  97 
  98     static void test_getNestHost() {
  99         Class<?> host = TestReflectionAPI.class;
 100 
 101         // sampling of "good" checks
 102 
 103         checkHost(host, host);
 104         checkHost(Member.class, host);
 105         Runnable r = new Runnable() { public void run() {}};
 106         checkHost(r.getClass(), host);
 107 
 108         // all the "bad" classes should report themselves as their
 109         // own nest host - no exceptions should be thrown
 110         Class<?>[] allClasses = host.getDeclaredClasses();
 111         for (Class<?> c : allClasses) {
 112             if (c == Member.class)
 113                 continue;
 114             checkHost(c, c);
 115         }
 116         checkHost(P1.PackagedNestHost.Member.class,
 117                   P1.PackagedNestHost.Member.class);
 118         checkHost(P2.PackagedNestHost2.Member.class,
 119                   P2.PackagedNestHost2.Member.class);
 120 
 121         // test some 'special' classes
 122         checkHost(int.class, int.class);                   // primitive
 123         checkHost(Object[].class, Object[].class);         // array
 124         checkHost(Thread.State.class, Thread.class);       // enum
 125         checkHost(java.lang.annotation.Documented.class,   // annotation
 126                   java.lang.annotation.Documented.class);
 127     }
 128 
 129     static void test_isNestmateOf() {
 130         Class<?> host = TestReflectionAPI.class;
 131         checkNestmates(host, host, true);
 132         checkNestmates(Member.class, host, true);
 133         Runnable r = new Runnable() { public void run() {}};
 134         checkNestmates(r.getClass(), host, true);
 135 
 136         // all the "bad" classes should report themselves as their
 137         // own nest host - no exceptions should be thrown - so not
 138         // nestmates
 139         Class<?>[] allClasses = host.getDeclaredClasses();
 140         for (Class<?> c : allClasses) {
 141             if (c == Member.class)
 142                 continue;
 143             checkNestmates(host, c, false);
 144         }
 145 
 146         // 'special' classes
 147         checkNestmates(int.class, int.class, true);             // primitive
 148         checkNestmates(int.class, long.class, false);           // primitive
 149         checkNestmates(Object[].class, Object[].class, true);   // array
 150         checkNestmates(Object[].class, int[].class, false);     // array
 151         checkNestmates(Thread.State.class, Thread.class, true); // enum
 152         checkNestmates(java.lang.annotation.Documented.class,   // annotation
 153                        java.lang.annotation.Documented.class, true);
 154     }
 155 
 156     static void test_getNestMembers() {
 157         // Sampling of "good" checks
 158         Class<?>[] good = { Object.class, Object[].class, int.class};
 159         checkSingletonNests(good);
 160 
 161         // More thorough correctness check
 162         checkNest(SampleNest.class, SampleNest.nestedTypes(), false);
 163 
 164         // Special cases - legal but not produced by javac
 165         checkNest(HostWithSelfMember.class,
 166                   new Class<?>[] { HostWithSelfMember.class,
 167                           HostWithSelfMember.Member.class },
 168                   true);
 169         checkNest(HostWithDuplicateMembers.class,
 170                   new Class<?>[] { HostWithDuplicateMembers.class,
 171                           HostWithDuplicateMembers.Member1.class,
 172                           HostWithDuplicateMembers.Member2.class },
 173                   true);
 174 
 175         // Hosts with "bad" members
 176         Class<?>[] bad = {
 177             HostOfMemberNoHost.class,
 178             HostOfMemberMissingHost.class,
 179             HostOfMemberNotOurHost.class,
 180             HostOfMemberNotInstanceHost.class,
 181             HostOfMemberMalformedHost.class,
 182         };
 183         Class<?>[] exceptions = {
 184             IncompatibleClassChangeError.class,
 185             NoClassDefFoundError.class,
 186             IncompatibleClassChangeError.class,
 187             IncompatibleClassChangeError.class,
 188             ClassFormatError.class,
 189         };
 190         String[] messages = {
 191             "Nest member HostOfMemberNoHost$MemberNoHost in HostOfMemberNoHost " +
 192             "declares a different nest host of HostOfMemberNoHost$MemberNoHost",
 193             "Unable to load nest-host class (NestHost) of " +
 194             "HostOfMemberMissingHost$MemberMissingHost",
 195             "Type HostOfMemberNotOurHost$MemberNotOurHost is not a nest member " +
 196             "of InvalidNestHost: current type is not listed as a nest member",
 197             "Type HostOfMemberNotInstanceHost$MemberNotInstanceHost is not a nest " +
 198             "member of [LInvalidNestHost;: current type is not listed as a nest member",
 199             "Incompatible magic value 3735928559 in class file MalformedHost",
 200         };
 201         for (int i = 0; i < bad.length; i++) {
 202             try {
 203                 bad[i].getNestMembers();
 204                 throw new Error("getNestMembers() succeeded for class " +
 205                                bad[i].getName());
 206             } catch (LinkageError e) {
 207                 checkException(e, messages[i], exceptions[i]);
 208             }
 209         }
 210     }
 211 
 212     static void checkException(Throwable actual, String msg, Class<?> expected) {
 213         if (!actual.getClass().equals(expected))
 214             throw new Error("Unexpected exception: got " + actual.getClass().getName()
 215                             + " but expected " + expected.getName());
 216         if (!actual.getMessage().contains(msg))
 217             throw new Error("Wrong " + actual.getClass().getSimpleName() +": \"" +
 218                             actual.getMessage() + "\" does not contain \"" +
 219                             msg + "\"");
 220         System.out.println("OK - got expected exception: " + actual);
 221     }
 222 
 223     static void checkHost(Class<?> target, Class<?> expected) {
 224         System.out.println("Checking nest host of " + target.getName());
 225         Class<?> host = target.getNestHost();
 226         if (host != expected)
 227             throw new Error("Class " + target.getName() +
 228                             " has nest host " + host.getName() +
 229                             " but expected " + expected.getName());
 230     }
 231 
 232     static void checkNestmates(Class<?> a, Class<?> b, boolean mates) {
 233         System.out.println("Checking if " + a.getName() +
 234                            " isNestmateOf " + b.getName());
 235 
 236         if (a.isNestmateOf(b) != mates)
 237             throw new Error("Class " + a.getName() + " is " +
 238                             (mates ? "not " : "") +
 239                             "a nestmate of " + b.getName() + " but should " +
 240                             (mates ? "" : "not ") + "be");
 241     }
 242 
 243     static Comparator<Class<?>> cmp = Comparator.comparing(Class::getName);
 244 
 245     static void checkNest(Class<?> host, Class<?>[] unsortedTypes, boolean expectDups) {
 246         Class<?>[] members = host.getNestMembers();
 247         Arrays.sort(members, cmp);
 248         Class<?>[] nestedTypes = unsortedTypes.clone();
 249         Arrays.sort(nestedTypes, cmp);
 250         printMembers(host, members);
 251         printDeclared(host, nestedTypes);
 252         if (!Arrays.equals(members, nestedTypes)) {
 253             if (!expectDups) {
 254                 throw new Error("Class " + host.getName() + " has different members " +
 255                                 "compared to declared classes");
 256             }
 257             else {
 258                 // get rid of duplicates
 259                 Class<?>[] memberSet =
 260                     new HashSet<Class<?>>(Arrays.asList(members)).toArray(new Class<?>[0]);
 261                 Arrays.sort(memberSet, cmp);
 262                 if (!Arrays.equals(memberSet, nestedTypes)) {
 263                     throw new Error("Class " + host.getName() + " has different members " +
 264                                 "compared to declared classes, even after duplicate removal");
 265                 }
 266             }
 267         }
 268         // verify all the relationships that must hold for nest members
 269         for (Class<?> a : members) {
 270             checkHost(a, host);
 271             checkNestmates(a, host, true);
 272             Class<?>[] aMembers = a.getNestMembers();
 273             if (aMembers[0] != host) {
 274                 throw new Error("Class " + a.getName() + " getNestMembers()[0] = " +
 275                                 aMembers[0].getName() + " not " + host.getName());
 276 
 277             }
 278             Arrays.sort(aMembers, cmp);
 279             if (!Arrays.equals(members, aMembers)) {
 280                 throw new Error("Class " + a.getName() + " has different members " +
 281                                 "compared to host " + host.getName());
 282             }
 283             for (Class<?> b : members) {
 284                 checkNestmates(a, b, true);
 285             }
 286         }
 287     }
 288 
 289     static void checkSingletonNests(Class<?>[] classes) {
 290         for (Class<?> host : classes) {
 291             Class<?>[] members = host.getNestMembers();
 292             if (members.length != 1) {
 293                 printMembers(host, members);
 294                 throw new Error("Class " + host.getName() + " lists " + members.length
 295                                 + " members instead of 1 (itself)");
 296             }
 297             if (members[0] != host) {
 298                 printMembers(host, members);
 299                 throw new Error("Class " + host.getName() + " lists " +
 300                                 members[0].getName() + " as member instead of itself");
 301             }
 302         }
 303     }
 304 
 305     static void printMembers(Class<?> host, Class<?>[] members) {
 306         System.out.println("Class " + host.getName() + " has members: ");
 307         for (Class<?> c : members) {
 308             System.out.println(" - " + c.getName());
 309         }
 310     }
 311 
 312     static void printDeclared(Class<?> host, Class<?>[] declared) {
 313         System.out.println("Class " + host.getName() + " has declared types: ");
 314         for (Class<?> c : declared) {
 315             System.out.println(" - " + c.getName());
 316         }
 317     }
 318 
 319 }