1 /* 2 * Copyright (c) 2020, 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 Tests a hidden class that implements interfaces with default methods. 27 * @library /testlibrary 28 * @modules java.base/jdk.internal.org.objectweb.asm 29 * java.management 30 * @run main HiddenDefMeths 31 */ 32 33 import jdk.internal.org.objectweb.asm.ClassWriter; 34 import jdk.internal.org.objectweb.asm.MethodVisitor; 35 import jdk.internal.org.objectweb.asm.Type; 36 37 import java.lang.invoke.MethodType; 38 import java.lang.invoke.MethodHandles; 39 import java.lang.invoke.MethodHandles.Lookup; 40 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*; 41 import java.lang.reflect.Field; 42 import java.lang.reflect.Method; 43 import java.util.stream.Collectors; 44 import java.util.stream.Stream; 45 46 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; 47 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 48 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; 49 import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 50 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; 51 import static jdk.internal.org.objectweb.asm.Opcodes.DUP; 52 import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD; 53 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; 54 import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD; 55 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 56 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8; 57 58 public class HiddenDefMeths { 59 60 interface Resource { 61 Pointer ptr(); 62 } 63 64 interface Struct extends Resource { 65 StructPointer ptr(); 66 } 67 68 interface Pointer { } 69 70 interface StructPointer extends Pointer { } 71 72 interface I extends Struct { 73 void m(); 74 } 75 76 static String IMPL_PREFIX = "$$impl"; 77 static String PTR_FIELD_NAME = "ptr"; 78 79 // Generate a class similar to: 80 // 81 // public class HiddenDefMeths$I$$impl implements HiddenDefMeths$I, HiddenDefMeths$Struct { 82 // 83 // public HiddenDefMeths$StructPointer ptr; 84 // 85 // public HiddenDefMeths$I$$impl(HiddenDefMeths$StructPointer p) { 86 // ptr = p; 87 // } 88 // 89 // public HiddenDefMeths$StructPointer ptr() { 90 // return ptr; 91 // } 92 // } 93 // 94 byte[] generate(Class<?> iface) { 95 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 96 97 String ifaceTypeName = Type.getInternalName(iface); 98 String proxyClassName = ifaceTypeName + IMPL_PREFIX; 99 // class definition 100 cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, proxyClassName, 101 desc(Object.class) + desc(ifaceTypeName) + desc(Struct.class), 102 name(Object.class), 103 new String[] { ifaceTypeName, name(Struct.class) }); 104 105 cw.visitField(ACC_PUBLIC, PTR_FIELD_NAME, desc(StructPointer.class), desc(StructPointer.class), null); 106 cw.visitEnd(); 107 108 // constructor 109 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", 110 meth(desc(void.class), desc(StructPointer.class)), 111 meth(desc(void.class), desc(StructPointer.class)), null); 112 mv.visitCode(); 113 mv.visitVarInsn(ALOAD, 0); 114 mv.visitInsn(DUP); 115 mv.visitMethodInsn(INVOKESPECIAL, name(Object.class), "<init>", meth(desc(void.class)), false); 116 mv.visitVarInsn(ALOAD, 1); 117 // Execution of this PUTFIELD instruction causes the bug's ClassNotFoundException. 118 mv.visitFieldInsn(PUTFIELD, proxyClassName, PTR_FIELD_NAME, desc(StructPointer.class)); 119 mv.visitInsn(RETURN); 120 mv.visitMaxs(0, 0); 121 mv.visitEnd(); 122 123 // ptr() impl 124 mv = cw.visitMethod(ACC_PUBLIC, PTR_FIELD_NAME, meth(desc(StructPointer.class)), 125 meth(desc(StructPointer.class)), null); 126 mv.visitCode(); 127 mv.visitVarInsn(ALOAD, 0); 128 mv.visitFieldInsn(GETFIELD, proxyClassName, PTR_FIELD_NAME, desc(StructPointer.class)); 129 mv.visitInsn(ARETURN); 130 mv.visitMaxs(0, 0); 131 mv.visitEnd(); 132 133 return cw.toByteArray(); 134 } 135 136 String name(Class<?> clazz) { 137 if (clazz.isPrimitive()) { 138 throw new IllegalStateException(); 139 } else if (clazz.isArray()) { 140 return desc(clazz); 141 } else { 142 return clazz.getName().replaceAll("\\.", "/"); 143 } 144 } 145 146 String desc(Class<?> clazz) { 147 String mdesc = MethodType.methodType(clazz).toMethodDescriptorString(); 148 return mdesc.substring(mdesc.indexOf(')') + 1); 149 } 150 151 String desc(String clazzName) { 152 return "L" + clazzName + ";"; 153 } 154 155 String gen(String clazz, String... typeargs) { 156 return clazz.substring(0, clazz.length() - 1) + Stream.of(typeargs).collect(Collectors.joining("", "<", ">")) + ";"; 157 } 158 159 String meth(String restype, String... argtypes) { 160 return Stream.of(argtypes).collect(Collectors.joining("", "(", ")")) + restype; 161 } 162 163 String meth(Method m) { 164 return MethodType.methodType(m.getReturnType(), m.getParameterTypes()).toMethodDescriptorString(); 165 } 166 167 public static void main(String[] args) throws Throwable { 168 byte[] bytes = new HiddenDefMeths().generate(I.class); 169 Lookup lookup = MethodHandles.lookup(); 170 Class<?> cl = lookup.defineHiddenClass(bytes, false, NESTMATE).lookupClass(); 171 I i = (I)cl.getConstructors()[0].newInstance(new Object[] { null }); 172 } 173 }