Skip to content

Commit

Permalink
Fix GJF without passing JVM args (#1224 fixes #834)
Browse files Browse the repository at this point in the history
  • Loading branch information
nedtwigg authored Jun 5, 2022
2 parents 95dc988 + bd83417 commit 2cb9043
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/src/main/java/com/diffplug/spotless/Jvm.java
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
138 changes: 138 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java
Original file line number Diff line number Diff line change
@@ -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<String, String> 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<String> unavailableRequiredPackages = unavailableRequiredPackages();
if (!unavailableRequiredPackages.isEmpty()) {
openPackages(unavailableRequiredPackages);
final List<String> 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<String> unavailableRequiredPackages() {
final List<String> packages = new ArrayList<>();
for (Map.Entry<String, String> 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<String> 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<String> packages = (Collection<String>) 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;
}
}
}
3 changes: 3 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 2cb9043

Please sign in to comment.