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  * @bug 8200261
  27  * @summary Tests an anonymous class that implements interfaces with default methods.
  28  * @library /testlibrary
  29  * @modules java.base/jdk.internal.org.objectweb.asm
  30  *          java.management
  31  * @compile -XDignore.symbol.file=true UnsafeDefMeths.java
  32  * @run main UnsafeDefMeths
  33  */
  34 
  35 import jdk.internal.org.objectweb.asm.ClassWriter;
  36 import jdk.internal.org.objectweb.asm.MethodVisitor;
  37 import jdk.internal.org.objectweb.asm.Type;
  38 import sun.misc.Unsafe;
  39 
  40 import java.lang.invoke.MethodType;
  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 UnsafeDefMeths {
  59 
  60     static final Unsafe UNSAFE;
  61 
  62     static {
  63         try {
  64             Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
  65             unsafeField.setAccessible(true);
  66             UNSAFE = (Unsafe) unsafeField.get(null);
  67         }
  68         catch (Exception e) {
  69             throw new InternalError(e);
  70         }
  71     }
  72 
  73     interface Resource {
  74         Pointer ptr();
  75     }
  76 
  77     interface Struct extends Resource {
  78        StructPointer ptr();
  79     }
  80 
  81     interface Pointer { }
  82 
  83     interface StructPointer extends Pointer { }
  84 
  85     interface I extends Struct {
  86         void m();
  87     }
  88 
  89     static String IMPL_PREFIX = "$$impl";
  90     static String PTR_FIELD_NAME = "ptr";
  91 
  92     public static void main(String[] args) throws Throwable {
  93         byte[] bytes = new UnsafeDefMeths().generate(I.class);
  94         Class<?> cl = UNSAFE.defineAnonymousClass(I.class, bytes, new Object[0]);
  95         I i = (I)cl.getConstructors()[0].newInstance(new Object[] { null }); //exception here!
  96     }
  97 
  98     // Generate a class similar to:
  99     //
 100     // public class UnsafeDefMeths$I$$impl implements UnsafeDefMeths$I, UnsafeDefMeths$Struct {
 101     //
 102     //     public UnsafeDefMeths$StructPointer ptr;
 103     //
 104     //     public UnsafeDefMeths$I$$impl(UnsafeDefMeths$StructPointer p) {
 105     //         ptr = p;
 106     //     }
 107     //
 108     //     public UnsafeDefMeths$StructPointer ptr() {
 109     //         return ptr;
 110     //     }
 111     // }
 112     //
 113     byte[] generate(Class<?> iface) {
 114         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
 115 
 116         String ifaceTypeName = Type.getInternalName(iface);
 117         String proxyClassName = ifaceTypeName + IMPL_PREFIX;
 118         // class definition
 119         cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, proxyClassName,
 120                 desc(Object.class) + desc(ifaceTypeName) + desc(Struct.class),
 121                 name(Object.class),
 122                 new String[] { ifaceTypeName, name(Struct.class) });
 123 
 124         cw.visitField(ACC_PUBLIC, PTR_FIELD_NAME, desc(StructPointer.class), desc(StructPointer.class), null);
 125         cw.visitEnd();
 126 
 127         // constructor
 128         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>",
 129                 meth(desc(void.class), desc(StructPointer.class)),
 130                 meth(desc(void.class), desc(StructPointer.class)), null);
 131         mv.visitCode();
 132         mv.visitVarInsn(ALOAD, 0);
 133         mv.visitInsn(DUP);
 134         mv.visitMethodInsn(INVOKESPECIAL, name(Object.class), "<init>", meth(desc(void.class)), false);
 135         mv.visitVarInsn(ALOAD, 1);
 136         mv.visitFieldInsn(PUTFIELD, proxyClassName, PTR_FIELD_NAME, desc(StructPointer.class));
 137         mv.visitInsn(RETURN);
 138         mv.visitMaxs(0, 0);
 139         mv.visitEnd();
 140 
 141         // ptr() impl
 142         mv = cw.visitMethod(ACC_PUBLIC, PTR_FIELD_NAME, meth(desc(StructPointer.class)),
 143                 meth(desc(StructPointer.class)), null);
 144         mv.visitCode();
 145         mv.visitVarInsn(ALOAD, 0);
 146         mv.visitFieldInsn(GETFIELD, proxyClassName, PTR_FIELD_NAME, desc(StructPointer.class));
 147         mv.visitInsn(ARETURN);
 148         mv.visitMaxs(0, 0);
 149         mv.visitEnd();
 150 
 151         return cw.toByteArray();
 152     }
 153 
 154     String name(Class<?> clazz) {
 155         if (clazz.isPrimitive()) {
 156             throw new IllegalStateException();
 157         } else if (clazz.isArray()) {
 158             return desc(clazz);
 159         } else {
 160             return clazz.getName().replaceAll("\\.", "/");
 161         }
 162     }
 163 
 164     String desc(Class<?> clazz) {
 165         String mdesc = MethodType.methodType(clazz).toMethodDescriptorString();
 166         return mdesc.substring(mdesc.indexOf(')') + 1);
 167     }
 168 
 169     String desc(String clazzName) {
 170         return "L" + clazzName + ";";
 171     }
 172 
 173     String gen(String clazz, String... typeargs) {
 174         return clazz.substring(0, clazz.length() - 1) + Stream.of(typeargs).collect(Collectors.joining("", "<", ">")) + ";";
 175     }
 176 
 177     String meth(String restype, String... argtypes) {
 178         return Stream.of(argtypes).collect(Collectors.joining("", "(", ")")) + restype;
 179     }
 180 
 181     String meth(Method m) {
 182         return MethodType.methodType(m.getReturnType(), m.getParameterTypes()).toMethodDescriptorString();
 183     }
 184 }