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 }