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