Store classes in separate files
This commit is contained in:
parent
277ea384e7
commit
15524d975d
|
|
@ -6,6 +6,7 @@ import java.lang.reflect.Field;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
|
||||||
import cpw.mods.modlauncher.api.ITransformer;
|
import cpw.mods.modlauncher.api.ITransformer;
|
||||||
import cpw.mods.modlauncher.api.ITransformerActivity;
|
import cpw.mods.modlauncher.api.ITransformerActivity;
|
||||||
|
|
@ -30,16 +31,15 @@ import javax.lang.model.SourceVersion;
|
||||||
public class ModernFixCachingClassTransformer extends ClassTransformer {
|
public class ModernFixCachingClassTransformer extends ClassTransformer {
|
||||||
private static final Logger LOGGER = LogManager.getLogger("ModernFixCachingTransformer");
|
private static final Logger LOGGER = LogManager.getLogger("ModernFixCachingTransformer");
|
||||||
|
|
||||||
private final File CLASS_CACHE_DAT = childFile(FMLPaths.GAMEDIR.get().resolve("modernfix").resolve("classTransformerV1.cache").toFile());
|
private final File CLASS_CACHE_FOLDER = childFile(FMLPaths.GAMEDIR.get().resolve("modernfix").resolve("classCacheV1").toFile());
|
||||||
private final LaunchPluginHandler pluginHandler;
|
private final LaunchPluginHandler pluginHandler;
|
||||||
private final TransformStore transformStore;
|
private final TransformStore transformStore;
|
||||||
private final TransformerAuditTrail auditTrail;
|
private final TransformerAuditTrail auditTrail;
|
||||||
private final TransformingClassLoader transformingClassLoader;
|
private final TransformingClassLoader transformingClassLoader;
|
||||||
private final HashMap<String, List<ITransformer<?>>> transformersByClass;
|
private final HashMap<String, List<ITransformer<?>>> transformersByClass;
|
||||||
|
|
||||||
private static final int MAX_NUM_CLASSES = 10000;
|
|
||||||
|
|
||||||
private ConcurrentHashMap<String, Pair<List<byte[]>, byte[]>> transformationCache;
|
private ConcurrentHashMap<String, Pair<List<byte[]>, byte[]>> transformationCache;
|
||||||
|
private ForkJoinPool classSaverPool = ForkJoinPool.commonPool();
|
||||||
|
|
||||||
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||||
|
|
||||||
|
|
@ -48,14 +48,6 @@ public class ModernFixCachingClassTransformer extends ClassTransformer {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isValidClassName(String className) {
|
|
||||||
final String DOT_PACKAGE_INFO = ".package-info";
|
|
||||||
if(className.endsWith(DOT_PACKAGE_INFO)) {
|
|
||||||
className = className.substring(0, className.length() - DOT_PACKAGE_INFO.length());
|
|
||||||
}
|
|
||||||
return SourceVersion.isName(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModernFixCachingClassTransformer(TransformStore transformStore, LaunchPluginHandler pluginHandler, TransformingClassLoader transformingClassLoader, TransformerAuditTrail trail) {
|
public ModernFixCachingClassTransformer(TransformStore transformStore, LaunchPluginHandler pluginHandler, TransformingClassLoader transformingClassLoader, TransformerAuditTrail trail) {
|
||||||
super(transformStore, pluginHandler, transformingClassLoader, trail);
|
super(transformStore, pluginHandler, transformingClassLoader, trail);
|
||||||
this.transformStore = transformStore;
|
this.transformStore = transformStore;
|
||||||
|
|
@ -84,43 +76,6 @@ public class ModernFixCachingClassTransformer extends ClassTransformer {
|
||||||
} catch(ReflectiveOperationException e) {
|
} catch(ReflectiveOperationException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
this.transformationCache = new ConcurrentHashMap<>();
|
|
||||||
try(ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(CLASS_CACHE_DAT))) {
|
|
||||||
this.transformationCache = (ConcurrentHashMap<String, Pair<List<byte[]>,byte[]>>)inStream.readObject();
|
|
||||||
/* Deduplicate any empty byte arrays to minimize impact of empty classes */
|
|
||||||
List<String> keys = new ArrayList<>(this.transformationCache.keySet());
|
|
||||||
for(String key : keys) {
|
|
||||||
Pair<List<byte[]>,byte[]> pair = this.transformationCache.get(key);
|
|
||||||
for(int i = 0; i < pair.getLeft().size(); i++) {
|
|
||||||
if(pair.getLeft().get(i).length == 0) {
|
|
||||||
pair.getLeft().set(i, EMPTY_BYTE_ARRAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(pair.getRight().length == 0) {
|
|
||||||
this.transformationCache.put(key, Pair.of(pair.getLeft(), EMPTY_BYTE_ARRAY));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int size = 0;
|
|
||||||
/* Approximate the size in bytes */
|
|
||||||
for(Map.Entry<String, Pair<List<byte[]>,byte[]>> entry : this.transformationCache.entrySet()) {
|
|
||||||
size += entry.getKey().length();
|
|
||||||
size += entry.getValue().getRight().length;
|
|
||||||
for(byte[] hash : entry.getValue().getLeft()) {
|
|
||||||
size += hash.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOGGER.info("Loaded transformer cache, contains " + this.transformationCache.size() + " classes and in-memory size is approximately " + FileUtils.byteCountToDisplaySize(size));
|
|
||||||
} catch(IOException | ClassNotFoundException e) {
|
|
||||||
LOGGER.error("An error occured while loading transform cache", e);
|
|
||||||
}
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
||||||
LOGGER.info("Serializing transform cache to disk");
|
|
||||||
try(ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream(CLASS_CACHE_DAT))) {
|
|
||||||
outStream.writeObject(transformationCache);
|
|
||||||
} catch(IOException e) {
|
|
||||||
LOGGER.error("An error occured while serializing transform cache", e);
|
|
||||||
}
|
|
||||||
}, "ModernFix transformer shutdown thread"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -169,28 +124,38 @@ public class ModernFixCachingClassTransformer extends ClassTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Check if the cache contains a transformed class matching these hashes */
|
/* Check if the cache contains a transformed class matching these hashes */
|
||||||
Pair<List<byte[]>, byte[]> oldPair = transformationCache.get(className);
|
/* TODO maybe sanitize the class name? */
|
||||||
|
File cacheLocation = new File(CLASS_CACHE_FOLDER, className.replace('.', '/'));
|
||||||
boolean hashesMatch = true;
|
boolean hashesMatch = true;
|
||||||
if(oldPair == null || oldPair.getLeft().size() != hashList.size()) {
|
try(ObjectInputStream stream = new ObjectInputStream(new FileInputStream(cacheLocation))) {
|
||||||
hashesMatch = false;
|
ArrayList<byte[]> savedHash = (ArrayList<byte[]>)stream.readObject();
|
||||||
} else {
|
for(int i = 0; i < savedHash.size(); i++) {
|
||||||
for(int i = 0; i < oldPair.getLeft().size(); i++) {
|
if(!Arrays.equals(savedHash.get(i), hashList.get(i))) {
|
||||||
if(!Arrays.equals(oldPair.getLeft().get(i), hashList.get(i))) {
|
|
||||||
hashesMatch = false;
|
hashesMatch = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(hashesMatch)
|
||||||
|
inputClass = (byte[])stream.readObject();
|
||||||
|
} catch(IOException | ClassNotFoundException | ClassCastException e) {
|
||||||
|
if(!(e instanceof FileNotFoundException))
|
||||||
|
e.printStackTrace();
|
||||||
|
hashesMatch = false;
|
||||||
}
|
}
|
||||||
if(!hashesMatch) {
|
if(!hashesMatch) {
|
||||||
if(oldPair != null) {
|
inputClass = super.transform(inputClass, className, reason);
|
||||||
LOGGER.warn("Hashes have changed, discarding cached version of " + className);
|
final byte[] classToSave = inputClass;
|
||||||
}
|
classSaverPool.submit(() -> {
|
||||||
byte[] transformed = super.transform(inputClass, className, reason);
|
cacheLocation.getParentFile().mkdirs();
|
||||||
if(transformed.length == 0)
|
try(ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(cacheLocation))) {
|
||||||
transformed = EMPTY_BYTE_ARRAY; /* deduplicate */
|
stream.writeObject(hashList);
|
||||||
oldPair = Pair.of(hashList, transformed);
|
stream.writeObject(classToSave);
|
||||||
transformationCache.put(className, oldPair);
|
} catch(IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return oldPair.getRight();
|
return inputClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final byte[] FORGE_HASH = LoadingModList.get().getModFileById("forge").getMods().get(0).getVersion().toString().getBytes(StandardCharsets.UTF_8);
|
private final byte[] FORGE_HASH = LoadingModList.get().getModFileById("forge").getMods().get(0).getVersion().toString().getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user