Skip to content

Commit 2cb9043

Browse files
authored
Fix GJF without passing JVM args (#1224 fixes #834)
2 parents 95dc988 + bd83417 commit 2cb9043

File tree

6 files changed

+148
-1
lines changed

6 files changed

+148
-1
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1212
## [Unreleased]
1313
### Added
1414
* Support for `editorConfigOverride` in `ktlint`. ([#1218](https://github.com/diffplug/spotless/pull/1218) fixes [#1193](https://github.com/diffplug/spotless/issues/1193))
15+
### Fixed
16+
* `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))
1517

1618
## [2.25.3] - 2022-05-10
1719
### Fixed

lib/src/main/java/com/diffplug/spotless/Jvm.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2021 DiffPlug
2+
* Copyright 2016-2022 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ static final class State implements Serializable {
136136

137137
State(String stepName, String groupArtifact, String version, String style, Provisioner provisioner, boolean reflowLongStrings) throws Exception {
138138
JVM_SUPPORT.assertFormatterSupported(version);
139+
ModuleHelper.doOpenInternalPackagesIfRequired();
139140
this.jarState = JarState.from(groupArtifact + ":" + version, provisioner);
140141
this.stepName = stepName;
141142
this.version = version;
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright 2016-2022 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.java;
17+
18+
import java.lang.invoke.MethodHandle;
19+
import java.lang.invoke.MethodHandles;
20+
import java.lang.reflect.Field;
21+
import java.lang.reflect.Method;
22+
import java.lang.reflect.Modifier;
23+
import java.util.ArrayList;
24+
import java.util.Collection;
25+
import java.util.HashMap;
26+
import java.util.List;
27+
import java.util.Map;
28+
29+
import javax.annotation.Nullable;
30+
31+
import com.diffplug.spotless.Jvm;
32+
33+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
34+
import sun.misc.Unsafe;
35+
36+
final class ModuleHelper {
37+
// prevent direct instantiation
38+
private ModuleHelper() {}
39+
40+
private static final Map<String, String> REQUIRED_PACKAGES_TO_TEST_CLASSES = new HashMap<>();
41+
42+
static {
43+
REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.util", "Context");
44+
REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.file", "CacheFSInfo");
45+
REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.tree", "TreeTranslator");
46+
REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.parser", "Tokens$TokenKind");
47+
REQUIRED_PACKAGES_TO_TEST_CLASSES.putIfAbsent("com.sun.tools.javac.api", "DiagnosticFormatter$PositionKind");
48+
}
49+
50+
private static boolean checkDone = false;
51+
52+
public static synchronized void doOpenInternalPackagesIfRequired() {
53+
if (Jvm.version() < 16 || checkDone) {
54+
return;
55+
}
56+
try {
57+
checkDone = true;
58+
final List<String> unavailableRequiredPackages = unavailableRequiredPackages();
59+
if (!unavailableRequiredPackages.isEmpty()) {
60+
openPackages(unavailableRequiredPackages);
61+
final List<String> failedToOpen = unavailableRequiredPackages();
62+
if (!failedToOpen.isEmpty()) {
63+
final StringBuilder message = new StringBuilder();
64+
message.append("WARNING: Some required internal classes are unavailable. Please consider adding the following JVM arguments\n");
65+
message.append("WARNING: ");
66+
for (String name : failedToOpen) {
67+
message.append(String.format("--add-opens jdk.compiler/%s=ALL-UNNAMED", name));
68+
}
69+
System.err.println(message);
70+
}
71+
}
72+
} catch (Throwable e) {
73+
System.err.println("WARNING: Failed to check for unavailable JDK packages. Reason: " + e.getMessage());
74+
}
75+
}
76+
77+
@SuppressFBWarnings("REC_CATCH_EXCEPTION") // workaround JDK11
78+
private static List<String> unavailableRequiredPackages() {
79+
final List<String> packages = new ArrayList<>();
80+
for (Map.Entry<String, String> e : REQUIRED_PACKAGES_TO_TEST_CLASSES.entrySet()) {
81+
final String key = e.getKey();
82+
final String value = e.getValue();
83+
try {
84+
final Class<?> clazz = Class.forName(key + "." + value);
85+
if (clazz.isEnum()) {
86+
clazz.getMethod("values").invoke(null);
87+
} else {
88+
clazz.getDeclaredConstructor().newInstance();
89+
}
90+
} catch (IllegalAccessException ex) {
91+
packages.add(key);
92+
} catch (Exception ignore) {
93+
// in old versions of JDK some classes could be unavailable
94+
}
95+
}
96+
return packages;
97+
}
98+
99+
@SuppressWarnings("unchecked")
100+
private static void openPackages(Collection<String> packagesToOpen) throws Throwable {
101+
final Collection<?> modules = allModules();
102+
if (modules == null) {
103+
return;
104+
}
105+
final Unsafe unsafe = Unsafe.getUnsafe();
106+
final Field implLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
107+
final MethodHandles.Lookup lookup = (MethodHandles.Lookup) unsafe.getObject(
108+
unsafe.staticFieldBase(implLookupField),
109+
unsafe.staticFieldOffset(implLookupField));
110+
final MethodHandle modifiers = lookup.findSetter(Method.class, "modifiers", Integer.TYPE);
111+
final Method exportMethod = Class.forName("java.lang.Module").getDeclaredMethod("implAddOpens", String.class);
112+
modifiers.invokeExact(exportMethod, Modifier.PUBLIC);
113+
for (Object module : modules) {
114+
final Collection<String> packages = (Collection<String>) module.getClass().getMethod("getPackages").invoke(module);
115+
for (String name : packages) {
116+
if (packagesToOpen.contains(name)) {
117+
exportMethod.invoke(module, name);
118+
}
119+
}
120+
}
121+
}
122+
123+
@Nullable
124+
@SuppressFBWarnings("REC_CATCH_EXCEPTION") // workaround JDK11
125+
private static Collection<?> allModules() {
126+
// calling ModuleLayer.boot().modules() by reflection
127+
try {
128+
final Object boot = Class.forName("java.lang.ModuleLayer").getMethod("boot").invoke(null);
129+
if (boot == null) {
130+
return null;
131+
}
132+
final Object modules = boot.getClass().getMethod("modules").invoke(boot);
133+
return (Collection<?>) modules;
134+
} catch (Exception ignore) {
135+
return null;
136+
}
137+
}
138+
}

plugin-gradle/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
66
### Added
77
* Support for `editorConfigOverride` in `ktlint`. ([#1218](https://github.com/diffplug/spotless/pull/1218) fixes [#1193](https://github.com/diffplug/spotless/issues/1193))
88
* If you are using properties like `indent_size`, you should pass now pass them as `editorConfigOverride` and not as `userData`.
9+
### Fixed
10+
* `googleJavaFormat` and `removeUnusedImports` works on JDK16+ without jvm args workaround. ([#1224](https://github.com/diffplug/spotless/pull/1224))
11+
* 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))
912

1013
## [6.6.1] - 2022-05-13
1114
### Fixed

plugin-maven/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
44

55
## [Unreleased]
6+
### Fixed
7+
* `googleJavaFormat` and `removeUnusedImports` works on JDK16+ without jvm args workaround. ([#1224](https://github.com/diffplug/spotless/pull/1224))
8+
* 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))
69

710
## [2.22.5] - 2022-05-10
811
### Fixed

0 commit comments

Comments
 (0)