Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 22 additions & 32 deletions lib/src/main/java/com/diffplug/spotless/java/ModuleHelper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 DiffPlug
* Copyright 2016-2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,9 +15,10 @@
*/
package com.diffplug.spotless.java;

import java.lang.invoke.MethodHandle;
import static java.lang.Class.forName;
import static java.lang.invoke.MethodHandles.privateLookupIn;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
Expand All @@ -26,15 +27,12 @@
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.diffplug.spotless.Jvm;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import sun.misc.Unsafe;

final class ModuleHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(ModuleHelper.class);
Expand Down Expand Up @@ -86,7 +84,7 @@ private static List<String> unavailableRequiredPackages() {
final String key = e.getKey();
final String value = e.getValue();
try {
final Class<?> clazz = Class.forName(key + "." + value);
final Class<?> clazz = forName(key + "." + value);
if (clazz.isEnum()) {
clazz.getMethod("values").invoke(null);
} else {
Expand All @@ -103,42 +101,34 @@ private static List<String> unavailableRequiredPackages() {

@SuppressWarnings("unchecked")
private static void openPackages(Collection<String> packagesToOpen) throws Throwable {
final Collection<?> modules = allModules();
if (modules == null) {
return;
}
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
final Unsafe unsafe = (Unsafe) unsafeField.get(null);
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) {
// Use MethodHandles.privateLookupIn for accessing private fields in JDK 9+
// Get the IMPL_LOOKUP field using MethodHandles
MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP").setAccessible(true);
// Use MethodHandles to modify method accessibility
var exportMethod = forName("java.lang.Module").getDeclaredMethod("implAddOpens", String.class);
// Set method to public using MethodHandle
privateLookupIn(MethodHandles.Lookup.class, MethodHandles.lookup())
.findSetter(Method.class, "modifiers", int.class)
.invokeExact(exportMethod, Modifier.PUBLIC);
for (var module : allModules()) {
for (var name : (Collection<String>) module.getClass().getMethod("getPackages").invoke(module)) {
if (packagesToOpen.contains(name)) {
exportMethod.invoke(module, name);
}
}
}
}

@Nullable @SuppressFBWarnings("REC_CATCH_EXCEPTION") // workaround JDK11
@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;
var boot = forName("java.lang.ModuleLayer").getMethod("boot").invoke(null);
return boot != null
? (Collection<?>) boot.getClass().getMethod("modules").invoke(boot)
: List.of();
} catch (Exception ignore) {
return null;
return List.of();
}
}
}
Loading