8198908: Add JVM support for preview features
authorhseigel
Fri, 06 Apr 2018 09:59:48 -0400
changeset 49716 450d709262c1
parent 49715 947560700a09
child 49717 cd4da74e310b
8198908: Add JVM support for preview features Summary: Add new major and minor version checks Reviewed-by: dholmes, lfoltan
src/hotspot/share/classfile/classFileParser.cpp
src/hotspot/share/logging/logTag.hpp
src/hotspot/share/runtime/arguments.cpp
src/hotspot/share/runtime/arguments.hpp
test/hotspot/jtreg/runtime/ClassFile/PreviewVersion.java
--- a/src/hotspot/share/classfile/classFileParser.cpp	Fri Apr 06 13:55:25 2018 +0200
+++ b/src/hotspot/share/classfile/classFileParser.cpp	Fri Apr 06 09:59:48 2018 -0400
@@ -57,6 +57,7 @@
 #include "oops/symbol.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
+#include "runtime/arguments.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/perfData.hpp"
@@ -89,6 +90,7 @@
 
 #define JAVA_CLASSFILE_MAGIC              0xCAFEBABE
 #define JAVA_MIN_SUPPORTED_VERSION        45
+#define JAVA_PREVIEW_MINOR_VERSION        65535
 
 // Used for two backward compatibility reasons:
 // - to check for new additions to the class file format in JDK1.5
@@ -4700,12 +4702,63 @@
           (is_protected && is_private));
 }
 
-static bool is_supported_version(u2 major, u2 minor){
+// A legal major_version.minor_version must be one of the following:
+//
+//   Major_version = 45, any minor_version.
+//   Major_version >= 46 and major_version <= current_major_version and minor_version = 0.
+//   Major_version = current_major_version and minor_version = 65535 and --enable-preview is present.
+//
+static void verify_class_version(u2 major, u2 minor, Symbol* class_name, TRAPS){
   const u2 max_version = JVM_CLASSFILE_MAJOR_VERSION;
-  return (major >= JAVA_MIN_SUPPORTED_VERSION) &&
-         (major <= max_version) &&
-         ((major != max_version) ||
-          (minor <= JVM_CLASSFILE_MINOR_VERSION));
+  if (major != JAVA_MIN_SUPPORTED_VERSION) { // All 45.* are ok including 45.65535
+    if (minor == JAVA_PREVIEW_MINOR_VERSION) {
+      if (major != max_version) {
+        ResourceMark rm(THREAD);
+        Exceptions::fthrow(
+          THREAD_AND_LOCATION,
+          vmSymbols::java_lang_UnsupportedClassVersionError(),
+          "%s (class file version %u.%u) was compiled with preview features that are unsupported. "
+          "This version of the Java Runtime only recognizes preview features for class file version %u.%u",
+          class_name->as_C_string(), major, minor, JVM_CLASSFILE_MAJOR_VERSION, JAVA_PREVIEW_MINOR_VERSION);
+        return;
+      }
+
+      if (!Arguments::enable_preview()) {
+        ResourceMark rm(THREAD);
+        Exceptions::fthrow(
+          THREAD_AND_LOCATION,
+          vmSymbols::java_lang_UnsupportedClassVersionError(),
+          "Preview features are not enabled for %s (class file version %u.%u). Try running with '--enable-preview'",
+          class_name->as_C_string(), major, minor);
+        return;
+      }
+
+    } else { // minor != JAVA_PREVIEW_MINOR_VERSION
+      if (major > max_version) {
+        ResourceMark rm(THREAD);
+        Exceptions::fthrow(
+          THREAD_AND_LOCATION,
+          vmSymbols::java_lang_UnsupportedClassVersionError(),
+          "%s has been compiled by a more recent version of the Java Runtime (class file version %u.%u), "
+          "this version of the Java Runtime only recognizes class file versions up to %u.0",
+          class_name->as_C_string(), major, minor, JVM_CLASSFILE_MAJOR_VERSION);
+      } else if (major < JAVA_MIN_SUPPORTED_VERSION) {
+        ResourceMark rm(THREAD);
+        Exceptions::fthrow(
+          THREAD_AND_LOCATION,
+          vmSymbols::java_lang_UnsupportedClassVersionError(),
+          "%s (class file version %u.%u) was compiled with an invalid major version",
+          class_name->as_C_string(), major, minor);
+      } else if (minor != 0) {
+        ResourceMark rm(THREAD);
+        Exceptions::fthrow(
+          THREAD_AND_LOCATION,
+          vmSymbols::java_lang_UnsupportedClassVersionError(),
+          "%s (class file version %u.%u) was compiled with an invalid non-zero minor version",
+          class_name->as_C_string(), major, minor);
+      }
+    }
+  }
 }
 
 void ClassFileParser::verify_legal_field_modifiers(jint flags,
@@ -5551,6 +5604,13 @@
       ik->print_class_load_logging(_loader_data, module_name, _stream);
     }
 
+    if (ik->minor_version() == JAVA_PREVIEW_MINOR_VERSION &&
+        ik->major_version() != JAVA_MIN_SUPPORTED_VERSION &&
+        log_is_enabled(Info, class, preview)) {
+      ResourceMark rm;
+      log_info(class, preview)("Loading preview feature type %s", ik->external_name());
+    }
+
     if (log_is_enabled(Debug, class, resolve))  {
       ResourceMark rm;
       // print out the superclass.
@@ -5866,20 +5926,7 @@
   }
 
   // Check version numbers - we check this even with verifier off
-  if (!is_supported_version(_major_version, _minor_version)) {
-    ResourceMark rm(THREAD);
-    Exceptions::fthrow(
-      THREAD_AND_LOCATION,
-      vmSymbols::java_lang_UnsupportedClassVersionError(),
-      "%s has been compiled by a more recent version of the Java Runtime (class file version %u.%u), "
-      "this version of the Java Runtime only recognizes class file versions up to %u.%u",
-      _class_name->as_C_string(),
-      _major_version,
-      _minor_version,
-      JVM_CLASSFILE_MAJOR_VERSION,
-      JVM_CLASSFILE_MINOR_VERSION);
-    return;
-  }
+  verify_class_version(_major_version, _minor_version, _class_name, CHECK);
 
   stream->guarantee_more(3, CHECK); // length, first cp tag
   u2 cp_size = stream->get_u2_fast();
--- a/src/hotspot/share/logging/logTag.hpp	Fri Apr 06 13:55:25 2018 +0200
+++ b/src/hotspot/share/logging/logTag.hpp	Fri Apr 06 09:59:48 2018 -0400
@@ -112,6 +112,7 @@
   LOG_TAG(perf) \
   LOG_TAG(phases) \
   LOG_TAG(plab) \
+  LOG_TAG(preview)   /* Trace loading of preview feature types */ \
   LOG_TAG(promotion) \
   LOG_TAG(preorder) /* Trace all classes loaded in order referenced (not loaded) */ \
   LOG_TAG(protectiondomain) /* "Trace protection domain verification" */ \
--- a/src/hotspot/share/runtime/arguments.cpp	Fri Apr 06 13:55:25 2018 +0200
+++ b/src/hotspot/share/runtime/arguments.cpp	Fri Apr 06 09:59:48 2018 -0400
@@ -96,6 +96,8 @@
 intx   Arguments::_Tier3InvokeNotifyFreqLog     = Tier3InvokeNotifyFreqLog;
 intx   Arguments::_Tier4InvocationThreshold     = Tier4InvocationThreshold;
 
+bool   Arguments::_enable_preview               = false;
+
 char*  Arguments::SharedArchivePath             = NULL;
 
 AgentLibraryList Arguments::_libraryList;
@@ -2739,6 +2741,9 @@
         }
       }
 #endif // !INCLUDE_JVMTI
+    // --enable_preview
+    } else if (match_option(option, "--enable-preview")) {
+      set_enable_preview();
     // -Xnoclassgc
     } else if (match_option(option, "-Xnoclassgc")) {
       if (FLAG_SET_CMDLINE(bool, ClassUnloading, false) != Flag::SUCCESS) {
--- a/src/hotspot/share/runtime/arguments.hpp	Fri Apr 06 13:55:25 2018 +0200
+++ b/src/hotspot/share/runtime/arguments.hpp	Fri Apr 06 09:59:48 2018 -0400
@@ -358,6 +358,9 @@
   static void set_xdebug_mode(bool arg) { _xdebug_mode = arg; }
   static bool xdebug_mode()             { return _xdebug_mode; }
 
+  // preview features
+  static bool _enable_preview;
+
   // Used to save default settings
   static bool _AlwaysCompileLoopMethods;
   static bool _UseOnStackReplacement;
@@ -691,6 +694,9 @@
   static Mode mode()                        { return _mode; }
   static bool is_interpreter_only() { return mode() == _int; }
 
+  // preview features
+  static void set_enable_preview() { _enable_preview = true; }
+  static bool enable_preview() { return _enable_preview; }
 
   // Utility: copies src into buf, replacing "%%" with "%" and "%p" with pid.
   static bool copy_expand_pid(const char* src, size_t srclen, char* buf, size_t buflen);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/ClassFile/PreviewVersion.java	Fri Apr 06 09:59:48 2018 -0400
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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 8198908
+ * @summary Check that preview minor version and --enable-preview are handled
+ *          correctly.
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @run main PreviewVersion
+ */
+
+import java.io.File;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.ByteCodeLoader;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class PreviewVersion {
+
+    public static void main(String args[]) throws Throwable {
+        System.out.println("Regression test for bug 8198908");
+
+        byte klassbuf[] = InMemoryJavaCompiler.compile("PVTest",
+            "public class PVTest { " +
+                "public static void main(String argv[]) { " +
+                    "System.out.println(\"Hi!\"); } }");
+
+        // Set class's minor version to 65535.
+        klassbuf[4] = -1;
+        klassbuf[5] = -1;
+
+        // Run the test. This should fail because --enable-preview is not specified.
+        ClassFileInstaller.writeClassToDisk("PVTest", klassbuf, System.getProperty("test.classes"));
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+            "-cp", "." + File.pathSeparator + System.getProperty("test.classes"), "PVTest");
+        OutputAnalyzer oa = new OutputAnalyzer(pb.start());
+        oa.shouldContain("Preview features are not enabled");
+        oa.shouldHaveExitValue(1);
+
+        // This should be successful because --enable-preview is specified.
+        pb = ProcessTools.createJavaProcessBuilder("--enable-preview",
+            "-cp", "." + File.pathSeparator + System.getProperty("test.classes"), "PVTest");
+        oa = new OutputAnalyzer(pb.start());
+        oa.shouldContain("Hi!");
+
+        // Test -Xlog:class+preview
+        pb = ProcessTools.createJavaProcessBuilder("--enable-preview", "-Xlog:class+preview",
+            "-cp", "." + File.pathSeparator + System.getProperty("test.classes"), "PVTest");
+        oa = new OutputAnalyzer(pb.start());
+        oa.shouldContain("[info][class,preview] Loading preview feature type PVTest");
+
+        // Subtract 1 from class's major version.  The class should fail to load
+        // because its major_version does not match the JVM current version.
+        int prev_major_version = Runtime.version().feature() - 1;
+        klassbuf[6] = (byte)((prev_major_version >> 8) & 0xff);
+        klassbuf[7] = (byte)(prev_major_version & 0xff);
+        try {
+            ByteCodeLoader.load("PVTest", klassbuf);
+            throw new RuntimeException("UnsupportedClassVersionError exception not thrown");
+        } catch (java.lang.UnsupportedClassVersionError e) {
+            if (!e.getMessage().contains("compiled with preview features that are unsupported")) {
+                throw new RuntimeException(
+                    "Wrong UnsupportedClassVersionError exception: " + e.getMessage());
+            }
+        }
+
+        // Set class's major version to 45.  The class should load because class
+        // version 45.65535 is valid.
+        klassbuf[6] = 0;
+        klassbuf[7] = 45;
+        try {
+            ByteCodeLoader.load("PVTest", klassbuf);
+        } catch (java.lang.UnsupportedClassVersionError e) {
+            throw new RuntimeException(
+                "Unexpected UnsupportedClassVersionError exception thrown: " + e.getMessage());
+        }
+
+        // Check that a class with a recent older major version and a non-zero
+        // minor version fails to load.
+        klassbuf[6] = 0;
+        klassbuf[7] = 53;
+        klassbuf[4] = 0;
+        klassbuf[5] = 2;
+        try {
+            ByteCodeLoader.load("PVTest", klassbuf);
+            throw new RuntimeException("UnsupportedClassVersionError exception not thrown");
+        } catch (java.lang.UnsupportedClassVersionError e) {
+            if (!e.getMessage().contains("was compiled with an invalid non-zero minor version")) {
+                throw new RuntimeException(
+                    "Wrong UnsupportedClassVersionError exception: " + e.getMessage());
+            }
+        }
+    }
+}