--- old/src/hotspot/share/classfile/classFileParser.cpp 2018-04-05 09:15:12.549602606 -0400 +++ new/src/hotspot/share/classfile/classFileParser.cpp 2018-04-05 09:15:11.930297581 -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,57 @@ (is_protected && is_private)); } -static bool is_supported_version(u2 major, u2 minor){ +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, @@ -5549,6 +5596,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. @@ -5864,20 +5918,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(); --- old/src/hotspot/share/logging/logTag.hpp 2018-04-05 09:15:14.860609041 -0400 +++ new/src/hotspot/share/logging/logTag.hpp 2018-04-05 09:15:14.238545675 -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" */ \ --- old/src/hotspot/share/runtime/arguments.cpp 2018-04-05 09:15:16.982234887 -0400 +++ new/src/hotspot/share/runtime/arguments.cpp 2018-04-05 09:15:16.377957014 -0400 @@ -98,6 +98,8 @@ intx Arguments::_Tier3InvokeNotifyFreqLog = Tier3InvokeNotifyFreqLog; intx Arguments::_Tier4InvocationThreshold = Tier4InvocationThreshold; +bool Arguments::_enable_preview = false; + char* Arguments::SharedArchivePath = NULL; AgentLibraryList Arguments::_libraryList; @@ -2776,6 +2778,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) { --- old/src/hotspot/share/runtime/arguments.hpp 2018-04-05 09:15:18.893294638 -0400 +++ new/src/hotspot/share/runtime/arguments.hpp 2018-04-05 09:15:18.261554732 -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 2018-03-24 19:23:43.035000297 -0400 +++ new/test/hotspot/jtreg/runtime/ClassFile/PreviewVersion.java 2018-04-05 09:15:19.701112277 -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()); + } + } + } +}