1 /*
   2  * Copyright (c) 2014, 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 /* @test
  25  * @bug 8046903
  26  * @summary VM anonymous class members can't be statically invocable
  27  * @run junit test.java.lang.invoke.VMAnonymousClass
  28  */
  29 package test.java.lang.invoke;
  30 
  31 import java.lang.invoke.MethodHandle;
  32 import java.lang.invoke.MethodHandles;
  33 import java.lang.invoke.MethodType;
  34 import java.lang.reflect.Field;
  35 import org.junit.Test;
  36 import sun.misc.Unsafe;
  37 import jdk.internal.org.objectweb.asm.*;
  38 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  39 
  40 public class VMAnonymousClass {
  41     public static void main(String[] args) throws Throwable {
  42         VMAnonymousClass test = new VMAnonymousClass();
  43         test.testJavaLang();
  44         test.testJavaUtil();
  45         test.testSunMisc();
  46         test.testJavaLangInvoke();
  47         System.out.println("TEST PASSED");
  48     }
  49 
  50     // Test VM anonymous classes from different packages
  51     // (see j.l.i.InvokerBytecodeGenerator::isStaticallyInvocable).
  52     @Test public void testJavaLang()       throws Throwable { test("java/lang");        }
  53     @Test public void testJavaUtil()       throws Throwable { test("java/util");        }
  54     @Test public void testSunMisc()        throws Throwable { test("sun/misc");         }
  55     @Test public void testJavaLangInvoke() throws Throwable { test("java/lang/invoke"); }
  56 
  57     private static Unsafe unsafe = getUnsafe();
  58 
  59     private static void test(String pkg) throws Throwable {
  60         byte[] bytes = dumpClass(pkg);
  61         // Define VM anonymous class in privileged context (on BCP).
  62         Class anonClass = unsafe.defineAnonymousClass(Object.class, bytes, null);
  63 
  64         MethodType t = MethodType.methodType(Object.class, int.class);
  65         MethodHandle target = MethodHandles.lookup().findStatic(anonClass, "get", t);
  66 
  67         // Wrap target into LF (convert) to get "target" referenced from LF
  68         MethodHandle wrappedMH = target.asType(MethodType.methodType(Object.class, Integer.class));
  69 
  70         // Invoke enough times to provoke LF compilation to bytecode.
  71         for (int i = 0; i<100; i++) {
  72             Object r = wrappedMH.invokeExact((Integer)1);
  73         }
  74     }
  75 
  76     /*
  77      * Constructs bytecode for the following class:
  78      * public class pkg.MyClass {
  79      *     MyClass() {}
  80      *     public Object get(int i) { return null; }
  81      * }
  82      */
  83     public static byte[] dumpClass(String pkg) {
  84         ClassWriter cw = new ClassWriter(0);
  85         MethodVisitor mv;
  86 
  87         cw.visit(52, ACC_SUPER | ACC_PUBLIC, pkg+"/MyClass", null, "java/lang/Object", null);
  88         {
  89             mv = cw.visitMethod(0, "<init>", "()V", null, null);
  90             mv.visitCode();
  91             mv.visitVarInsn(ALOAD, 0);
  92             mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
  93             mv.visitInsn(RETURN);
  94             mv.visitMaxs(1, 1);
  95             mv.visitEnd();
  96         }
  97         {
  98             mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "get", "(I)Ljava/lang/Object;", null, null);
  99             mv.visitCode();
 100             mv.visitInsn(ACONST_NULL);
 101             mv.visitInsn(ARETURN);
 102             mv.visitMaxs(1, 1);
 103             mv.visitEnd();
 104         }
 105         cw.visitEnd();
 106         return cw.toByteArray();
 107     }
 108 
 109     private static synchronized Unsafe getUnsafe() {
 110         try {
 111             Field f = Unsafe.class.getDeclaredField("theUnsafe");
 112             f.setAccessible(true);
 113             return (Unsafe) f.get(null);
 114         } catch (NoSuchFieldException | IllegalAccessException e) {
 115             throw new RuntimeException("Unable to get Unsafe instance.", e);
 116         }
 117     }
 118 }