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