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