1 /* 2 * Copyright (c) 2019, 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 * @library /test/lib 27 * @modules java.base/jdk.internal.org.objectweb.asm 28 * @build HiddenNestmateTest 29 * @run testng/othervm HiddenNestmateTest 30 */ 31 32 import java.lang.invoke.*; 33 import java.lang.invoke.MethodHandles.Lookup; 34 import java.lang.reflect.InvocationTargetException; 35 import java.lang.reflect.Method; 36 import java.util.stream.Stream; 37 import java.util.Arrays; 38 39 import jdk.internal.org.objectweb.asm.*; 40 import org.testng.annotations.Test; 41 42 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*; 43 import static java.lang.invoke.MethodHandles.Lookup.*; 44 45 import static jdk.internal.org.objectweb.asm.Opcodes.*; 46 import static org.testng.Assert.*; 47 48 public class HiddenNestmateTest { 49 private static final byte[] bytes = classBytes("HiddenInjected"); 50 51 private static void assertNestmate(Lookup lookup) { 52 assertTrue((lookup.lookupModes() & PRIVATE) != 0); 53 assertTrue((lookup.lookupModes() & MODULE) != 0); 54 55 Class<?> hiddenClass = lookup.lookupClass(); 56 Class<?> nestHost = hiddenClass.getNestHost(); 57 assertTrue(hiddenClass.isHiddenClass()); 58 assertTrue(nestHost == MethodHandles.lookup().lookupClass()); 59 60 // hidden nestmate is not listed in the return array of getNestMembers 61 assertTrue(Stream.of(nestHost.getNestMembers()).noneMatch(k -> k == hiddenClass)); 62 assertTrue(hiddenClass.isNestmateOf(lookup.lookupClass())); 63 assertTrue(Arrays.equals(hiddenClass.getNestMembers(), nestHost.getNestMembers())); 64 } 65 66 @Test 67 public void hiddenClass() throws Throwable { 68 // define a hidden class 69 Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false); 70 Class<?> c = lookup.lookupClass(); 71 assertTrue((lookup.lookupModes() & PRIVATE) != 0); 72 assertTrue((lookup.lookupModes() & MODULE) != 0); 73 assertTrue(c.getNestHost() == c); // host of its own nest 74 assertTrue(c.isHiddenClass()); 75 76 // invoke int test(HiddenNestmateTest o) via MethodHandle 77 MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class)); 78 MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class)); 79 try { 80 int x = (int) mh.bindTo(ctor.invoke()).invokeExact(this); 81 throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()"); 82 } catch (IllegalAccessError e) {} 83 84 // invoke int test(HiddenNestmateTest o) 85 try { 86 int x1 = testInjectedClass(c); 87 throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()"); 88 } catch (IllegalAccessError e) {} 89 } 90 91 @Test 92 public void hiddenNestmate() throws Throwable { 93 // define a hidden nestmate class 94 Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE); 95 Class<?> c = lookup.lookupClass(); 96 assertNestmate(lookup); 97 98 // invoke int test(HiddenNestmateTest o) via MethodHandle 99 MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class)); 100 MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class)); 101 int x = (int)mh.bindTo(ctor.invoke()).invokeExact( this); 102 assertTrue(x == privMethod()); 103 104 // invoke int test(HiddenNestmateTest o) 105 int x1 = testInjectedClass(c); 106 assertTrue(x1 == privMethod()); 107 } 108 109 @Test 110 public void hiddenWeakClass() throws Throwable { 111 // define a weak class 112 Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE, WEAK); 113 assertNestmate(lookup); 114 } 115 116 @Test(expectedExceptions = IllegalAccessException.class) 117 public void noPrivateLookupAccess() throws Throwable { 118 Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); 119 lookup.defineHiddenClass(bytes, false, NESTMATE); 120 } 121 122 public void teleportToNestmate() throws Throwable { 123 Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE); 124 assertNestmate(lookup); 125 126 // Teleport to a hidden nestmate 127 Lookup lc = MethodHandles.lookup().in(lookup.lookupClass()); 128 assertTrue((lc.lookupModes() & PRIVATE) != 0); 129 Lookup lc2 = lc.defineHiddenClass(bytes, false, NESTMATE); 130 assertNestmate(lc2); 131 } 132 133 @Test(expectedExceptions = IllegalArgumentException.class) 134 public void notSamePackage() throws Throwable { 135 MethodHandles.lookup().defineHiddenClass(classBytes("p/HiddenInjected"), false, NESTMATE); 136 } 137 138 /* 139 * invoke int test(HiddenNestmateTest o) method defined in the injected class 140 */ 141 private int testInjectedClass(Class<?> c) throws Throwable { 142 try { 143 Method m = c.getMethod("test", HiddenNestmateTest.class); 144 return (int) m.invoke(c.newInstance(), this); 145 } catch (InvocationTargetException e) { 146 throw e.getCause(); 147 } 148 } 149 150 private static byte[] classBytes(String classname) { 151 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); 152 MethodVisitor mv; 153 154 cw.visit(V12, ACC_FINAL, classname, null, "java/lang/Object", null); 155 156 { 157 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 158 mv.visitCode(); 159 mv.visitVarInsn(ALOAD, 0); 160 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); 161 mv.visitInsn(RETURN); 162 mv.visitMaxs(0, 0); 163 mv.visitEnd(); 164 } 165 { 166 // access a private member of the nest host class 167 mv = cw.visitMethod(ACC_PUBLIC, "test", "(LHiddenNestmateTest;)I", null, null); 168 mv.visitCode(); 169 mv.visitVarInsn(ALOAD, 0); 170 mv.visitVarInsn(ALOAD, 1); 171 mv.visitMethodInsn(INVOKEVIRTUAL, "HiddenNestmateTest", "privMethod", "()I"); 172 mv.visitInsn(IRETURN); 173 mv.visitMaxs(0, 0); 174 mv.visitEnd(); 175 } 176 cw.visitEnd(); 177 178 return cw.toByteArray(); 179 } 180 181 private int privMethod() { return 1234; } 182 }