1 /*
   2  * Copyright (c) 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  * @summary Test the rules for dynamic nest membership using the Lookup.defineHiddenClass API
  27  * @compile OtherPackage.java
  28  * @run main/othervm TestDynamicNestmateMembership
  29  */
  30 
  31 import java.io.IOException;
  32 import java.nio.file.Files;
  33 import java.nio.file.Paths;
  34 import java.nio.file.Path;
  35 import java.lang.invoke.MethodHandles;
  36 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
  37 
  38 /* package */ class DynamicNestmate { }
  39 
  40 /* package */ class DynamicNestmate2 { }
  41 
  42 /* package */ class StaticHost {
  43     static class StaticMember {
  44     }
  45 }
  46 
  47 public class TestDynamicNestmateMembership {
  48 
  49     static class Member {
  50         static MethodHandles.Lookup getLookup() {
  51             return MethodHandles.lookup();
  52         }
  53     }
  54 
  55     static final String CLASSES = System.getProperty("test.classes");
  56     static final Path CLASSES_DIR = Paths.get(CLASSES);
  57 
  58     static byte[] getBytesForClass(String name) throws IOException {
  59         Path classFile;
  60         if (name.indexOf('.') > 0) {
  61             // it's in a package
  62             String[] paths = name.split("\\.");
  63             classFile = CLASSES_DIR.resolve(paths[0]);
  64             classFile = classFile.resolve(paths[1] + ".class");
  65         }
  66         else {
  67             classFile = CLASSES_DIR.resolve(name + ".class");
  68         }
  69         return Files.readAllBytes(classFile);
  70     }
  71 
  72     static final Class<?> cls = TestDynamicNestmateMembership.class;
  73     static final MethodHandles.Lookup main_lookup = MethodHandles.lookup();
  74 
  75     public static void main(String[] args) throws Throwable {
  76         test_validInjection();
  77         test_hostIsMember();
  78         test_otherPackage();
  79         test_alreadyNestMember();
  80         test_alreadyNestHost();
  81     }
  82 
  83     // Inject a valid class into the nest of the current class
  84     static void test_validInjection() {
  85         String name = "DynamicNestmate";
  86         inject(name, null);
  87     }
  88 
  89     // Try to inject a class into a "host" that is itself a member.
  90     // This is redirected at the defineClass level to the member's
  91     // host and so will succeed.
  92     static void test_hostIsMember() {
  93         String name = "DynamicNestmate2";
  94         inject(name, Member.getLookup(), null);
  95     }
  96 
  97     // Try to inject a class that has a static NestHost attribute
  98     // No error since the nest host has been set when it's created.
  99     // Static nest membership is effectively ignored.
 100     static void test_alreadyNestMember() {
 101         String name = "StaticHost$StaticMember";
 102         inject(name, null);
 103     }
 104 
 105     // Try to inject a class that has the NestMembers attribute.
 106     // No error since the nest host has been set when it's created.
 107     // Static nest membership is effectively ignored.
 108     static void test_alreadyNestHost() {
 109         String name = "StaticHost";
 110         inject(name, null);
 111     }
 112 
 113     // Try to inject a class that is in another package
 114     static void test_otherPackage() {
 115         String name = "test.OtherPackage";
 116         inject(name, IllegalArgumentException.class);
 117     }
 118 
 119     static void inject(String name, Class<? extends Throwable> ex) {
 120         inject(name, main_lookup, ex);
 121     }
 122 
 123     static void inject(String name, MethodHandles.Lookup lookup,
 124                        Class<? extends Throwable> ex) {
 125         Class<?> target = lookup.lookupClass();
 126         String action = "Injecting " + name + " into the nest of " +
 127             target.getSimpleName();
 128         try {
 129             byte[] bytes = getBytesForClass(name);
 130             Class<?> nestmate = lookup.defineHiddenClass(bytes, false, NESTMATE).lookupClass();
 131             if (ex != null) {
 132                 throw new RuntimeException(action + " was expected to throw " +
 133                                            ex.getSimpleName());
 134             }
 135             Class<?> actualHost = nestmate.getNestHost();
 136             Class<?> expectedHost = target.getNestHost();
 137             if (actualHost != expectedHost) {
 138                 throw new RuntimeException(action + " expected a nest-host of "
 139                                            + expectedHost.getSimpleName() +
 140                                            " but got " + actualHost.getSimpleName());
 141             }
 142             System.out.print("Ok: " + action + " succeeded: ");
 143             if (actualHost != target) {
 144                 System.out.print("(re-directed to target's nest-host) ");
 145             }
 146             System.out.println("Nesthost of " + nestmate.getName() +
 147                                " is " + actualHost.getName());
 148         } catch (Throwable t) {
 149             if (t.getClass() == ex) {
 150                 System.out.println("Ok: " + action + " got expected exception: " +
 151                                    t.getClass().getSimpleName() + ":" +
 152                                    t.getMessage());
 153             }
 154             else {
 155                 throw new RuntimeException(action + " got unexpected exception " +
 156                                            t.getClass().getSimpleName(), t);
 157             }
 158         }
 159     }
 160 
 161 }
 162