From 2e3e9aeb4e0a7144e5ed5f3f6f49b7935de5cc3e Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Sun, 13 Jun 2021 17:10:47 +0800 Subject: [PATCH] `ModuleAccess.getEVERYONE_MODULE & getALL_UNNAMED_MODULE` --- .../unsafeaccessor/ModuleAccess.java | 18 +++++ .../unsafeaccessor/ModuleAccessImpl.java | 20 ++++++ .../github/karlatemp/unsafeaccessor/Root.java | 25 +++++++ .../karlatemp/unsafeaccessor/UsfAccessor.java | 35 ++++++++-- build.gradle | 2 +- .../codegen/GenObjectInitializer.java | 49 +++++++++++++ .../unsafeaccessor/ModuleAccessImpl$JDK9.java | 42 +++++++++++- .../karlatemp/unsafeaccessor/Open9.java | 1 + .../unsafeaccessor/UsfAccessorImpl.java | 56 ++++++++++++++- .../jdk9/java/runtest/ModuleAccessTest.java | 68 +++++++++++++++++++ .../java/runtest/TestObjectInitializer.java | 28 ++++++++ .../src/main/java/runtest/TestTasks.java | 6 +- 12 files changed, 335 insertions(+), 15 deletions(-) create mode 100644 impl/codegen/src/test/java/io/github/karlatemp/unsafeaccessor/codegen/GenObjectInitializer.java create mode 100644 impl/testunit/src/main/java/runtest/TestObjectInitializer.java diff --git a/api/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccess.java b/api/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccess.java index f07d330..ecad27b 100644 --- a/api/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccess.java +++ b/api/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccess.java @@ -13,6 +13,24 @@ public interface ModuleAccess { boolean isSupport(); + /** + * special Module to mean "everyone" + *

+ * Only used for {@link #addExports(Object, String, Object)}, {@link #addOpens(Object, String, Object)} + * + * @since 1.6.0 + */ + Object getEVERYONE_MODULE(); + + /** + * special Module to mean "all unnamed modules" + *

+ * Only used for {@link #addExports(Object, String, Object)}, {@link #addOpens(Object, String, Object)} + * + * @since 1.6.0 + */ + Object getALL_UNNAMED_MODULE(); + /** * klass.getModule() */ diff --git a/api/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccessImpl.java b/api/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccessImpl.java index be19850..38eecad 100644 --- a/api/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccessImpl.java +++ b/api/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccessImpl.java @@ -16,6 +16,16 @@ public Object getModule(Class klass) { throw new UnsupportedOperationException(); } + @Override + public Object getEVERYONE_MODULE() { + throw new UnsupportedOperationException(); + } + + @Override + public Object getALL_UNNAMED_MODULE() { + throw new UnsupportedOperationException(); + } + @Override public Package definePackage(ClassLoader cl, String name, Object module) { throw new UnsupportedOperationException(); @@ -83,6 +93,16 @@ public boolean isSupport() { return false; } + @Override + public Object getEVERYONE_MODULE() { + return null; + } + + @Override + public Object getALL_UNNAMED_MODULE() { + return null; + } + @Override public Object getModule(Class klass) { return null; diff --git a/api/src/main/java/io/github/karlatemp/unsafeaccessor/Root.java b/api/src/main/java/io/github/karlatemp/unsafeaccessor/Root.java index f724f75..6721e7d 100644 --- a/api/src/main/java/io/github/karlatemp/unsafeaccessor/Root.java +++ b/api/src/main/java/io/github/karlatemp/unsafeaccessor/Root.java @@ -6,6 +6,7 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; +import java.util.function.Consumer; /** * JVM Root Access. @@ -177,4 +178,28 @@ public static ModuleAccess getModuleAccess() { getUnsafe(); return Secret.MACCESS; } + + static class ObjectInitializer { + static Consumer initializer; + + static Consumer initializer() { + if (initializer != null) return initializer; + synchronized (ObjectInitializer.class) { + if (initializer != null) return initializer; + initializer = UsfAccessor.allocateObjectInitializer(); + } + return initializer; + } + } + + /** + * Do nothing + * + * @since 1.6.0 + */ + public static void initializeObject(Object instance) { + if (instance == null) return; + Unsafe.getUnsafe0().ensureClassInitialized(instance.getClass()); + ObjectInitializer.initializer().accept(instance); + } } diff --git a/api/src/main/java/io/github/karlatemp/unsafeaccessor/UsfAccessor.java b/api/src/main/java/io/github/karlatemp/unsafeaccessor/UsfAccessor.java index 4f28d3b..7ab4667 100644 --- a/api/src/main/java/io/github/karlatemp/unsafeaccessor/UsfAccessor.java +++ b/api/src/main/java/io/github/karlatemp/unsafeaccessor/UsfAccessor.java @@ -1,16 +1,37 @@ package io.github.karlatemp.unsafeaccessor; +import java.util.function.Consumer; + // internal public class UsfAccessor { + static abstract class UsfAccessorSpi { + abstract Object allocateUnsafe(); + + abstract Consumer allocateObjectInitializer(); + } + + static UsfAccessorSpi spi; + + static UsfAccessorSpi spi() { + if (spi != null) return spi; + synchronized (UsfAccessorSpi.class) { + if (spi != null) return spi; + try { + // in module :impl.loader + Class impl = Class.forName("io.github.karlatemp.unsafeaccessor.UsfAccessorImpl"); + return spi = impl.asSubclass(UsfAccessorSpi.class).getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new UnsupportedOperationException(e); + } + } + } // Internal service for load Unsafe. protected static Object allocateUnsafe() { - try { - // in module :impl.loader - Class impl = Class.forName("io.github.karlatemp.unsafeaccessor.UsfAccessorImpl"); - return impl.getDeclaredMethod("allocateUnsafe").invoke(null); - } catch (Exception e) { - throw new UnsupportedOperationException(e); - } + return spi().allocateUnsafe(); + } + + protected static Consumer allocateObjectInitializer() { + return spi().allocateObjectInitializer(); } } diff --git a/build.gradle b/build.gradle index 3748b50..b0ab73a 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ description = "A bridge for access jdk.internal.misc.Unsafe" allprojects { group 'io.github.karlatemp' - version '1.5.0' + version '1.6.0' repositories { mavenCentral() } diff --git a/impl/codegen/src/test/java/io/github/karlatemp/unsafeaccessor/codegen/GenObjectInitializer.java b/impl/codegen/src/test/java/io/github/karlatemp/unsafeaccessor/codegen/GenObjectInitializer.java new file mode 100644 index 0000000..cbee896 --- /dev/null +++ b/impl/codegen/src/test/java/io/github/karlatemp/unsafeaccessor/codegen/GenObjectInitializer.java @@ -0,0 +1,49 @@ +package io.github.karlatemp.unsafeaccessor.codegen; + +import io.github.karlatemp.unsafeaccessor.CodeGenUtils; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; + +import java.util.Base64; + +public class GenObjectInitializer { + public static void main(String[] args) throws Throwable { + ClassNode newClass = new ClassNode(); + newClass.visit( + Opcodes.V1_8, + 0, + "io/github/karlatemp/unsafeaccessor/ObjectInitializerHolder", + null, + "java/lang/Object", + null + ); + CodeGenUtils.genCons(newClass); + MethodVisitor code = newClass.visitMethod(Opcodes.ACC_STATIC, "code", "()[B", null, null); + code.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Base64", "getDecoder", "()Ljava/util/Base64$Decoder;", false); + { + ClassNode initializer = new ClassNode(); + initializer.visit( + Opcodes.V1_8, + 0, + "io/github/karlatemp/unsafeaccessor/ObjectInitializerImpl", + null, + "jdk/internal/reflect/MagicAccessorImpl", + new String[]{"java/util/function/Consumer"} + ); + CodeGenUtils.genCons(initializer); + MethodVisitor accept = initializer.visitMethod(Opcodes.ACC_PUBLIC, "accept", "(Ljava/lang/Object;)V", null, null); + accept.visitVarInsn(Opcodes.ALOAD, 0); + accept.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false); + accept.visitInsn(Opcodes.RETURN); + accept.visitMaxs(2, 2); + byte[] bytecode = CodeGenUtils.toBytecode(initializer); + code.visitLdcInsn(Base64.getEncoder().encodeToString(bytecode)); + } + code.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/Base64$Decoder", "decode", "(Ljava/lang/String;)[B", false); + code.visitInsn(Opcodes.ARETURN); + code.visitMaxs(3, 3); + + CodeGenUtils.save(newClass); + } +} diff --git a/impl/jdk9/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccessImpl$JDK9.java b/impl/jdk9/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccessImpl$JDK9.java index a65f254..c4c13f2 100644 --- a/impl/jdk9/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccessImpl$JDK9.java +++ b/impl/jdk9/src/main/java/io/github/karlatemp/unsafeaccessor/ModuleAccessImpl$JDK9.java @@ -9,12 +9,28 @@ import java.util.Iterator; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Supplier; -@SuppressWarnings("unchecked") +@SuppressWarnings({"unchecked", "rawtypes"}) class ModuleAccessImpl$JDK9 implements ModuleAccess { + private static Object EVERYONE_MODULE; + private static Object ALL_UNNAMED_MODULE; + private static final Supplier SUPPLIER; + + private static Object findModule(String name) { + try { + return (Module) SUPPLIER.get().getTrustedIn(Module.class) + .findStaticGetter(Module.class, name, Module.class) + .invoke(); + } catch (Throwable throwable) { + return throwable; + } + } + static { - ((Consumer) ModuleAccessImpl$JDK9.class.getClassLoader()) - .accept(new ModuleAccessImpl$JDK9()); + ClassLoader classLoader = ModuleAccessImpl$JDK9.class.getClassLoader(); + SUPPLIER = (Supplier) ((Supplier) classLoader).get(); + ((Consumer) classLoader).accept(new ModuleAccessImpl$JDK9()); } @Override @@ -30,6 +46,26 @@ public Object getModule(Class klass) { return klass.getModule(); } + @Override + public Object getALL_UNNAMED_MODULE() { + if (ALL_UNNAMED_MODULE == null) { + ALL_UNNAMED_MODULE = findModule("ALL_UNNAMED_MODULE"); + } + if (ALL_UNNAMED_MODULE instanceof Throwable) + throw new UnsupportedOperationException((Throwable) ALL_UNNAMED_MODULE); + return ALL_UNNAMED_MODULE; + } + + @Override + public Object getEVERYONE_MODULE() { + if (EVERYONE_MODULE == null) { + EVERYONE_MODULE = findModule("EVERYONE_MODULE"); + } + if (EVERYONE_MODULE instanceof Throwable) + throw new UnsupportedOperationException((Throwable) EVERYONE_MODULE); + return EVERYONE_MODULE; + } + @Override public Object newModuleBuilder( String mn, diff --git a/impl/jdk9/src/main/java/io/github/karlatemp/unsafeaccessor/Open9.java b/impl/jdk9/src/main/java/io/github/karlatemp/unsafeaccessor/Open9.java index 9fa7449..7222977 100644 --- a/impl/jdk9/src/main/java/io/github/karlatemp/unsafeaccessor/Open9.java +++ b/impl/jdk9/src/main/java/io/github/karlatemp/unsafeaccessor/Open9.java @@ -138,6 +138,7 @@ JLA, findSS(), findJLMA() data = replace(data, "Lio/github/karlatemp/unsafeaccessor/ModuleAccessImpl$JDK9;", "L" + targetJvmName + ";"); data = doRemap(data, ACCESS_CLASSES); + env = (Supplier) () -> UnsafeAccess.INSTANCE; Class.forName(loader.define(data).getName(), true, loader); Root.Secret.MACCESS = (ModuleAccess) env; diff --git a/impl/loader/src/main/java/io/github/karlatemp/unsafeaccessor/UsfAccessorImpl.java b/impl/loader/src/main/java/io/github/karlatemp/unsafeaccessor/UsfAccessorImpl.java index 7a9d02d..cbd7d64 100644 --- a/impl/loader/src/main/java/io/github/karlatemp/unsafeaccessor/UsfAccessorImpl.java +++ b/impl/loader/src/main/java/io/github/karlatemp/unsafeaccessor/UsfAccessorImpl.java @@ -1,7 +1,13 @@ package io.github.karlatemp.unsafeaccessor; -class UsfAccessorImpl { - static Object allocateUnsafe() throws Exception { +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +@SuppressWarnings("unchecked") +class UsfAccessorImpl extends UsfAccessor.UsfAccessorSpi { + Object allocateUnsafe() { try { Class.forName("java.lang.Module"); Root.Secret.MACCESS = new ModuleAccessImpl.PendingInit(); @@ -16,4 +22,50 @@ static Object allocateUnsafe() throws Exception { return new SunMiscUnsafeImpl(); } } + + static Class findC(ClassLoader cl, String... names) { + List throwables = new ArrayList<>(); + for (String n : names) { + try { + return Class.forName(n, false, cl); + } catch (ClassNotFoundException e) { + throwables.add(e); + } + } + NoClassDefFoundError error = new NoClassDefFoundError(); + for (Throwable t : throwables) error.addSuppressed(t); + throw error; + } + + @Override + Consumer allocateObjectInitializer() { + byte[] code = ObjectInitializerHolder.code(); + Class MagicAccessorImpl = findC( + null, + "jdk.internal.reflect.MagicAccessorImpl", + "sun.reflect.MagicAccessorImpl" + ); + Class DelegatingClassLoader = findC( + null, + "jdk.internal.reflect.DelegatingClassLoader", + "sun.reflect.DelegatingClassLoader" + ).asSubclass(ClassLoader.class); + if (!MagicAccessorImpl.getName().equals("jdk.internal.reflect.MagicAccessorImpl")) { + code = BytecodeUtil.replace(code, + "jdk/internal/reflect/MagicAccessorImpl", + MagicAccessorImpl.getName().replace('.', '/') + ); + } + try { + Constructor constructor = DelegatingClassLoader.getDeclaredConstructor(ClassLoader.class); + Root.OpenAccess.openAccess0(constructor, true); + ClassLoader delegate = constructor.newInstance(ClassLoader.getSystemClassLoader()); + Class klass = Unsafe.getUnsafe0().defineClass(null, code, 0, code.length, delegate, null); + Constructor constructor1 = klass.getDeclaredConstructor(); + Root.OpenAccess.openAccess0(constructor1, true); + return (Consumer) constructor1.newInstance(); + } catch (Exception e) { + throw new UnsupportedOperationException(e); + } + } } diff --git a/impl/testunit/src/jdk9/java/runtest/ModuleAccessTest.java b/impl/testunit/src/jdk9/java/runtest/ModuleAccessTest.java index c10af8a..0ae1b7d 100644 --- a/impl/testunit/src/jdk9/java/runtest/ModuleAccessTest.java +++ b/impl/testunit/src/jdk9/java/runtest/ModuleAccessTest.java @@ -43,4 +43,72 @@ public static void run() { } } + + @TestTask(name = "test methods") + public static void testMethods() { + ModuleAccess access = Root.getModuleAccess(); + Unsafe usf = Unsafe.getUnsafe(); + + ClassLoader cl = ModuleAccessTest.class.getClassLoader(); + ModuleDescriptor.Builder moduleBuilder = (ModuleDescriptor.Builder) access.newModuleBuilder( + "asra.xatafaser.azxarmoivdsoijzoijfoijdsjoi", + false, + Set.of() + ); + String pkg = "aoiroiasjria.sdaiorjaoica.ar3094ksdaxapkfrafdasd.asejmaodh"; + moduleBuilder.packages(Set.of(pkg)); + Module module = (Module) access.defineModule(cl, moduleBuilder.build(), null); + + Module m1 = ModuleAccessTest.class.getModule(); + + Assertions.assertFalse(module.isExported(pkg)); + Assertions.assertFalse(module.isOpen(pkg)); + + Assertions.assertFalse(module.isExported(pkg, m1)); + Assertions.assertFalse(module.isOpen(pkg, m1)); + + access.addExports(module, pkg, m1); + + Assertions.assertTrue(module.isExported(pkg, m1)); + Assertions.assertFalse(module.isOpen(pkg, m1)); + + access.addOpens(module, pkg, m1); + + Assertions.assertTrue(module.isExported(pkg, m1)); + Assertions.assertTrue(module.isOpen(pkg, m1)); + + Assertions.assertFalse(module.isExported(pkg)); + Assertions.assertFalse(module.isOpen(pkg)); + + Module newUm = (Module) access.defineUnnamedModule(cl); + + + Assertions.assertFalse(module.isExported(pkg, newUm)); + Assertions.assertFalse(module.isOpen(pkg, newUm)); + + access.addExportsToAllUnnamed(module, pkg); + + Assertions.assertTrue(module.isExported(pkg, newUm)); + Assertions.assertFalse(module.isOpen(pkg, newUm)); + + access.addOpensToAllUnnamed(module, pkg); + + Assertions.assertTrue(module.isExported(pkg, newUm)); + Assertions.assertTrue(module.isOpen(pkg, newUm)); + + Module jl = java.util.logging.Logger.class.getModule(); + + + Assertions.assertFalse(module.isExported(pkg, jl)); + Assertions.assertFalse(module.isOpen(pkg, jl)); + + access.addExports(module, pkg, access.getEVERYONE_MODULE()); + Assertions.assertTrue(module.isExported(pkg, jl)); + Assertions.assertFalse(module.isOpen(pkg, jl)); + + access.addOpens(module, pkg, access.getEVERYONE_MODULE()); + Assertions.assertTrue(module.isExported(pkg, jl)); + Assertions.assertTrue(module.isOpen(pkg, jl)); + + } } diff --git a/impl/testunit/src/main/java/runtest/TestObjectInitializer.java b/impl/testunit/src/main/java/runtest/TestObjectInitializer.java new file mode 100644 index 0000000..a649e8c --- /dev/null +++ b/impl/testunit/src/main/java/runtest/TestObjectInitializer.java @@ -0,0 +1,28 @@ +package runtest; + +import io.github.karlatemp.unsafeaccessor.Root; +import io.github.karlatemp.unsafeaccessor.Unsafe; + +public class TestObjectInitializer { + @SuppressWarnings("InnerClassMayBeStatic") + class Kcl { + Kcl() { + throw new RuntimeException("SB"); + } + + Kcl(Void s) { + + } + } + + @TestTask + void run() throws Exception { + Unsafe usf = Unsafe.getUnsafe(); + Root.initializeObject(usf.allocateInstance(Object.class)); + Root.initializeObject(usf.allocateInstance(System.class)); + usf.allocateInstance(Kcl.class); + System.gc(); + Root.initializeObject(usf.allocateInstance(Kcl.class)); + System.gc(); + } +} diff --git a/impl/testunit/src/main/java/runtest/TestTasks.java b/impl/testunit/src/main/java/runtest/TestTasks.java index 6fcb1cb..5d51257 100644 --- a/impl/testunit/src/main/java/runtest/TestTasks.java +++ b/impl/testunit/src/main/java/runtest/TestTasks.java @@ -401,12 +401,14 @@ static void runTests(Collection tasks) throws Throwable { System.setErr(standardError); DumpOptions options = new DumpOptions(); - options.throwExceptionOnFailure = false; + boolean dump = Boolean.parseBoolean(System.getenv("writeToFile")); + options.throwExceptionOnFailure = !dump; options.noException = false; options.noRawOutput = false; dump(root, standardOutput, options); - if (Boolean.parseBoolean(System.getenv("writeToFile"))) { + if (dump) { + options.throwExceptionOnFailure = true; File runInfo = new File("build/run-unit-response.txt"); try (PrintStream ps = new PrintStream(new BufferedOutputStream( new FileOutputStream(runInfo, true)