8073432: Object.getClass() throws stackless NPE, due to C2 intrinsic
Summary: Javac should generate NPE checks using Objects.requireNonNull if -target >= 7
Reviewed-by: jlahoda
Contributed-by: aleksey.shipilev@oracle.com
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Thu Mar 05 13:10:49 2015 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Thu Mar 05 15:03:07 2015 +0000
@@ -147,6 +147,7 @@
/** Predefined types.
*/
public final Type objectType;
+ public final Type objectsType;
public final Type classType;
public final Type classLoaderType;
public final Type stringType;
@@ -408,6 +409,7 @@
// Enter predefined classes.
objectType = enterClass("java.lang.Object");
+ objectsType = enterClass("java.util.Objects");
classType = enterClass("java.lang.Class");
stringType = enterClass("java.lang.String");
stringBufferType = enterClass("java.lang.StringBuffer");
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Thu Mar 05 13:10:49 2015 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Thu Mar 05 15:03:07 2015 +0000
@@ -124,6 +124,7 @@
genCrt = options.isSet(XJCOV);
debugCode = options.isSet("debugcode");
allowInvokedynamic = target.hasInvokedynamic() || options.isSet("invokedynamic");
+ allowBetterNullChecks = target.hasObjects();
pool = new Pool(types);
// ignore cldc because we cannot have both stackmap formats
@@ -150,6 +151,7 @@
private final boolean genCrt;
private final boolean debugCode;
private final boolean allowInvokedynamic;
+ private final boolean allowBetterNullChecks;
/** Default limit of (approximate) size of finalizer to inline.
* Zero means always use jsr. 100 or greater means never use
@@ -1983,8 +1985,13 @@
/** Generate a null check from the object value at stack top. */
private void genNullCheck(DiagnosticPosition pos) {
- callMethod(pos, syms.objectType, names.getClass,
- List.<Type>nil(), false);
+ if (allowBetterNullChecks) {
+ callMethod(pos, syms.objectsType, names.requireNonNull,
+ List.of(syms.objectType), true);
+ } else {
+ callMethod(pos, syms.objectType, names.getClass,
+ List.<Type>nil(), false);
+ }
code.emitop0(pop);
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java Thu Mar 05 13:10:49 2015 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java Thu Mar 05 15:03:07 2015 +0000
@@ -121,6 +121,12 @@
return compareTo(JDK1_7) >= 0;
}
+ /** Does the target JDK contains the java.util.Objects class?
+ */
+ public boolean hasObjects() {
+ return compareTo(JDK1_7) >= 0;
+ }
+
/** Does the VM support polymorphic method handle invocation?
* Affects the linkage information output to the classfile.
* An alias for {@code hasInvokedynamic}, since all the JSR 292 features appear together.
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java Thu Mar 05 13:10:49 2015 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java Thu Mar 05 15:03:07 2015 +0000
@@ -171,6 +171,7 @@
public final Name deprecated;
public final Name ex;
public final Name package_info;
+ public final Name requireNonNull;
//lambda-related
public final Name lambda;
@@ -307,6 +308,7 @@
deprecated = fromString("deprecated");
ex = fromString("ex");
package_info = fromString("package-info");
+ requireNonNull = fromString("requireNonNull");
//lambda-related
lambda = fromString("lambda$");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/8074306/TestSyntheticNullChecks.java Thu Mar 05 15:03:07 2015 +0000
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8074306
+ * @summary NULLCHK is emitted as Object.getClass
+ * @compile -source 6 -target 6 TestSyntheticNullChecks.java
+ * @run main TestSyntheticNullChecks 6
+ * @clean TestSyntheticNullChecks*
+ * @compile -source 7 -target 7 TestSyntheticNullChecks.java
+ * @run main TestSyntheticNullChecks 7
+ * @clean TestSyntheticNullChecks*
+ * @compile TestSyntheticNullChecks.java
+ * @run main TestSyntheticNullChecks 9
+ */
+public class TestSyntheticNullChecks {
+
+ class Inner { }
+
+ static void generateSyntheticNPE(TestSyntheticNullChecks outer) {
+ outer.new Inner(); //javac will generate a synthetic NPE check for 'outer'
+ }
+
+ public static void main(String[] args) {
+ int version = Integer.valueOf(args[0]);
+ boolean useObjects = version >= 7;
+ try {
+ generateSyntheticNPE(null);
+ } catch (NullPointerException npe) {
+ boolean hasRequireNotNull = false;
+ for (StackTraceElement e : npe.getStackTrace()) {
+ if (e.getClassName().equals("java.util.Objects") &&
+ e.getMethodName().equals("requireNonNull")) {
+ hasRequireNotNull = true;
+ break;
+ }
+ }
+ if (hasRequireNotNull != useObjects) {
+ throw new AssertionError();
+ }
+ }
+ }
+}