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