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 defineStrongClass() throws Throwable { 117 Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(T_CLASS_BYTES, classData, true, STRONG); 118 Class<?> c = lookup.lookupClass(); 119 assertTrue(c.getNestHost() == c); 120 assertTrue(c.isHiddenClass()); 121 } 122 123 @Test(expectedExceptions = IllegalAccessException.class) 124 public void noPrivateLookupAccess() throws Throwable { 125 Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); 126 lookup.defineHiddenClassWithClassData(T2_CLASS_BYTES, classData, false, NESTMATE); 127 } 128 129 @Test 130 public void teleportToNestmate() throws Throwable { 131 Lookup lookup = MethodHandles.lookup() 132 .defineHiddenClassWithClassData(T_CLASS_BYTES, classData, false, NESTMATE); 133 Class<?> c = lookup.lookupClass(); 134 assertTrue(c.getNestHost() == DefineClassWithClassData.class); 135 assertEquals(classData, injectedData(c)); 136 assertTrue(c.isHiddenClass()); 137 138 // Teleport to a nestmate 139 Lookup lookup2 = MethodHandles.lookup().in(DefineClassWithClassData.class); 140 assertTrue((lookup2.lookupModes() & PRIVATE) != 0); 141 Lookup lc = lookup2.defineHiddenClassWithClassData(T2_CLASS_BYTES, classData, false, NESTMATE); 142 assertTrue(lc.lookupClass().getNestHost() == DefineClassWithClassData.class); 143 assertTrue(lc.lookupClass().isHiddenClass()); 144 } 145 146 static class ClassByteBuilder { 147 static final String OBJECT_CLS = "java/lang/Object"; 148 static final String MHS_CLS = "java/lang/invoke/MethodHandles"; 149 static final String LIST_SIG = "Ljava/util/List;"; 150 151 static byte[] classBytes(String classname) { 152 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 153 MethodVisitor mv; 154 FieldVisitor fv; 155 156 String hostClassName = DefineClassWithClassData.class.getName(); 157 158 cw.visit(V13, ACC_FINAL, classname, null, OBJECT_CLS, null); 159 { 160 fv = cw.visitField(ACC_STATIC | ACC_FINAL, "data", LIST_SIG, null, null); 161 fv.visitEnd(); 162 } 163 { 164 mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); 165 mv.visitCode(); 166 167 Handle bsm = new Handle(H_INVOKESTATIC, MHS_CLS, "classData", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;", false); 168 ConstantDynamic dynamic = new ConstantDynamic("classdata", LIST_SIG, bsm); 169 mv.visitLdcInsn(dynamic); 170 mv.visitFieldInsn(PUTSTATIC, classname, "data", LIST_SIG); 171 mv.visitInsn(RETURN); 172 mv.visitMaxs(0, 0); 173 mv.visitEnd(); 174 } 175 176 { 177 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 178 mv.visitCode(); 179 mv.visitVarInsn(ALOAD, 0); 180 mv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLS, "<init>", "()V", false); 181 mv.visitInsn(RETURN); 182 mv.visitMaxs(0, 0); 183 mv.visitEnd(); 184 } 185 { 186 mv = cw.visitMethod(ACC_PUBLIC, "test", "(L" + hostClassName + ";)I", null, null); 187 mv.visitCode(); 188 mv.visitVarInsn(ALOAD, 0); 189 mv.visitVarInsn(ALOAD, 1); 190 mv.visitMethodInsn(INVOKEVIRTUAL, hostClassName, "privMethod", "()I", false); 191 mv.visitInsn(IRETURN); 192 mv.visitMaxs(0, 0); 193 mv.visitEnd(); 194 } 195 196 { 197 mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "printData", "()V", null, null); 198 mv.visitCode(); 199 mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 200 mv.visitFieldInsn(GETSTATIC, classname, "data", LIST_SIG); 201 mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false); 202 mv.visitInsn(RETURN); 203 mv.visitMaxs(0, 0); 204 mv.visitEnd(); 205 } 206 cw.visitEnd(); 207 208 byte[] bytes = cw.toByteArray(); 209 Path p = Paths.get(classname + ".class"); 210 try (OutputStream os = Files.newOutputStream(p)) { 211 os.write(bytes); 212 } catch (IOException e) { 213 throw new UncheckedIOException(e); 214 } 215 return bytes; 216 } 217 } 218 } 219 220 --- EOF ---