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