diff --git a/CHANGES.md b/CHANGES.md index 0a3351fe91..df4135c4bd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added * Support for `editorConfigOverride` in `ktlint`. ([#1218](https://github.com/diffplug/spotless/pull/1218) fixes [#1193](https://github.com/diffplug/spotless/issues/1193)) +### Fixed +* `google-java-format` and `RemoveUnusedImportsStep` works on JDK16+ without jvm args workaround. ([#1224](https://github.com/diffplug/spotless/pull/1224) fixes [#834](https://github.com/diffplug/spotless/issues/834)) ## [2.25.3] - 2022-05-10 ### Fixed diff --git a/lib/src/main/java/com/diffplug/spotless/Jvm.java b/lib/src/main/java/com/diffplug/spotless/Jvm.java index e22e0c6339..14686c1e4c 100644 --- a/lib/src/main/java/com/diffplug/spotless/Jvm.java +++ b/lib/src/main/java/com/diffplug/spotless/Jvm.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java index 6aec20c891..6390c619a9 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java @@ -136,6 +136,7 @@ static final class State implements Serializable { State(String stepName, String groupArtifact, String version, String style, Provisioner provisioner, boolean reflowLongStrings) throws Exception { JVM_SUPPORT.assertFormatterSupported(version); + ModuleHelper.doOpenInternalPackagesIfRequired(); this.jarState = JarState.from(groupArtifact + ":" + version, provisioner); this.stepName = stepName; this.version = version; diff --git a/lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java b/lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java new file mode 100644 index 0000000000..68a2a181d0 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java @@ -0,0 +1,138 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.diffplug.spotless.java; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.diffplug.spotless.Jvm; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import sun.misc.Unsafe; + +final class ModuleHelper { + // prevent direct instantiation + private ModuleHelper() {} + + private static final Map REQUIRED_PACKAGES_TO_TEST_CLASSES = new HashMap<>(); + + static { + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.util", "Context"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.file", "CacheFSInfo"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.tree", "TreeTranslator"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.parser", "Tokens$TokenKind"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.api", "DiagnosticFormatter$PositionKind"); + } + + private static boolean checkDone = false; + + public static synchronized void doOpenInternalPackagesIfRequired() { + if (Jvm.version() < 16 || checkDone) { + return; + } + try { + checkDone = true; + final List unavailableRequiredPackages = unavailableRequiredPackages(); + if (!unavailableRequiredPackages.isEmpty()) { + openPackages(unavailableRequiredPackages); + final List failedToOpen = unavailableRequiredPackages(); + if (!failedToOpen.isEmpty()) { + final StringBuilder message = new StringBuilder(); + message.append("WARNING: Some required internal classes are unavailable. Please consider adding the following JVM arguments\n"); + message.append("WARNING: "); + for (String name : failedToOpen) { + message.append(String.format("--add-opens jdk.compiler/%s=ALL-UNNAMED", name)); + } + System.err.println(message); + } + } + } catch (Throwable e) { + System.err.println("WARNING: Failed to check for unavailable JDK packages. Reason: " + e.getMessage()); + } + } + + @SuppressFBWarnings("REC_CATCH_EXCEPTION") // workaround JDK11 + private static List unavailableRequiredPackages() { + final List packages = new ArrayList<>(); + for (Map.Entry e : REQUIRED_PACKAGES_TO_TEST_CLASSES.entrySet()) { + final String key = e.getKey(); + final String value = e.getValue(); + try { + final Class clazz = Class.forName(key + "." + value); + if (clazz.isEnum()) { + clazz.getMethod("values").invoke(null); + } else { + clazz.getDeclaredConstructor().newInstance(); + } + } catch (IllegalAccessException ex) { + packages.add(key); + } catch (Exception ignore) { + // in old versions of JDK some classes could be unavailable + } + } + return packages; + } + + @SuppressWarnings("unchecked") + private static void openPackages(Collection packagesToOpen) throws Throwable { + final Collection modules = allModules(); + if (modules == null) { + return; + } + final Unsafe unsafe = Unsafe.getUnsafe(); + final Field implLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); + final MethodHandles.Lookup lookup = (MethodHandles.Lookup) unsafe.getObject( + unsafe.staticFieldBase(implLookupField), + unsafe.staticFieldOffset(implLookupField)); + final MethodHandle modifiers = lookup.findSetter(Method.class, "modifiers", Integer.TYPE); + final Method exportMethod = Class.forName("java.lang.Module").getDeclaredMethod("implAddOpens", String.class); + modifiers.invokeExact(exportMethod, Modifier.PUBLIC); + for (Object module : modules) { + final Collection packages = (Collection) module.getClass().getMethod("getPackages").invoke(module); + for (String name : packages) { + if (packagesToOpen.contains(name)) { + exportMethod.invoke(module, name); + } + } + } + } + + @Nullable + @SuppressFBWarnings("REC_CATCH_EXCEPTION") // workaround JDK11 + private static Collection allModules() { + // calling ModuleLayer.boot().modules() by reflection + try { + final Object boot = Class.forName("java.lang.ModuleLayer").getMethod("boot").invoke(null); + if (boot == null) { + return null; + } + final Object modules = boot.getClass().getMethod("modules").invoke(boot); + return (Collection) modules; + } catch (Exception ignore) { + return null; + } + } +} diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 0732a18cee..edbbf11915 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -6,6 +6,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Added * Support for `editorConfigOverride` in `ktlint`. ([#1218](https://github.com/diffplug/spotless/pull/1218) fixes [#1193](https://github.com/diffplug/spotless/issues/1193)) * If you are using properties like `indent_size`, you should pass now pass them as `editorConfigOverride` and not as `userData`. +### Fixed +* `googleJavaFormat` and `removeUnusedImports` works on JDK16+ without jvm args workaround. ([#1224](https://github.com/diffplug/spotless/pull/1224)) + * If you have a bunch of `--add-exports` calls in your `org.gradle.jvmargs` property in `gradle.properties`, you should be able to remove them. (fixes [#834](https://github.com/diffplug/spotless/issues/834#issuecomment-819118761)) ## [6.6.1] - 2022-05-13 ### Fixed diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index a0dbcc1d97..01cf26bcbd 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -3,6 +3,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Fixed +* `googleJavaFormat` and `removeUnusedImports` works on JDK16+ without jvm args workaround. ([#1224](https://github.com/diffplug/spotless/pull/1224)) + * If you have a bunch of `--add-exports` calls in `MAVEN_OPTS` or `.mvn/jvm.config`, you should be able to remove them. (fixes [#834](https://github.com/diffplug/spotless/issues/834#issuecomment-817524058)) ## [2.22.5] - 2022-05-10 ### Fixed