1 /*
   2  * Copyright (c) 2015, 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 8057967
  27  * @ignore 8079205
  28  * @run main/bootclasspath -Xbatch java.lang.invoke.CallSiteDepContextTest
  29  */
  30 package java.lang.invoke;
  31 
  32 import java.lang.ref.*;
  33 import jdk.internal.org.objectweb.asm.*;
  34 import sun.misc.Unsafe;
  35 
  36 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  37 
  38 public class CallSiteDepContextTest {
  39     static final Unsafe               UNSAFE = Unsafe.getUnsafe();
  40     static final MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP;
  41     static final String           CLASS_NAME = "java/lang/invoke/Test";
  42     static final String          METHOD_NAME = "m";
  43     static final MethodType             TYPE = MethodType.methodType(int.class);
  44 
  45     static MutableCallSite mcs;
  46     static MethodHandle bsmMH;
  47 
  48     static {
  49         try {
  50             bsmMH = LOOKUP.findStatic(
  51                     CallSiteDepContextTest.class, "bootstrap",
  52                     MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class));
  53         } catch(Throwable e) {
  54             throw new InternalError(e);
  55         }
  56     }
  57 
  58     public static CallSite bootstrap(MethodHandles.Lookup caller,
  59                                      String invokedName,
  60                                      MethodType invokedType) {
  61         return mcs;
  62     }
  63 
  64     static class T {
  65         static int f1() { return 1; }
  66         static int f2() { return 2; }
  67     }
  68 
  69     static byte[] getClassFile(String suffix) {
  70         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
  71         MethodVisitor mv;
  72         cw.visit(52, ACC_PUBLIC | ACC_SUPER, CLASS_NAME + suffix, null, "java/lang/Object", null);
  73         {
  74             mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, METHOD_NAME, TYPE.toMethodDescriptorString(), null, null);
  75             mv.visitCode();
  76             Handle bsm = new Handle(H_INVOKESTATIC,
  77                     "java/lang/invoke/CallSiteDepContextTest", "bootstrap",
  78                     bsmMH.type().toMethodDescriptorString());
  79             mv.visitInvokeDynamicInsn("methodName", TYPE.toMethodDescriptorString(), bsm);
  80             mv.visitInsn(IRETURN);
  81             mv.visitMaxs(0, 0);
  82             mv.visitEnd();
  83         }
  84         cw.visitEnd();
  85         return cw.toByteArray();
  86     }
  87 
  88     private static void execute(int expected, MethodHandle... mhs) throws Throwable {
  89         for (int i = 0; i < 20_000; i++) {
  90             for (MethodHandle mh : mhs) {
  91                 int r = (int) mh.invokeExact();
  92                 if (r != expected) {
  93                     throw new Error(r + " != " + expected);
  94                 }
  95             }
  96         }
  97     }
  98 
  99     public static void testSharedCallSite() throws Throwable {
 100         Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_1"), null);
 101         Class<?> cls2 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_2"), null);
 102 
 103         MethodHandle[] mhs = new MethodHandle[] {
 104             LOOKUP.findStatic(cls1, METHOD_NAME, TYPE),
 105             LOOKUP.findStatic(cls2, METHOD_NAME, TYPE)
 106         };
 107 
 108         mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
 109         execute(1, mhs);
 110         mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
 111         execute(2, mhs);
 112     }
 113 
 114     public static void testNonBoundCallSite() throws Throwable {
 115         mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
 116 
 117         // mcs.context == null
 118         MethodHandle mh = mcs.dynamicInvoker();
 119         execute(1, mh);
 120 
 121         // mcs.context == cls1
 122         Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("NonBound_1"), null);
 123         MethodHandle mh1 = LOOKUP.findStatic(cls1, METHOD_NAME, TYPE);
 124 
 125         execute(1, mh1);
 126 
 127         mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
 128 
 129         execute(2, mh, mh1);
 130     }
 131 
 132     static ReferenceQueue rq = new ReferenceQueue();
 133     static PhantomReference ref;
 134 
 135     public static void testGC() throws Throwable {
 136         mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
 137 
 138         Class<?>[] cls = new Class[] {
 139                 UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_1"), null),
 140                 UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_2"), null),
 141         };
 142 
 143         MethodHandle[] mhs = new MethodHandle[] {
 144                 LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE),
 145                 LOOKUP.findStatic(cls[1], METHOD_NAME, TYPE),
 146         };
 147 
 148         // mcs.context == cls[0]
 149         int r = (int) mhs[0].invokeExact();
 150 
 151         execute(1, mhs);
 152 
 153         ref = new PhantomReference<>(cls[0], rq);
 154         cls[0] = UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_3"), null);
 155         mhs[0] = LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE);
 156 
 157         do {
 158             System.gc();
 159             try {
 160                 Reference ref1 = rq.remove(1000);
 161                 if (ref1 == ref) {
 162                     ref1.clear();
 163                     System.gc(); // Ensure that the stale context is cleared
 164                     break;
 165                 }
 166             } catch(InterruptedException e) { /* ignore */ }
 167         } while (true);
 168 
 169         execute(1, mhs);
 170         mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
 171         execute(2, mhs);
 172     }
 173 
 174     public static void main(String[] args) throws Throwable {
 175         testSharedCallSite();
 176         testNonBoundCallSite();
 177         testGC();
 178         System.out.println("TEST PASSED");
 179     }
 180 }