diff --git a/Troubleshooting.md b/Troubleshooting.md index 538eefc..34c8e53 100644 --- a/Troubleshooting.md +++ b/Troubleshooting.md @@ -51,6 +51,26 @@ This error indicates your application attempts to reference some classes which a 3. Run VS Code command *"Java: Force Java compilation"* to force the language server to rebuild the current project. 4. If the problem persists, it's probably because the language server doesn't load your project correctly. Please reference the [language server troubleshooting](#try) paragraph for more troubleshooting info. +## Program throws UnsupportedClassVersionError +Below is a typical error message. + +![image](https://user-images.githubusercontent.com/14052197/78854443-ed47c780-7a53-11ea-8317-d8b097dfba99.png) + +### Reason: +The compiled classes are not compatible with the runtime JDK. + +The class file version `57.65535` stands for Java 13 preview, where the major version `57` stands for Java 13, the minor version `65535` stands for preview feature. Similarly `58.65535` stands for Java 14 preview. + +The error says the compiled class is `57.65535`, but the runtime JDK only recognizes class file versoin `58.65535`. That's because the preview feature is not backward compatible, i.e. JVM 14 doesn't support 13 preview feature. The [openjdk](https://openjdk.java.net/jeps/12) website has claimed the reason that it would be costly for JDK 14 to support preview features from JDK 13 which were changed or dropped in response to feedback. + +One possible root cause for this error is your runtime JDK is the latest JDK but the upstream [Language Support for Java](https://marketplace.visualstudio.com/items?itemName=redhat.java) extension doesn't catch up the support yet. + +### Try: +1. Try to update [Language Support for Java](https://marketplace.visualstudio.com/items?itemName=redhat.java) to the latest, and then try step 3 to rebuild the workspace. +2. If it doesn't work, then try to install an older JDK version, set its installation folder to "java.home" user setting in _.vscode/settings.json_ and reopen your VS Code workspace. +3. Click **F1** -> **Java: Force Java compilation** -> **Full** to rebuild the workspace. +4. If it still doesn't work, then try **F1** -> **Java: Clean the Java language server workspace** to clean the cache. + ## Failed to complete hot code replace: ### Reason: This error indicates you are doing `Hot Code Replace`. The `Hot Code Replace` feature depends on the underlying JVM implementation. If you get this error, that indicates the new changes cannot be hot replaced by JVM. diff --git a/src/anchor.ts b/src/anchor.ts index 807567e..22e2d19 100644 --- a/src/anchor.ts +++ b/src/anchor.ts @@ -7,3 +7,4 @@ export const ATTACH_CONFIG_ERROR = "please-specify-the-host-name-and-the-port-of export const EVALUATE_ON_RUNNING_THREAD = "failed-to-evaluate-reason-cannot-evaluate-because-the-thread-is-resumed"; export const CANNOT_FIND_MAIN_CLASS = "cannot-find-a-class-with-the-main-method"; export const BUILD_FAILED = "build-failed-do-you-want-to-continue"; +export const UNSUPPORTED_CLASS_VERSION_ERROR = "program-throws-unsupportedclassversionerror"; diff --git a/src/commands.ts b/src/commands.ts index 7ffa3d4..5710a0d 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -38,6 +38,8 @@ export const JAVA_IS_ON_CLASSPATH = "vscode.java.isOnClasspath"; export const JAVA_RESOLVE_JAVAEXECUTABLE = "vscode.java.resolveJavaExecutable"; +export const JAVA_FETCH_PLATFORM_SETTINGS = "vscode.java.fetchPlatformSettings"; + export function executeJavaLanguageServerCommand(...rest) { return executeJavaExtensionCommand(JAVA_EXECUTE_WORKSPACE_COMMAND, ...rest); } diff --git a/src/configurationProvider.ts b/src/configurationProvider.ts index bd6aec2..5c0e08d 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -11,7 +11,7 @@ import * as anchor from "./anchor"; import { buildWorkspace } from "./build"; import * as commands from "./commands"; import * as lsPlugin from "./languageServerPlugin"; -import { detectLaunchCommandStyle } from "./launchCommand"; +import { detectLaunchCommandStyle, validateRuntime } from "./launchCommand"; import { logger, Type } from "./logger"; import * as utility from "./utility"; import { VariableResolver } from "./variableResolver"; @@ -207,6 +207,27 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration config.javaExec = await lsPlugin.resolveJavaExecutable(config.mainClass, config.projectName); // Add the default launch options to the config. config.cwd = config.cwd || _.get(folder, "uri.fsPath"); + if (Array.isArray(config.args)) { + config.args = this.concatArgs(config.args); + } + + if (Array.isArray(config.vmArgs)) { + config.vmArgs = this.concatArgs(config.vmArgs); + } + + // Auto add '--enable-preview' vmArgs if the java project enables COMPILER_PB_ENABLE_PREVIEW_FEATURES flag. + if (await lsPlugin.detectPreviewFlag(config.mainClass, config.projectName)) { + config.vmArgs = (config.vmArgs || "") + " --enable-preview"; + validateRuntime(config); + } + + if (!config.shortenCommandLine || config.shortenCommandLine === "auto") { + config.shortenCommandLine = await detectLaunchCommandStyle(config); + } + + if (process.platform === "win32" && config.console !== "internalConsole") { + config.launcherScript = utility.getLauncherScriptPath(); + } } else if (config.request === "attach") { if (!config.hostName || !config.port) { throw new utility.UserError({ @@ -223,27 +244,6 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration }); } - if (Array.isArray(config.args)) { - config.args = this.concatArgs(config.args); - } - - if (Array.isArray(config.vmArgs)) { - config.vmArgs = this.concatArgs(config.vmArgs); - } - - // Auto add '--enable-preview' vmArgs if the java project enables COMPILER_PB_ENABLE_PREVIEW_FEATURES flag. - if (await lsPlugin.detectPreviewFlag(config.mainClass, config.projectName)) { - config.vmArgs = (config.vmArgs || "") + " --enable-preview"; - } - - if (config.request === "launch" && (!config.shortenCommandLine || config.shortenCommandLine === "auto")) { - config.shortenCommandLine = await detectLaunchCommandStyle(config); - } - - if (process.platform === "win32" && config.request === "launch" && config.console !== "internalConsole") { - config.launcherScript = utility.getLauncherScriptPath(); - } - const debugServerPort = await lsPlugin.startDebugSession(); if (debugServerPort) { config.debugServer = debugServerPort; diff --git a/src/languageServerPlugin.ts b/src/languageServerPlugin.ts index bc7021b..1ba21f1 100644 --- a/src/languageServerPlugin.ts +++ b/src/languageServerPlugin.ts @@ -100,3 +100,7 @@ export async function isOnClasspath(uri: string): Promise { export function resolveJavaExecutable(mainClass, projectName) { return commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_JAVAEXECUTABLE, mainClass, projectName); } + +export function fetchPlatformSettings(): any { + return commands.executeJavaLanguageServerCommand(commands.JAVA_FETCH_PLATFORM_SETTINGS); +} diff --git a/src/launchCommand.ts b/src/launchCommand.ts index 15971a6..ac129d3 100644 --- a/src/launchCommand.ts +++ b/src/launchCommand.ts @@ -5,8 +5,9 @@ import * as _ from "lodash"; import * as path from "path"; import * as vscode from "vscode"; -import { inferLaunchCommandLength } from "./languageServerPlugin"; -import { getJavaHome } from "./utility"; +import { UNSUPPORTED_CLASS_VERSION_ERROR } from "./anchor"; +import { fetchPlatformSettings, inferLaunchCommandLength } from "./languageServerPlugin"; +import { getJavaHome, showWarningMessageWithTroubleshooting } from "./utility"; enum shortenApproach { none = "none", @@ -21,6 +22,24 @@ export async function detectLaunchCommandStyle(config: vscode.DebugConfiguration return (await shouldShortenIfNecessary(config)) ? recommendedShortenApproach : shortenApproach.none; } +export async function validateRuntime(config: vscode.DebugConfiguration) { + try { + const platformSettings = await fetchPlatformSettings(); + if (platformSettings && platformSettings.latestSupportedJavaVersion) { + const latestSupportedVersion = flattenMajorVersion(platformSettings.latestSupportedJavaVersion); + const runtimeVersion = await checkJavaVersion(config.javaExec || path.join(await getJavaHome(), "bin", "java")); + if (latestSupportedVersion < runtimeVersion) { + showWarningMessageWithTroubleshooting({ + message: "The compiled classes are not compatible with the runtime JDK. To mitigate the issue, please refer to \"Learn More\".", + anchor: UNSUPPORTED_CLASS_VERSION_ERROR, + }); + } + } + } catch (err) { + // do nothing + } +} + function checkJavaVersion(javaExec: string): Promise { return new Promise((resolve, reject) => { cp.execFile(javaExec, ["-version"], {}, (error, stdout, stderr) => { @@ -31,24 +50,29 @@ function checkJavaVersion(javaExec: string): Promise { } function parseMajorVersion(content: string): number { - let regexp = /version "(.*)"/g; - let match = regexp.exec(content); + const regexp = /version "(.*)"/g; + const match = regexp.exec(content); if (!match) { return 0; } - let version = match[1]; + + return flattenMajorVersion(match[1]); +} + +function flattenMajorVersion(version: string): number { // Ignore '1.' prefix for legacy Java versions if (version.startsWith("1.")) { version = version.substring(2); } // look into the interesting bits now - regexp = /\d+/g; - match = regexp.exec(version); + const regexp = /\d+/g; + const match = regexp.exec(version); let javaVersion = 0; if (match) { javaVersion = parseInt(match[0], 10); } + return javaVersion; }