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