From d2f5c4b15ab84d81355791be1e13b911867b0881 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 18 Jan 2023 20:55:35 -0500 Subject: [PATCH] Implement class location cache --- .../classloading/ModernFixResourceFinder.java | 104 +++++++++++++++--- .../modernfix/core/ModernFixMixinPlugin.java | 28 +++-- 2 files changed, 107 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/embeddedt/modernfix/classloading/ModernFixResourceFinder.java b/src/main/java/org/embeddedt/modernfix/classloading/ModernFixResourceFinder.java index 5ee4c96d..a3f59b30 100644 --- a/src/main/java/org/embeddedt/modernfix/classloading/ModernFixResourceFinder.java +++ b/src/main/java/org/embeddedt/modernfix/classloading/ModernFixResourceFinder.java @@ -1,38 +1,110 @@ package org.embeddedt.modernfix.classloading; +import com.google.common.collect.Lists; +import cpw.mods.modlauncher.api.LamdbaExceptionUtils; +import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.LoadingModList; -import net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileLocator; -import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator; -import net.minecraftforge.fml.loading.moddiscovery.ModFile; -import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; +import net.minecraftforge.fml.loading.moddiscovery.*; +import net.minecraftforge.forgespi.language.IModInfo; +import net.minecraftforge.forgespi.locating.IModFile; import net.minecraftforge.forgespi.locating.IModLocator; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import java.io.IOException; +import java.lang.reflect.Field; import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; +import java.util.*; +import java.util.stream.Stream; public class ModernFixResourceFinder { - private static HashMap> urlsForClass = null; - public static void init() { + private static HashMap> urlsForClass = null; + private static final Class MINECRAFT_LOCATOR; + private static Field explodedDirModsField = null; + private static final Logger LOGGER = LogManager.getLogger("ModernFixResourceFinder"); + static { + try { + MINECRAFT_LOCATOR = (Class)Class.forName("net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer$MinecraftLocator"); + } catch(ClassNotFoundException e) { + /* that shouldn't happen */ + throw new RuntimeException(e); + } + } + public static synchronized void init() throws ReflectiveOperationException { urlsForClass = new HashMap<>(); + LOGGER.info("Start building list of class locations..."); for(ModFileInfo fileInfo : LoadingModList.get().getModFiles()) { ModFile file = fileInfo.getFile(); IModLocator locator = file.getLocator(); - Path rootPath = locator.findPath(file, "."); - System.out.println(rootPath.getParent().toAbsolutePath()); + Iterable rootPath = getRootPathForLocator(locator, file); + for(Path root : rootPath) { + try(Stream stream = Files.walk(root)) { + stream + .map(root::relativize) + .forEach(path -> { + String strPath = path.toString(); + URL url = (URL)LamdbaExceptionUtils.uncheck(() -> { + return new URL("modjar://" + fileInfo.getMods().get(0).getModId() + "/" + strPath); + }); + List urlList = urlsForClass.get(strPath); + if(urlList != null) { + if(urlList.size() > 1) + urlList.add(url); + else { + /* Convert singleton to real list */ + ArrayList newList = new ArrayList<>(urlList); + newList.add(url); + urlsForClass.put(strPath, newList); + } + } else { + /* Use a singleton list initially to keep memory usage down */ + urlsForClass.put(strPath, Collections.singletonList(url)); + } + }); + } catch(IOException e) { + throw new RuntimeException(e); + } + } } - for(ArrayList list : urlsForClass.values()) { - list.trimToSize(); + for(List list : urlsForClass.values()) { + if(list instanceof ArrayList) + ((ArrayList)list).trimToSize(); } + LOGGER.info("Finish building"); } + + private static Iterable getRootPathForLocator(IModLocator locator, ModFile file) throws ReflectiveOperationException { + if(locator instanceof AbstractJarFileLocator) { + FileSystem modFs = locator.findPath(file, ".").getFileSystem(); + return modFs.getRootDirectories(); + } else if (locator instanceof ExplodedDirectoryLocator) { + if(explodedDirModsField == null) { + explodedDirModsField = ExplodedDirectoryLocator.class.getDeclaredField("mods"); + explodedDirModsField.setAccessible(true); + } + Map>> mods = (Map>>)explodedDirModsField.get(locator); + return mods.get(file).getRight(); + } else if(MINECRAFT_LOCATOR.isAssignableFrom(locator.getClass())) { + Path mcJar = FMLLoader.getMCPaths()[0]; + if(Files.isDirectory(mcJar)) { + return mcJar; + } else { + return locator.findPath(file, ".").getFileSystem().getRootDirectories(); + } + } else + throw new UnsupportedOperationException("Unknown ModLocator type: " + locator.getClass().getName()); + } + public static Enumeration findAllURLsForResource(String input) { - ArrayList urlList = urlsForClass.get(input); + List urlList = urlsForClass.get(input); if(urlList != null) return Collections.enumeration(urlList); - else + else { return Collections.emptyEnumeration(); + } } } diff --git a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index 0ffbe37e..fcd33e12 100644 --- a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -1,6 +1,7 @@ package org.embeddedt.modernfix.core; import cpw.mods.modlauncher.*; +import cpw.mods.modlauncher.api.LamdbaExceptionUtils; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -15,6 +16,7 @@ import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; +import java.net.URLClassLoader; import java.util.*; import java.util.function.Function; @@ -25,6 +27,7 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { public static ModernFixEarlyConfig config = null; private static final boolean USE_TRANSFORMER_CACHE = false; + private static final boolean USE_CLASS_LOCATION_CACHE = false; public ModernFixMixinPlugin() { /* We abuse the constructor of a mixin plugin as a safe location to start modifying the classloader */ @@ -43,13 +46,20 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { TransformerAuditTrail trail = ObfuscationReflectionHelper.getPrivateValue(ClassTransformer.class, t, "auditTrail"); classTransformerField.set(loader, new ModernFixCachingClassTransformer(store, pluginHandler, (TransformingClassLoader)loader, trail)); } - Field resourceFinderField = TransformingClassLoader.class.getDeclaredField("resourceFinder"); - /* Construct a new list of resource finders, using similar logic to ML */ - resourceFinderField.setAccessible(true); - Function> resourceFinder = constructResourceFinder(); - resourceFinderField.set(loader, resourceFinder); - } catch(ReflectiveOperationException e) { - e.printStackTrace(); + if(USE_CLASS_LOCATION_CACHE) { + Field resourceFinderField = TransformingClassLoader.class.getDeclaredField("resourceFinder"); + /* Construct a new list of resource finders, using similar logic to ML */ + resourceFinderField.setAccessible(true); + Function> resourceFinder = constructResourceFinder(); + /* Merge with the findResources implementation provided by the DelegatedClassLoader */ + Field dclField = TransformingClassLoader.class.getDeclaredField("delegatedClassLoader"); + dclField.setAccessible(true); + URLClassLoader dcl = (URLClassLoader)dclField.get(loader); + resourceFinder = EnumerationHelper.mergeFunctors(resourceFinder, LamdbaExceptionUtils.rethrowFunction(dcl::findResources)); + resourceFinderField.set(loader, resourceFinder); + } + } catch(RuntimeException | ReflectiveOperationException e) { + logger.error("Failed to make classloading changes", e); } } @@ -66,10 +76,10 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { Function> resourceEnumeratorLocator = ModernFixResourceFinder::findAllURLsForResource; for(TransformationServiceDecorator decorator : serviceLookup.values()) { Function> func = (Function>)getClassLoaderMethod.invoke(decorator); - if(func != null) + if(func != null) { resourceEnumeratorLocator = EnumerationHelper.mergeFunctors(resourceEnumeratorLocator, EnumerationHelper.fromOptional(func)); + } } - System.out.println(EnumerationHelper.firstElementOrNull(resourceEnumeratorLocator.apply("net.minecraft.client.Minecraft"))); return resourceEnumeratorLocator; }