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