diff --git a/build.gradle b/build.gradle index 4ce3e48..8234cf0 100644 --- a/build.gradle +++ b/build.gradle @@ -12,10 +12,6 @@ java { withJavadocJar() } -tasks.withType(JavaExec).configureEach { - jvmArgs '--add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED' -} - tasks.withType(Javadoc).configureEach { options { quiet() @@ -71,7 +67,6 @@ targetVendors.each { spec, info -> toolchain.vendor = spec } test.useJUnitPlatform() - test.jvmArgs '--add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED' if (javaVersion < 17) { test.jvmArgs '--illegal-access=deny' } diff --git a/src/main/java/zone/rong/imaginebreaker/ImagineBreaker.java b/src/main/java/zone/rong/imaginebreaker/ImagineBreaker.java index a08377a..a5441f0 100644 --- a/src/main/java/zone/rong/imaginebreaker/ImagineBreaker.java +++ b/src/main/java/zone/rong/imaginebreaker/ImagineBreaker.java @@ -28,6 +28,8 @@ public final class ImagineBreaker { private static final Unsafe UNSAFE = retrieveUnsafe(); private static final Lookup LOOKUP = retrieveLookup(); + private static final MethodHandle MODULE$ADD_EXPORTS_TO_ALL_0 = retrieveAddExportsToAll0(); + private static final Set EVERYONE_MODULE_SET = retrieveEveryoneSet(); private static final VarHandle MODULE$OPEN_PACKAGES = retrieveOpenPackagesHandle(); private static final VarHandle CLASS$MODULE = retrieveModuleHandle(); @@ -60,6 +62,10 @@ public static void openBootModules() { openModuleLayer(ModuleLayer.boot()); } + public static void openBootModule(String bootModule) { + openModule(ModuleLayer.boot().findModule(bootModule).orElseThrow()); + } + /** * Opens all modules within the specified ModuleLayer * @@ -76,6 +82,13 @@ public static void openModuleLayer(ModuleLayer layer) { */ public static void openModule(Module module) { MODULE$OPEN_PACKAGES.set(module, WorldRejector.INSTANCE); + for (String pkg : module.getPackages()) { + try { + MODULE$ADD_EXPORTS_TO_ALL_0.invokeExact(module, pkg); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } } /** @@ -199,6 +212,14 @@ private static Field retrieveImplLookup() { } } + private static MethodHandle retrieveAddExportsToAll0() { + try { + return LOOKUP.findStatic(Module.class, "addExportsToAll0", MethodType.methodType(void.class, Module.class, String.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + private static Set retrieveEveryoneSet() { try { return (Set) LOOKUP.findStaticVarHandle(Module.class, "EVERYONE_SET", Set.class).get(); @@ -234,6 +255,7 @@ private static VarHandle retrieveModuleHandle() { private static VarHandle retrieveFieldFilterMap() { try { + openBootModule("java.base"); return LOOKUP.findStaticVarHandle(Reflection.class, "fieldFilterMap", Map.class); } catch (IllegalAccessException e1) { if (e1.getMessage().endsWith("Expected static field.")) { @@ -251,6 +273,7 @@ private static VarHandle retrieveFieldFilterMap() { private static VarHandle retrieveMethodFilterMap() { try { + openBootModule("java.base"); return LOOKUP.findStaticVarHandle(Reflection.class, "methodFilterMap", Map.class); } catch (IllegalAccessException e1) { if (e1.getMessage().endsWith("Expected static field.")) { diff --git a/src/test/java/zone/rong/imaginebreaker/FilterTest.java b/src/test/java/zone/rong/imaginebreaker/FilterTest.java deleted file mode 100644 index dccdfb9..0000000 --- a/src/test/java/zone/rong/imaginebreaker/FilterTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package zone.rong.imaginebreaker; - -import jdk.internal.reflect.Reflection; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Set; - -public class FilterTest { - - private static class Subject { - - private static int subjectField = -1; - - private static int subjectMethod() { - return -1; - } - - } - - @Test - public void removeFieldFilters() throws ReflectiveOperationException { - registerFieldFilter(); - Assertions.assertThrows(NoSuchFieldException.class, this::retrieveSubjectField); - ImagineBreaker.wipeFieldFilters(); - Assertions.assertDoesNotThrow(this::retrieveSubjectMethod); - } - - @Test - public void removeMethodFilters() throws ReflectiveOperationException { - registerMethodFilter(); - Assertions.assertThrows(NoSuchMethodException.class, this::retrieveSubjectMethod); - ImagineBreaker.wipeMethodFilters(); - Assertions.assertDoesNotThrow(this::retrieveSubjectMethod); - } - - private void registerFieldFilter() throws ReflectiveOperationException { - try { - Method method = Reflection.class.getDeclaredMethod("registerFieldsToFilter", Class.class, String[].class); - method.invoke(null, Subject.class, new String[] { "subjectField" }); - } catch (Exception e) { - Method method = Reflection.class.getDeclaredMethod("registerFieldsToFilter", Class.class, Set.class); - method.invoke(null, Subject.class, Set.of("subjectField")); - } - } - - private void registerMethodFilter() throws ReflectiveOperationException { - try { - Method method = Reflection.class.getDeclaredMethod("registerMethodsToFilter", Class.class, String[].class); - method.invoke(null, Subject.class, new String[] { "subjectMethod" }); - } catch (Exception e) { - Method method = Reflection.class.getDeclaredMethod("registerMethodsToFilter", Class.class, Set.class); - method.invoke(null, Subject.class, Set.of("subjectMethod")); - } - } - - private Field retrieveSubjectField() throws NoSuchFieldException { - return Subject.class.getDeclaredField("subjectField"); - } - - private Method retrieveSubjectMethod() throws NoSuchMethodException { - return Subject.class.getDeclaredMethod("subjectMethod"); - } - -} diff --git a/src/test/java/zone/rong/imaginebreaker/LookupTest.java b/src/test/java/zone/rong/imaginebreaker/lookup/LookupTest.java similarity index 91% rename from src/test/java/zone/rong/imaginebreaker/LookupTest.java rename to src/test/java/zone/rong/imaginebreaker/lookup/LookupTest.java index 63f090a..56e0c3e 100644 --- a/src/test/java/zone/rong/imaginebreaker/LookupTest.java +++ b/src/test/java/zone/rong/imaginebreaker/lookup/LookupTest.java @@ -1,9 +1,11 @@ -package zone.rong.imaginebreaker; +package zone.rong.imaginebreaker.lookup; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.parallel.Isolated; +import zone.rong.imaginebreaker.ImagineBreaker; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -11,6 +13,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +@Isolated public class LookupTest { @Test diff --git a/src/test/java/zone/rong/imaginebreaker/ModuleTest.java b/src/test/java/zone/rong/imaginebreaker/module/ModuleTest.java similarity index 76% rename from src/test/java/zone/rong/imaginebreaker/ModuleTest.java rename to src/test/java/zone/rong/imaginebreaker/module/ModuleTest.java index 0e4ab71..e8200f6 100644 --- a/src/test/java/zone/rong/imaginebreaker/ModuleTest.java +++ b/src/test/java/zone/rong/imaginebreaker/module/ModuleTest.java @@ -1,15 +1,17 @@ -package zone.rong.imaginebreaker; +package zone.rong.imaginebreaker.module; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; +import zone.rong.imaginebreaker.ImagineBreaker; import java.lang.reflect.Field; -import java.lang.reflect.InaccessibleObjectException; +@Isolated public class ModuleTest { @Test - public void openBootModules() throws ReflectiveOperationException { + public void openBootModules() { Assertions.assertThrows(RuntimeException.class, this::retrieveStringBackingArray); ImagineBreaker.openBootModules(); Assertions.assertDoesNotThrow(this::retrieveStringBackingArray); diff --git a/src/test/java/zone/rong/imaginebreaker/reflectionfilter/NewFieldReflectionFilterTest.java b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/NewFieldReflectionFilterTest.java new file mode 100644 index 0000000..284157e --- /dev/null +++ b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/NewFieldReflectionFilterTest.java @@ -0,0 +1,36 @@ +package zone.rong.imaginebreaker.reflectionfilter; + +import jdk.internal.reflect.Reflection; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.parallel.Isolated; +import zone.rong.imaginebreaker.ImagineBreaker; + +import java.lang.reflect.Method; +import java.util.Set; + +@Isolated +@EnabledForJreRange(min = JRE.JAVA_12) +public class NewFieldReflectionFilterTest { + + @Test + public void removeFieldFilters() { + ImagineBreaker.openBootModule("java.base"); + registerFieldFilter(); + Assertions.assertThrows(NoSuchFieldException.class, Subject::retrieveSubjectField); + ImagineBreaker.wipeFieldFilters(); + Assertions.assertDoesNotThrow(Subject::retrieveSubjectField); + } + + private void registerFieldFilter() { + try { + Method method = Reflection.class.getDeclaredMethod("registerFieldsToFilter", Class.class, Set.class); + method.invoke(null, Subject.class, Set.of("subjectField")); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/test/java/zone/rong/imaginebreaker/reflectionfilter/NewMethodReflectionFilterTest.java b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/NewMethodReflectionFilterTest.java new file mode 100644 index 0000000..6666a06 --- /dev/null +++ b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/NewMethodReflectionFilterTest.java @@ -0,0 +1,36 @@ +package zone.rong.imaginebreaker.reflectionfilter; + +import jdk.internal.reflect.Reflection; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.parallel.Isolated; +import zone.rong.imaginebreaker.ImagineBreaker; + +import java.lang.reflect.Method; +import java.util.Set; + +@Isolated +@EnabledForJreRange(min = JRE.JAVA_12) +public class NewMethodReflectionFilterTest { + + @Test + public void removeMethodFilters() { + ImagineBreaker.openBootModule("java.base"); + registerMethodFilter(); + Assertions.assertThrows(NoSuchMethodException.class, Subject::retrieveSubjectMethod); + ImagineBreaker.wipeMethodFilters(); + Assertions.assertDoesNotThrow(Subject::retrieveSubjectMethod); + } + + private void registerMethodFilter() { + try { + Method method = Reflection.class.getDeclaredMethod("registerMethodsToFilter", Class.class, Set.class); + method.invoke(null, Subject.class, Set.of("subjectMethod")); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/test/java/zone/rong/imaginebreaker/reflectionfilter/OldFieldReflectionFilterTest.java b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/OldFieldReflectionFilterTest.java new file mode 100644 index 0000000..6556c36 --- /dev/null +++ b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/OldFieldReflectionFilterTest.java @@ -0,0 +1,35 @@ +package zone.rong.imaginebreaker.reflectionfilter; + +import jdk.internal.reflect.Reflection; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.parallel.Isolated; +import zone.rong.imaginebreaker.ImagineBreaker; + +import java.lang.reflect.Method; + +@Isolated +@EnabledForJreRange(min = JRE.JAVA_9, max = JRE.JAVA_11) +public class OldFieldReflectionFilterTest { + + @Test + public void removeFieldFilters() { + ImagineBreaker.openBootModule("java.base"); + registerFieldFilter(); + Assertions.assertThrows(NoSuchFieldException.class, Subject::retrieveSubjectField); + ImagineBreaker.wipeFieldFilters(); + Assertions.assertDoesNotThrow(Subject::retrieveSubjectField); + } + + private void registerFieldFilter() { + try { + Method method = Reflection.class.getDeclaredMethod("registerFieldsToFilter", Class.class, String[].class); + method.invoke(null, Subject.class, new String[] { "subjectField" }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/test/java/zone/rong/imaginebreaker/reflectionfilter/OldMethodReflectionFilterTest.java b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/OldMethodReflectionFilterTest.java new file mode 100644 index 0000000..764a794 --- /dev/null +++ b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/OldMethodReflectionFilterTest.java @@ -0,0 +1,35 @@ +package zone.rong.imaginebreaker.reflectionfilter; + +import jdk.internal.reflect.Reflection; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.parallel.Isolated; +import zone.rong.imaginebreaker.ImagineBreaker; + +import java.lang.reflect.Method; + +@Isolated +@EnabledForJreRange(min = JRE.JAVA_9, max = JRE.JAVA_11) +public class OldMethodReflectionFilterTest { + + @Test + public void removeMethodFilters() { + ImagineBreaker.openBootModule("java.base"); + registerMethodFilter(); + Assertions.assertThrows(NoSuchMethodException.class, Subject::retrieveSubjectMethod); + ImagineBreaker.wipeMethodFilters(); + Assertions.assertDoesNotThrow(Subject::retrieveSubjectMethod); + } + + private void registerMethodFilter() { + try { + Method method = Reflection.class.getDeclaredMethod("registerMethodsToFilter", Class.class, String[].class); + method.invoke(null, Subject.class, new String[] { "subjectMethod" }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/test/java/zone/rong/imaginebreaker/reflectionfilter/Subject.java b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/Subject.java new file mode 100644 index 0000000..2e965a5 --- /dev/null +++ b/src/test/java/zone/rong/imaginebreaker/reflectionfilter/Subject.java @@ -0,0 +1,22 @@ +package zone.rong.imaginebreaker.reflectionfilter; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +class Subject { + + private static int subjectField = -1; + + private static int subjectMethod() { + return -1; + } + + static Field retrieveSubjectField() throws NoSuchFieldException { + return Subject.class.getDeclaredField("subjectField"); + } + + static Method retrieveSubjectMethod() throws NoSuchMethodException { + return Subject.class.getDeclaredMethod("subjectMethod"); + } + +}