1/* 2 * Copyright (c) 2014, 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 NullCheckDroppingsTest 26 * @bug 8054492 27 * @summary Casting can result in redundant null checks in generated code 28 * @requires vm.flavor == "server" & !vm.emulatedClient 29 * @library /test/lib 30 * @modules java.base/jdk.internal.misc 31 * java.management 32 * 33 * @build sun.hotspot.WhiteBox 34 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 35 * sun.hotspot.WhiteBox$WhiteBoxPermission 36 * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 37 * -Xmixed -XX:-BackgroundCompilation -XX:-TieredCompilation -XX:CompileThreshold=1000 38 * -XX:CompileCommand=exclude,compiler.intrinsics.klass.CastNullCheckDroppingsTest::runTest 39 * compiler.intrinsics.klass.CastNullCheckDroppingsTest 40 */ 41 42package compiler.intrinsics.klass; 43 44import jdk.test.lib.Platform; 45import sun.hotspot.WhiteBox; 46import sun.hotspot.code.NMethod; 47 48import java.lang.invoke.MethodHandle; 49import java.lang.invoke.MethodHandles; 50import java.lang.invoke.MethodType; 51import java.lang.reflect.Method; 52import java.util.function.BiFunction; 53 54public class CastNullCheckDroppingsTest { 55 56 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 57 58 static final BiFunction<Class, Object, Object> fCast = (c, o) -> c.cast(o); 59 60 static final MethodHandle SET_SSINK; 61 static final MethodHandle MH_CAST; 62 63 static { 64 try { 65 SET_SSINK = MethodHandles.lookup().findSetter(CastNullCheckDroppingsTest.class, "ssink", String.class); 66 MH_CAST = MethodHandles.lookup().findVirtual(Class.class, 67 "cast", 68 MethodType.methodType(Object.class, Object.class)); 69 } 70 catch (Exception e) { 71 throw new Error(e); 72 } 73 } 74 75 static volatile String svalue = "A"; 76 static volatile String snull = null; 77 static volatile Integer iobj = new Integer(0); 78 static volatile int[] arr = new int[2]; 79 static volatile Class objClass = String.class; 80 static volatile Class nullClass = null; 81 82 String ssink; 83 Integer isink; 84 int[] asink; 85 86 public static void main(String[] args) throws Exception { 87 if (!Platform.isServer() || Platform.isEmulatedClient()) { 88 throw new Error("TESTBUG: Not server mode"); 89 } 90 // Make sure background compilation is disabled 91 if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { 92 throw new Error("TESTBUG: Background compilation enabled"); 93 } 94 // Make sure Tiered compilation is disabled 95 if (WHITE_BOX.getBooleanVMFlag("TieredCompilation")) { 96 throw new Error("TESTBUG: Tiered compilation enabled"); 97 } 98 99 Method methodClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCast", String.class); 100 Method methodMHCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testMHCast", String.class); 101 Method methodMHSetter = CastNullCheckDroppingsTest.class.getDeclaredMethod("testMHSetter", String.class); 102 Method methodFunction = CastNullCheckDroppingsTest.class.getDeclaredMethod("testFunction", String.class); 103 104 CastNullCheckDroppingsTest t = new CastNullCheckDroppingsTest(); 105 t.runTest(methodClassCast, false); 106 t.runTest(methodMHCast, false); 107 t.runTest(methodMHSetter, false); 108 t.runTest(methodFunction, false); 109 110 // Edge cases 111 Method methodClassCastNull = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastNull", String.class); 112 Method methodNullClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testNullClassCast", String.class); 113 Method methodClassCastObj = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastObj", Object.class); 114 Method methodObjClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testObjClassCast", String.class); 115 Method methodVarClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testVarClassCast", String.class); 116 Method methodClassCastInt = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastInt", Object.class); 117 Method methodIntClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testIntClassCast", Object.class); 118 Method methodClassCastint = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastint", Object.class); 119 Method methodintClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testintClassCast", Object.class); 120 Method methodClassCastPrim = CastNullCheckDroppingsTest.class.getDeclaredMethod("testClassCastPrim", Object.class); 121 Method methodPrimClassCast = CastNullCheckDroppingsTest.class.getDeclaredMethod("testPrimClassCast", Object.class); 122 123 t.runTest(methodClassCastNull, false); 124 t.runTest(methodNullClassCast, false); 125 t.runTest(methodClassCastObj, false); 126 t.runTest(methodObjClassCast, true); 127 t.runTest(methodVarClassCast, true); 128 t.runTest(methodClassCastInt, false); 129 t.runTest(methodIntClassCast, true); 130 t.runTest(methodClassCastint, false); 131 t.runTest(methodintClassCast, false); 132 t.runTest(methodClassCastPrim, false); 133 t.runTest(methodPrimClassCast, true); 134 } 135 136 void testClassCast(String s) { 137 try { 138 ssink = String.class.cast(s); 139 } catch (Throwable t) { 140 throw new Error(t); 141 } 142 } 143 144 void testClassCastNull(String s) { 145 try { 146 ssink = String.class.cast(null); 147 } catch (Throwable t) { 148 throw new Error(t); 149 } 150 } 151 152 void testNullClassCast(String s) { 153 try { 154 ssink = (String)nullClass.cast(s); 155 throw new AssertionError("NullPointerException is not thrown"); 156 } catch (NullPointerException t) { 157 // Ignore NullPointerException 158 } catch (Throwable t) { 159 throw new Error(t); 160 } 161 } 162 163 void testClassCastObj(Object s) { 164 try { 165 ssink = String.class.cast(s); 166 } catch (Throwable t) { 167 throw new Error(t); 168 } 169 } 170 171 void testObjClassCast(String s) { 172 try { 173 ssink = (String)objClass.cast(s); 174 } catch (Throwable t) { 175 throw new Error(t); 176 } 177 } 178 179 void testVarClassCast(String s) { 180 Class cl = (s == null) ? null : String.class; 181 try { 182 ssink = (String)cl.cast(svalue); 183 if (s == null) { 184 throw new AssertionError("NullPointerException is not thrown"); 185 } 186 } catch (NullPointerException t) { 187 // Ignore NullPointerException 188 } catch (Throwable t) { 189 throw new Error(t); 190 } 191 } 192 193 void testClassCastInt(Object s) { 194 try { 195 ssink = String.class.cast(iobj); 196 throw new AssertionError("ClassCastException is not thrown"); 197 } catch (ClassCastException t) { 198 // Ignore ClassCastException: Cannot cast java.lang.Integer to java.lang.String 199 } catch (Throwable t) { 200 throw new Error(t); 201 } 202 } 203 204 void testIntClassCast(Object s) { 205 try { 206 isink = Integer.class.cast(s); 207 if (s != null) { 208 throw new AssertionError("ClassCastException is not thrown"); 209 } 210 } catch (ClassCastException t) { 211 // Ignore ClassCastException: Cannot cast java.lang.String to java.lang.Integer 212 } catch (Throwable t) { 213 throw new Error(t); 214 } 215 } 216 217 void testClassCastint(Object s) { 218 try { 219 ssink = String.class.cast(45); 220 throw new AssertionError("ClassCastException is not thrown"); 221 } catch (ClassCastException t) { 222 // Ignore ClassCastException: Cannot cast java.lang.Integer to java.lang.String 223 } catch (Throwable t) { 224 throw new Error(t); 225 } 226 } 227 228 void testintClassCast(Object s) { 229 try { 230 isink = int.class.cast(s); 231 if (s != null) { 232 throw new AssertionError("ClassCastException is not thrown"); 233 } 234 } catch (ClassCastException t) { 235 // Ignore ClassCastException: Cannot cast java.lang.String to java.lang.Integer 236 } catch (Throwable t) { 237 throw new Error(t); 238 } 239 } 240 241 void testClassCastPrim(Object s) { 242 try { 243 ssink = String.class.cast(arr); 244 throw new AssertionError("ClassCastException is not thrown"); 245 } catch (ClassCastException t) { 246 // Ignore ClassCastException: Cannot cast [I to java.lang.String 247 } catch (Throwable t) { 248 throw new Error(t); 249 } 250 } 251 252 void testPrimClassCast(Object s) { 253 try { 254 asink = int[].class.cast(s); 255 if (s != null) { 256 throw new AssertionError("ClassCastException is not thrown"); 257 } 258 } catch (ClassCastException t) { 259 // Ignore ClassCastException: Cannot cast java.lang.String to [I 260 } catch (Throwable t) { 261 throw new Error(t); 262 } 263 } 264 265 void testMHCast(String s) { 266 try { 267 ssink = (String) (Object) MH_CAST.invokeExact(String.class, (Object) s); 268 } catch (Throwable t) { 269 throw new Error(t); 270 } 271 } 272 273 void testMHSetter(String s) { 274 try { 275 SET_SSINK.invokeExact(this, s); 276 } catch (Throwable t) { 277 throw new Error(t); 278 } 279 } 280 281 void testFunction(String s) { 282 try { 283 ssink = (String) fCast.apply(String.class, s); 284 } catch (Throwable t) { 285 throw new Error(t); 286 } 287 } 288 289 void runTest(Method method, boolean deopt) { 290 if (method == null) { 291 throw new AssertionError("method was not found"); 292 } 293 // Ensure method is compiled 294 WHITE_BOX.testSetDontInlineMethod(method, true); 295 for (int i = 0; i < 3000; i++) { 296 try { 297 method.invoke(this, svalue); 298 } catch (Exception e) { 299 throw new Error("Unexpected exception: ", e); 300 } 301 } 302 NMethod nm = getNMethod(method); 303 304 // Passing null should cause a de-optimization 305 // if method is compiled with a null-check. 306 try { 307 method.invoke(this, snull); 308 } catch (Exception e) { 309 throw new Error("Unexpected exception: ", e); 310 } 311 checkDeoptimization(method, nm, deopt); 312 } 313 314 static NMethod getNMethod(Method test) { 315 // Because background compilation is disabled, method should now be compiled 316 if (!WHITE_BOX.isMethodCompiled(test)) { 317 throw new AssertionError(test + " not compiled"); 318 } 319 320 NMethod nm = NMethod.get(test, false); // not OSR nmethod 321 if (nm == null) { 322 throw new AssertionError(test + " missing nmethod?"); 323 } 324 if (nm.comp_level != 4) { 325 throw new AssertionError(test + " compiled by not C2: " + nm); 326 } 327 return nm; 328 } 329 330 static void checkDeoptimization(Method method, NMethod nmOrig, boolean deopt) { 331 // Check deoptimization event (intrinsic Class.cast() works). 332 if (WHITE_BOX.isMethodCompiled(method) == deopt) { 333 throw new AssertionError(method + " was" + (deopt ? " not" : "") + " deoptimized"); 334 } 335 if (deopt) { 336 return; 337 } 338 // Ensure no recompilation when no deoptimization is expected. 339 NMethod nm = NMethod.get(method, false); // not OSR nmethod 340 if (nm == null) { 341 throw new AssertionError(method + " missing nmethod?"); 342 } 343 if (nm.comp_level != 4) { 344 throw new AssertionError(method + " compiled by not C2: " + nm); 345 } 346 if (nm.compile_id != nmOrig.compile_id) { 347 throw new AssertionError(method + " was recompiled: old nmethod=" + nmOrig + ", new nmethod=" + nm); 348 } 349 } 350} 351