Implement class location cache
This commit is contained in:
parent
6dbf4b5a12
commit
d2f5c4b15a
|
|
@ -1,38 +1,110 @@
|
||||||
package org.embeddedt.modernfix.classloading;
|
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.LoadingModList;
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileLocator;
|
import net.minecraftforge.fml.loading.moddiscovery.*;
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator;
|
import net.minecraftforge.forgespi.language.IModInfo;
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
import net.minecraftforge.forgespi.locating.IModFile;
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
|
|
||||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
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.net.URL;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
import java.util.stream.Stream;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public class ModernFixResourceFinder {
|
public class ModernFixResourceFinder {
|
||||||
private static HashMap<String, ArrayList<URL>> urlsForClass = null;
|
private static HashMap<String, List<URL>> urlsForClass = null;
|
||||||
public static void init() {
|
private static final Class<? extends IModLocator> MINECRAFT_LOCATOR;
|
||||||
|
private static Field explodedDirModsField = null;
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger("ModernFixResourceFinder");
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
MINECRAFT_LOCATOR = (Class<? extends IModLocator>)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<>();
|
urlsForClass = new HashMap<>();
|
||||||
|
LOGGER.info("Start building list of class locations...");
|
||||||
for(ModFileInfo fileInfo : LoadingModList.get().getModFiles()) {
|
for(ModFileInfo fileInfo : LoadingModList.get().getModFiles()) {
|
||||||
ModFile file = fileInfo.getFile();
|
ModFile file = fileInfo.getFile();
|
||||||
IModLocator locator = file.getLocator();
|
IModLocator locator = file.getLocator();
|
||||||
Path rootPath = locator.findPath(file, ".");
|
Iterable<Path> rootPath = getRootPathForLocator(locator, file);
|
||||||
System.out.println(rootPath.getParent().toAbsolutePath());
|
for(Path root : rootPath) {
|
||||||
|
try(Stream<Path> 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<URL> urlList = urlsForClass.get(strPath);
|
||||||
|
if(urlList != null) {
|
||||||
|
if(urlList.size() > 1)
|
||||||
|
urlList.add(url);
|
||||||
|
else {
|
||||||
|
/* Convert singleton to real list */
|
||||||
|
ArrayList<URL> 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<URL> list : urlsForClass.values()) {
|
for(List<URL> list : urlsForClass.values()) {
|
||||||
list.trimToSize();
|
if(list instanceof ArrayList)
|
||||||
|
((ArrayList<URL>)list).trimToSize();
|
||||||
}
|
}
|
||||||
|
LOGGER.info("Finish building");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Iterable<Path> 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<IModFile, Pair<Path, List<Path>>> mods = (Map<IModFile, Pair<Path, List<Path>>>)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<URL> findAllURLsForResource(String input) {
|
public static Enumeration<URL> findAllURLsForResource(String input) {
|
||||||
ArrayList<URL> urlList = urlsForClass.get(input);
|
List<URL> urlList = urlsForClass.get(input);
|
||||||
if(urlList != null)
|
if(urlList != null)
|
||||||
return Collections.enumeration(urlList);
|
return Collections.enumeration(urlList);
|
||||||
else
|
else {
|
||||||
return Collections.emptyEnumeration();
|
return Collections.emptyEnumeration();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package org.embeddedt.modernfix.core;
|
package org.embeddedt.modernfix.core;
|
||||||
|
|
||||||
import cpw.mods.modlauncher.*;
|
import cpw.mods.modlauncher.*;
|
||||||
|
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
@ -15,6 +16,7 @@ import java.io.File;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
|
@ -25,6 +27,7 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
||||||
public static ModernFixEarlyConfig config = null;
|
public static ModernFixEarlyConfig config = null;
|
||||||
|
|
||||||
private static final boolean USE_TRANSFORMER_CACHE = false;
|
private static final boolean USE_TRANSFORMER_CACHE = false;
|
||||||
|
private static final boolean USE_CLASS_LOCATION_CACHE = false;
|
||||||
|
|
||||||
public ModernFixMixinPlugin() {
|
public ModernFixMixinPlugin() {
|
||||||
/* We abuse the constructor of a mixin plugin as a safe location to start modifying the classloader */
|
/* 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");
|
TransformerAuditTrail trail = ObfuscationReflectionHelper.getPrivateValue(ClassTransformer.class, t, "auditTrail");
|
||||||
classTransformerField.set(loader, new ModernFixCachingClassTransformer(store, pluginHandler, (TransformingClassLoader)loader, trail));
|
classTransformerField.set(loader, new ModernFixCachingClassTransformer(store, pluginHandler, (TransformingClassLoader)loader, trail));
|
||||||
}
|
}
|
||||||
Field resourceFinderField = TransformingClassLoader.class.getDeclaredField("resourceFinder");
|
if(USE_CLASS_LOCATION_CACHE) {
|
||||||
/* Construct a new list of resource finders, using similar logic to ML */
|
Field resourceFinderField = TransformingClassLoader.class.getDeclaredField("resourceFinder");
|
||||||
resourceFinderField.setAccessible(true);
|
/* Construct a new list of resource finders, using similar logic to ML */
|
||||||
Function<String, Enumeration<URL>> resourceFinder = constructResourceFinder();
|
resourceFinderField.setAccessible(true);
|
||||||
resourceFinderField.set(loader, resourceFinder);
|
Function<String, Enumeration<URL>> resourceFinder = constructResourceFinder();
|
||||||
} catch(ReflectiveOperationException e) {
|
/* Merge with the findResources implementation provided by the DelegatedClassLoader */
|
||||||
e.printStackTrace();
|
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<String, Enumeration<URL>> resourceEnumeratorLocator = ModernFixResourceFinder::findAllURLsForResource;
|
Function<String, Enumeration<URL>> resourceEnumeratorLocator = ModernFixResourceFinder::findAllURLsForResource;
|
||||||
for(TransformationServiceDecorator decorator : serviceLookup.values()) {
|
for(TransformationServiceDecorator decorator : serviceLookup.values()) {
|
||||||
Function<String, Optional<URL>> func = (Function<String, Optional<URL>>)getClassLoaderMethod.invoke(decorator);
|
Function<String, Optional<URL>> func = (Function<String, Optional<URL>>)getClassLoaderMethod.invoke(decorator);
|
||||||
if(func != null)
|
if(func != null) {
|
||||||
resourceEnumeratorLocator = EnumerationHelper.mergeFunctors(resourceEnumeratorLocator, EnumerationHelper.fromOptional(func));
|
resourceEnumeratorLocator = EnumerationHelper.mergeFunctors(resourceEnumeratorLocator, EnumerationHelper.fromOptional(func));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
System.out.println(EnumerationHelper.firstElementOrNull(resourceEnumeratorLocator.apply("net.minecraft.client.Minecraft")));
|
|
||||||
return resourceEnumeratorLocator;
|
return resourceEnumeratorLocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user