From aa9e37a4a35e96348ae079e923e837adcfa27a8b Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 1 Jun 2022 23:45:16 +0800 Subject: [PATCH 1/8] Open com.sun.tools.javac modules on JDK 16+ Signed-off-by: tison --- .../main/java/com/diffplug/spotless/Jvm.java | 3 + .../com/diffplug/spotless/ModuleHelper.java | 136 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 lib/src/main/java/com/diffplug/spotless/ModuleHelper.java diff --git a/lib/src/main/java/com/diffplug/spotless/Jvm.java b/lib/src/main/java/com/diffplug/spotless/Jvm.java index e22e0c6339..8d7482cb61 100644 --- a/lib/src/main/java/com/diffplug/spotless/Jvm.java +++ b/lib/src/main/java/com/diffplug/spotless/Jvm.java @@ -48,6 +48,9 @@ public final class Jvm { if (VERSION <= 8) { throw new IllegalArgumentException("Expected " + jre + " to start with an integer greater than 8"); } + if (VERSION >= 16) { + ModuleHelper.doOpenInternalPackagesIfRequired(); + } } } diff --git a/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java b/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java new file mode 100644 index 0000000000..d4895afd20 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java @@ -0,0 +1,136 @@ +/* + * 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; + +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 sun.misc.Unsafe; + +public final class 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.main", "CommandLine"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.jvm", "ClassFile"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.parser", "Tokens$TokenKind"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.code", "Source"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.processing", "PrintingProcessor"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.comp", "AttrContext"); + REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.api", "DiagnosticFormatter$PositionKind"); + } + + private static boolean checkDone = false; + + public static synchronized void doOpenInternalPackagesIfRequired() { + if (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()); + } + } + + 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 + // calling ModuleLayer.boot().modules() by reflection + private static Collection allModules() { + 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; + } + } +} From 81ce19b2841f74f9c31ce4b68e2aef42eba4a27f Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 1 Jun 2022 23:46:27 +0800 Subject: [PATCH 2/8] remove unrelated modules Signed-off-by: tison --- lib/src/main/java/com/diffplug/spotless/ModuleHelper.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java b/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java index d4895afd20..a81be46442 100644 --- a/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java +++ b/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java @@ -38,12 +38,7 @@ public final class ModuleHelper { 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.main", "CommandLine"); - REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.jvm", "ClassFile"); REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.parser", "Tokens$TokenKind"); - REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.code", "Source"); - REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.processing", "PrintingProcessor"); - REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.comp", "AttrContext"); REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.api", "DiagnosticFormatter$PositionKind"); } From 31d5791aac07f282064776b064666e1355de0a61 Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 2 Jun 2022 00:39:47 +0800 Subject: [PATCH 3/8] run spotlessApply Signed-off-by: tison --- lib/src/main/java/com/diffplug/spotless/Jvm.java | 2 +- lib/src/main/java/com/diffplug/spotless/ModuleHelper.java | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/Jvm.java b/lib/src/main/java/com/diffplug/spotless/Jvm.java index 8d7482cb61..7756ffa917 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/ModuleHelper.java b/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java index a81be46442..0ccd3c8c15 100644 --- a/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java +++ b/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.diffplug.spotless; import java.lang.invoke.MethodHandle; @@ -58,7 +57,7 @@ public static synchronized void doOpenInternalPackagesIfRequired() { 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) { + for (String name : failedToOpen) { message.append(String.format("--add-opens jdk.compiler/%s=ALL-UNNAMED", name)); } System.err.println(message); @@ -99,8 +98,8 @@ private static void openPackages(Collection packagesToOpen) throws Throw 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)); + 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); From 2033c6ab7cc570f7fedcb83af4647eac31dfee60 Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 2 Jun 2022 07:31:39 +0800 Subject: [PATCH 4/8] SuppressFBWarnings REC_CATCH_EXCEPTION Signed-off-by: tison --- lib/src/main/java/com/diffplug/spotless/ModuleHelper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java b/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java index 0ccd3c8c15..0ab4f95540 100644 --- a/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java +++ b/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java @@ -28,6 +28,7 @@ import javax.annotation.Nullable; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import sun.misc.Unsafe; public final class ModuleHelper { @@ -68,6 +69,7 @@ public static synchronized void doOpenInternalPackagesIfRequired() { } } + @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()) { @@ -114,8 +116,9 @@ private static void openPackages(Collection packagesToOpen) throws Throw } @Nullable - // calling ModuleLayer.boot().modules() by reflection + @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) { From 7474c2544849ebc3c1a15a49548122de0da776ca Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 2 Jun 2022 23:20:18 +0800 Subject: [PATCH 5/8] Update CHANGES files Signed-off-by: tison --- CHANGES.md | 2 ++ plugin-gradle/CHANGES.md | 2 ++ plugin-maven/CHANGES.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 8d2011de17..a5b391e794 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ This document is intended for Spotless developers. We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Fixed +* google-java-format works on JDK16+ without jvm args workaround. ([#1224](https://github.com/diffplug/spotless/pull/1224)) ## [2.25.3] - 2022-05-10 ### Fixed diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index e67e6546bb..ad048e0a52 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Fixed +* google-java-format works on JDK16+ without jvm args workaround. ([#1224](https://github.com/diffplug/spotless/pull/1224)) ## [6.6.1] - 2022-05-13 ### Fixed diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index a0dbcc1d97..85094381e2 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Fixed +* google-java-format works on JDK16+ without jvm args workaround. ([#1224](https://github.com/diffplug/spotless/pull/1224)) ## [2.22.5] - 2022-05-10 ### Fixed From 2631a3f276b0350c360ea92eec553e1a8780f5ea Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 2 Jun 2022 23:34:18 +0800 Subject: [PATCH 6/8] only doOpenInternalPackagesIfRequired when GoogleJavaFormatStep is relevant Signed-off-by: tison --- lib/src/main/java/com/diffplug/spotless/Jvm.java | 3 --- .../com/diffplug/spotless/java/GoogleJavaFormatStep.java | 1 + .../com/diffplug/spotless/{ => java}/ModuleHelper.java | 7 +++++-- 3 files changed, 6 insertions(+), 5 deletions(-) rename lib/src/main/java/com/diffplug/spotless/{ => java}/ModuleHelper.java (97%) diff --git a/lib/src/main/java/com/diffplug/spotless/Jvm.java b/lib/src/main/java/com/diffplug/spotless/Jvm.java index 7756ffa917..14686c1e4c 100644 --- a/lib/src/main/java/com/diffplug/spotless/Jvm.java +++ b/lib/src/main/java/com/diffplug/spotless/Jvm.java @@ -48,9 +48,6 @@ public final class Jvm { if (VERSION <= 8) { throw new IllegalArgumentException("Expected " + jre + " to start with an integer greater than 8"); } - if (VERSION >= 16) { - ModuleHelper.doOpenInternalPackagesIfRequired(); - } } } 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/ModuleHelper.java b/lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java similarity index 97% rename from lib/src/main/java/com/diffplug/spotless/ModuleHelper.java rename to lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java index 0ab4f95540..274b33d09f 100644 --- a/lib/src/main/java/com/diffplug/spotless/ModuleHelper.java +++ b/lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.spotless; +package com.diffplug.spotless.java; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -31,7 +31,10 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import sun.misc.Unsafe; -public final class ModuleHelper { +final class ModuleHelper { + // prevent direct instantiation + private ModuleHelper() {} + private static final Map REQUIRED_PACKAGES_TO_TEST_CLASSES = new HashMap<>(); static { From b3fc6bc069f39cda48ff4d2144981acdb5358e8c Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 4 Jun 2022 18:56:54 -0700 Subject: [PATCH 7/8] Only run ModuleHelper for JVM >= 16. --- .../main/java/com/diffplug/spotless/java/ModuleHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java b/lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java index 274b33d09f..68a2a181d0 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java +++ b/lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java @@ -28,6 +28,8 @@ import javax.annotation.Nullable; +import com.diffplug.spotless.Jvm; + import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import sun.misc.Unsafe; @@ -48,7 +50,7 @@ private ModuleHelper() {} private static boolean checkDone = false; public static synchronized void doOpenInternalPackagesIfRequired() { - if (checkDone) { + if (Jvm.version() < 16 || checkDone) { return; } try { From bd8341772ad31305538de09458d51517a278c310 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 4 Jun 2022 18:57:45 -0700 Subject: [PATCH 8/8] Improve the maven changelog entry. --- plugin-maven/CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 85094381e2..01cf26bcbd 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -4,7 +4,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Fixed -* google-java-format works on JDK16+ without jvm args workaround. ([#1224](https://github.com/diffplug/spotless/pull/1224)) +* `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