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 * @library /test/lib 27 * @modules java.base/jdk.internal.org.objectweb.asm 28 * @build DefineClassWithClassData 29 * @run testng/othervm DefineClassWithClassData 30 */ 31 32 import java.io.IOException; 33 import java.io.OutputStream; 34 import java.io.UncheckedIOException; 35 import java.lang.invoke.MethodHandles; 36 import java.lang.invoke.MethodHandles.Lookup; 37 import java.lang.reflect.InvocationTargetException; 38 import java.lang.reflect.Method; 39 import java.nio.file.Files; 40 import java.nio.file.Path; 41 import java.nio.file.Paths; 42 import java.util.List; 43 import java.util.stream.Stream; 44 45 import jdk.internal.org.objectweb.asm.*; 46 import org.testng.annotations.Test; 47 48 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*; 49 import static java.lang.invoke.MethodHandles.Lookup.PRIVATE; 50 import static jdk.internal.org.objectweb.asm.Opcodes.*; 51 import static org.testng.Assert.*; 52 53 public class DefineClassWithClassData { 54 private static final byte[] T_CLASS_BYTES = ClassByteBuilder.classBytes("T"); 55 private static final byte[] T2_CLASS_BYTES = ClassByteBuilder.classBytes("T2"); 56 57 private int privMethod() { return 1234; } 58 59 /* 60 * invoke int test(DefineClassWithClassData o) method defined in the injected class 61 */ 62 private int testInjectedClass(Class<?> c) throws Throwable { 63 try { 64 Method m = c.getMethod("test", DefineClassWithClassData.class); 65 return (int) m.invoke(c.newInstance(), this); 66 } catch (InvocationTargetException e) { 67 throw e.getCause(); 68 } 69 } 70 71 /* 72 * Returns the value of the static final "data" field in the injected class 73 */ 74 private Object injectedData(Class<?> c) throws Throwable { 75 return c.getDeclaredField("data").get(null); 76 } 77 78 private static final List<String> classData = List.of("nestmate", "classdata"); 79 80 @Test 81 public void defineNestMate() throws Throwable { 82 // define a nestmate 83 Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(T_CLASS_BYTES, classData, true, NESTMATE); 84 Class<?> c = lookup.lookupClass(); 85 assertTrue(c.getNestHost() == DefineClassWithClassData.class); 86 assertEquals(classData, injectedData(c)); 87 88 // invoke int test(DefineClassWithClassData o) 89 int x = testInjectedClass(c); 90 assertTrue(x == privMethod()); 91 92 // dynamic nestmate is not listed in the return array of getNestMembers 93 assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); 94 assertTrue(c.isNestmateOf(DefineClassWithClassData.class)); 95 } 96 97 @Test 98 public void defineHiddenClass() throws Throwable { 99 // define a hidden class 100 Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(T_CLASS_BYTES, classData, false, NESTMATE); 101 Class<?> c = lookup.lookupClass(); 102 assertTrue(c.getNestHost() == DefineClassWithClassData.class); 103 assertTrue(c.isHiddenClass()); 104 assertEquals(classData, injectedData(c)); 105 106 // invoke int test(DefineClassWithClassData o) 107 int x = testInjectedClass(c); 108 assertTrue(x == privMethod()); 109 110 // dynamic nestmate is not listed in the return array of getNestMembers 111 assertTrue(Stream.of(c.getNestHost().getNestMembers()).noneMatch(k -> k == c)); 112 assertTrue(c.isNestmateOf(DefineClassWithClassData.class)); 113 } 114 115 @Test 116 public void defineWeakClass() throws Throwable { 117 // define a weak class 118 Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(T_CLASS_BYTES, classData, true, WEAK); 119 Class<?> c = lookup.lookupClass(); 120 assertTrue(c.getNestHost() == c); 121 assertTrue(c.isHiddenClass()); 122 } 123 124 @Test(expectedExceptions = IllegalAccessException.class) 125 public void noPrivateLookupAccess() throws Throwable { 126 Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); 127 lookup.defineHiddenClassWithClassData(T2_CLASS_BYTES, classData, false, NESTMATE); 128 } 129 130 @Test 131 public void teleportToNestmate() throws Throwable { 132 Lookup lookup = MethodHandles.lookup() 133 .defineHiddenClassWithClassData(T_CLASS_BYTES, classData, false, NESTMATE); 134 Class<?> c = lookup.lookupClass(); 135 assertTrue(c.getNestHost() == DefineClassWithClassData.class); 136 assertEquals(classData, injectedData(c)); 137 assertTrue(c.isHiddenClass()); 138 139 // Teleport to a nestmate 140 Lookup lookup2 = MethodHandles.lookup().in(DefineClassWithClassData.class); 141 assertTrue((lookup2.lookupModes() & PRIVATE) != 0); 142 Lookup lc = lookup2.defineHiddenClassWithClassData(T2_CLASS_BYTES, classData, false, NESTMATE); 143 assertTrue(lc.lookupClass().getNestHost() == DefineClassWithClassData.class); 144 assertTrue(lc.lookupClass().isHiddenClass()); 145 } 146 147 static class ClassByteBuilder { 148 static final String OBJECT_CLS = "java/lang/Object"; 149 static final String MHS_CLS = "java/lang/invoke/MethodHandles"; 150 static final String LIST_SIG = "Ljava/util/List;"; 151 152 static byte[] classBytes(String classname) { 153 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 154 MethodVisitor mv; 155 FieldVisitor fv; 156 157 String hostClassName = DefineClassWithClassData.class.getName(); 158 159 cw.visit(V13, ACC_FINAL, classname, null, OBJECT_CLS, null); 160 { 161 fv = cw.visitField(ACC_STATIC | ACC_FINAL, "data", LIST_SIG, null, null); 162 fv.visitEnd(); 163 } 164 { 165 mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); 166 mv.visitCode(); 167 168 Handle bsm = new Handle(H_INVOKESTATIC, MHS_CLS, "classData", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;", false); 169 ConstantDynamic dynamic = new ConstantDynamic("classdata", LIST_SIG, bsm); 170 mv.visitLdcInsn(dynamic); 171 mv.visitFieldInsn(PUTSTATIC, classname, "data", LIST_SIG); 172 mv.visitInsn(RETURN); 173 mv.visitMaxs(0, 0); 174 mv.visitEnd(); 175 } 176 177 { 178 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 179 mv.visitCode(); 180 mv.visitVarInsn(ALOAD, 0); 181 mv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLS, "<init>", "()V", false); 182 mv.visitInsn(RETURN); 183 mv.visitMaxs(0, 0); 184 mv.visitEnd(); 185 } 186 { 187 mv = cw.visitMethod(ACC_PUBLIC, "test", "(L" + hostClassName + ";)I", null, null); 188 mv.visitCode(); 189 mv.visitVarInsn(ALOAD, 0); 190 mv.visitVarInsn(ALOAD, 1); 191 mv.visitMethodInsn(INVOKEVIRTUAL, hostClassName, "privMethod", "()I", false); 192 mv.visitInsn(IRETURN); 193 mv.visitMaxs(0, 0); 194 mv.visitEnd(); 195 } 196 197 { 198 mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "printData", "()V", null, null); 199 mv.visitCode(); 200 mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 201 mv.visitFieldInsn(GETSTATIC, classname, "data", LIST_SIG); 202 mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false); 203 mv.visitInsn(RETURN); 204 mv.visitMaxs(0, 0); 205 mv.visitEnd(); 206 } 207 cw.visitEnd(); 208 209 byte[] bytes = cw.toByteArray(); 210 Path p = Paths.get(classname + ".class"); 211 try (OutputStream os = Files.newOutputStream(p)) { 212 os.write(bytes); 213 } catch (IOException e) { 214 throw new UncheckedIOException(e); 215 } 216 return bytes; 217 } 218 } 219 } 220 221