Implement plugin class cache
Plugins are expected to load faster on second run.
This commit is contained in:
parent
831b37b58d
commit
81b5b4812a
|
@ -23,6 +23,7 @@ import java.net.URLConnection;
|
||||||
import java.security.CodeSigner;
|
import java.security.CodeSigner;
|
||||||
import java.security.CodeSource;
|
import java.security.CodeSource;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
@Mixin(targets = "org.bukkit.plugin.java.PluginClassLoader", remap = false)
|
@Mixin(targets = "org.bukkit.plugin.java.PluginClassLoader", remap = false)
|
||||||
|
@ -71,29 +72,31 @@ public class PluginClassLoaderMixin extends URLClassLoader implements RemappingC
|
||||||
URL url = this.findResource(path);
|
URL url = this.findResource(path);
|
||||||
|
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
byte[] classBytes;
|
|
||||||
|
|
||||||
URLConnection connection;
|
URLConnection connection;
|
||||||
CodeSigner[] signers;
|
CodeSigner[] signers;
|
||||||
|
Callable<byte[]> byteSource;
|
||||||
try {
|
try {
|
||||||
connection = url.openConnection();
|
connection = url.openConnection();
|
||||||
try (InputStream is = connection.getInputStream()) {
|
connection.connect();
|
||||||
classBytes = ByteStreams.toByteArray(is);
|
if (connection instanceof JarURLConnection) {
|
||||||
if (connection instanceof JarURLConnection) {
|
signers = ((JarURLConnection) connection).getJarEntry().getCodeSigners();
|
||||||
signers = ((JarURLConnection) connection).getJarEntry().getCodeSigners();
|
} else {
|
||||||
} else {
|
signers = new CodeSigner[0];
|
||||||
signers = new CodeSigner[0];
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new ClassNotFoundException(name, ex);
|
|
||||||
}
|
}
|
||||||
|
byteSource = () -> {
|
||||||
|
try (InputStream is = connection.getInputStream()) {
|
||||||
|
byte[] classBytes = ByteStreams.toByteArray(is);
|
||||||
|
classBytes = SwitchTableFixer.INSTANCE.processClass(classBytes);
|
||||||
|
classBytes = Bukkit.getUnsafe().processClass(description, path, classBytes);
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
};
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new ClassNotFoundException(name, e);
|
throw new ClassNotFoundException(name, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
classBytes = SwitchTableFixer.INSTANCE.processClass(classBytes);
|
byte[] classBytes = this.getRemapper().remapClass(name, byteSource, connection);
|
||||||
classBytes = Bukkit.getUnsafe().processClass(description, path, classBytes);
|
|
||||||
classBytes = this.getRemapper().remapClass(classBytes);
|
|
||||||
|
|
||||||
int dot = name.lastIndexOf('.');
|
int dot = name.lastIndexOf('.');
|
||||||
if (dot != -1) {
|
if (dot != -1) {
|
||||||
|
|
|
@ -0,0 +1,262 @@
|
||||||
|
package io.izzel.arclight.common.mod.util.remapper;
|
||||||
|
|
||||||
|
import com.google.common.hash.Hasher;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
|
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||||
|
import io.izzel.arclight.common.mod.ArclightMod;
|
||||||
|
import io.izzel.arclight.i18n.ArclightConfig;
|
||||||
|
import io.izzel.tools.product.Product;
|
||||||
|
import io.izzel.tools.product.Product2;
|
||||||
|
import io.izzel.tools.product.Product4;
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.logging.log4j.Marker;
|
||||||
|
import org.apache.logging.log4j.MarkerManager;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.JarURLConnection;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
|
public abstract class ArclightClassCache implements AutoCloseable {
|
||||||
|
|
||||||
|
public abstract CacheSegment makeSegment(URLConnection connection) throws IOException;
|
||||||
|
|
||||||
|
public abstract void save() throws IOException;
|
||||||
|
|
||||||
|
public interface CacheSegment {
|
||||||
|
|
||||||
|
Optional<byte[]> findByName(String name) throws IOException;
|
||||||
|
|
||||||
|
void addToCache(String name, byte[] value);
|
||||||
|
|
||||||
|
void save() throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Marker MARKER = MarkerManager.getMarker("CLCACHE");
|
||||||
|
private static final ArclightClassCache INSTANCE = new Impl();
|
||||||
|
|
||||||
|
public static ArclightClassCache instance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Impl extends ArclightClassCache {
|
||||||
|
|
||||||
|
private final boolean enabled = ArclightConfig.spec().getOptimization().isCachePluginClass();
|
||||||
|
private final ConcurrentHashMap<String, JarSegment> map = new ConcurrentHashMap<>();
|
||||||
|
private final Path basePath = Paths.get(".arclight/class_cache");
|
||||||
|
private ScheduledExecutorService executor;
|
||||||
|
|
||||||
|
public Impl() {
|
||||||
|
if (!enabled) return;
|
||||||
|
executor = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||||
|
Thread thread = new Thread(r);
|
||||||
|
thread.setName("arclight class cache saving thread");
|
||||||
|
thread.setDaemon(true);
|
||||||
|
return thread;
|
||||||
|
});
|
||||||
|
executor.scheduleWithFixedDelay(() -> {
|
||||||
|
try {
|
||||||
|
this.save();
|
||||||
|
} catch (IOException e) {
|
||||||
|
ArclightMod.LOGGER.error(MARKER, "Failed to save class cache", e);
|
||||||
|
}
|
||||||
|
}, 1, 10, TimeUnit.MINUTES);
|
||||||
|
try {
|
||||||
|
if (Files.isRegularFile(basePath)) {
|
||||||
|
Files.delete(basePath);
|
||||||
|
}
|
||||||
|
if (!Files.isDirectory(basePath)) {
|
||||||
|
Files.createDirectories(basePath);
|
||||||
|
}
|
||||||
|
String current = ModList.get().getModContainerById("arclight")
|
||||||
|
.orElseThrow(IllegalStateException::new).getModInfo().getVersion().toString();
|
||||||
|
String store;
|
||||||
|
Path version = basePath.resolve(".version");
|
||||||
|
if (Files.exists(version)) {
|
||||||
|
store = new String(Files.readAllBytes(version), StandardCharsets.UTF_8);
|
||||||
|
} else {
|
||||||
|
store = null;
|
||||||
|
}
|
||||||
|
boolean obsolete = !Objects.equals(current, store);
|
||||||
|
Path index = basePath.resolve("index");
|
||||||
|
if (obsolete) {
|
||||||
|
FileUtils.deleteDirectory(index.toFile());
|
||||||
|
}
|
||||||
|
if (!Files.exists(index)) {
|
||||||
|
Files.createDirectories(index);
|
||||||
|
}
|
||||||
|
Path blob = basePath.resolve("blob");
|
||||||
|
if (obsolete) {
|
||||||
|
FileUtils.deleteDirectory(blob.toFile());
|
||||||
|
}
|
||||||
|
if (!Files.exists(blob)) {
|
||||||
|
Files.createDirectories(blob);
|
||||||
|
}
|
||||||
|
if (obsolete) {
|
||||||
|
Files.write(version, current.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
|
||||||
|
ArclightMod.LOGGER.info(MARKER, "Obsolete plugin class cache is cleared");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
ArclightMod.LOGGER.error(MARKER, "Failed to initialize class cache", e);
|
||||||
|
}
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
this.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ArclightMod.LOGGER.error(MARKER, "Failed to close class cache", e);
|
||||||
|
}
|
||||||
|
}, "arclight class cache cleanup");
|
||||||
|
thread.setDaemon(true);
|
||||||
|
Runtime.getRuntime().addShutdownHook(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CacheSegment makeSegment(URLConnection connection) throws IOException {
|
||||||
|
if (enabled && connection instanceof JarURLConnection) {
|
||||||
|
JarFile file = ((JarURLConnection) connection).getJarFile();
|
||||||
|
return this.map.computeIfAbsent(file.getName(), LamdbaExceptionUtils.rethrowFunction(JarSegment::new));
|
||||||
|
} else {
|
||||||
|
return new EmptySegment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() throws IOException {
|
||||||
|
if (enabled) {
|
||||||
|
for (CacheSegment segment : map.values()) {
|
||||||
|
segment.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
if (enabled) {
|
||||||
|
save();
|
||||||
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class JarSegment implements CacheSegment {
|
||||||
|
|
||||||
|
private final Map<String, Product2<Long, Integer>> rangeMap = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentLinkedQueue<Product4<String, byte[], Long, Integer>> savingQueue = new ConcurrentLinkedQueue<>();
|
||||||
|
private final AtomicLong sizeAllocator;
|
||||||
|
private final Path indexPath, blobPath;
|
||||||
|
|
||||||
|
private JarSegment(String fileName) throws IOException {
|
||||||
|
Path jarFile = new File(fileName).toPath();
|
||||||
|
Hasher hasher = Hashing.sha256().newHasher();
|
||||||
|
hasher.putBytes(Files.readAllBytes(jarFile));
|
||||||
|
String hash = hasher.hash().toString();
|
||||||
|
this.indexPath = basePath.resolve("index").resolve(hash);
|
||||||
|
this.blobPath = basePath.resolve("blob").resolve(hash);
|
||||||
|
if (!Files.exists(indexPath)) {
|
||||||
|
Files.createFile(indexPath);
|
||||||
|
}
|
||||||
|
if (!Files.exists(blobPath)) {
|
||||||
|
Files.createFile(blobPath);
|
||||||
|
}
|
||||||
|
sizeAllocator = new AtomicLong(Files.size(blobPath));
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> findByName(String name) throws IOException {
|
||||||
|
Product2<Long, Integer> product2 = rangeMap.get(name);
|
||||||
|
if (product2 != null) {
|
||||||
|
long off = product2._1;
|
||||||
|
int len = product2._2;
|
||||||
|
try (SeekableByteChannel channel = Files.newByteChannel(blobPath)) {
|
||||||
|
channel.position(off);
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(len);
|
||||||
|
channel.read(buffer);
|
||||||
|
return Optional.of(buffer.array());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToCache(String name, byte[] value) {
|
||||||
|
int len = value.length;
|
||||||
|
long off = sizeAllocator.getAndAdd(len);
|
||||||
|
savingQueue.add(Product.of(name, value, off, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void save() throws IOException {
|
||||||
|
if (savingQueue.isEmpty()) return;
|
||||||
|
List<Product4<String, byte[], Long, Integer>> list = new ArrayList<>();
|
||||||
|
while (!savingQueue.isEmpty()) {
|
||||||
|
list.add(savingQueue.poll());
|
||||||
|
}
|
||||||
|
try (OutputStream outIndex = Files.newOutputStream(indexPath, StandardOpenOption.APPEND);
|
||||||
|
DataOutputStream dataOutIndex = new DataOutputStream(outIndex);
|
||||||
|
SeekableByteChannel channel = Files.newByteChannel(blobPath, StandardOpenOption.WRITE)) {
|
||||||
|
for (Product4<String, byte[], Long, Integer> product4 : list) {
|
||||||
|
channel.position(product4._3);
|
||||||
|
channel.write(ByteBuffer.wrap(product4._2));
|
||||||
|
dataOutIndex.writeUTF(product4._1);
|
||||||
|
dataOutIndex.writeLong(product4._3);
|
||||||
|
dataOutIndex.writeInt(product4._4);
|
||||||
|
rangeMap.put(product4._1, Product.of(product4._3, product4._4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void read() throws IOException {
|
||||||
|
try (InputStream inputStream = Files.newInputStream(indexPath);
|
||||||
|
DataInputStream dataIn = new DataInputStream(inputStream)) {
|
||||||
|
while (dataIn.available() > 0) {
|
||||||
|
String name = dataIn.readUTF();
|
||||||
|
long off = dataIn.readLong();
|
||||||
|
int len = dataIn.readInt();
|
||||||
|
rangeMap.put(name, Product.of(off, len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EmptySegment implements CacheSegment {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> findByName(String name) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToCache(String name, byte[] value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import com.google.common.collect.HashBiMap;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import io.izzel.arclight.api.Unsafe;
|
import io.izzel.arclight.api.Unsafe;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.generated.ArclightReflectionHandler;
|
import io.izzel.arclight.common.mod.util.remapper.generated.ArclightReflectionHandler;
|
||||||
|
import io.izzel.arclight.i18n.ArclightConfig;
|
||||||
import net.md_5.specialsource.JarMapping;
|
import net.md_5.specialsource.JarMapping;
|
||||||
import net.md_5.specialsource.JarRemapper;
|
import net.md_5.specialsource.JarRemapper;
|
||||||
import net.md_5.specialsource.RemappingClassAdapter;
|
import net.md_5.specialsource.RemappingClassAdapter;
|
||||||
|
@ -27,12 +28,15 @@ 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.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.net.URLConnection;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@ -40,12 +44,14 @@ public class ClassLoaderRemapper extends LenientJarRemapper {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger("Arclight");
|
private static final Logger LOGGER = LogManager.getLogger("Arclight");
|
||||||
private static final String PREFIX = "net/minecraft/";
|
private static final String PREFIX = "net/minecraft/";
|
||||||
|
private static final String REPLACED_NAME = Type.getInternalName(ArclightReflectionHandler.class);
|
||||||
|
|
||||||
private final JarMapping toBukkitMapping;
|
private final JarMapping toBukkitMapping;
|
||||||
private final JarRemapper toBukkitRemapper;
|
private final JarRemapper toBukkitRemapper;
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
private final String generatedHandler;
|
private final String generatedHandler;
|
||||||
private final Class<?> generatedHandlerClass;
|
private final Class<?> generatedHandlerClass;
|
||||||
|
private final GeneratedHandlerAdapter generatedHandlerAdapter;
|
||||||
|
|
||||||
public ClassLoaderRemapper(JarMapping jarMapping, JarMapping toBukkitMapping, ClassLoader classLoader) {
|
public ClassLoaderRemapper(JarMapping jarMapping, JarMapping toBukkitMapping, ClassLoader classLoader) {
|
||||||
super(jarMapping);
|
super(jarMapping);
|
||||||
|
@ -57,6 +63,7 @@ public class ClassLoaderRemapper extends LenientJarRemapper {
|
||||||
this.toBukkitRemapper = new LenientJarRemapper(this.toBukkitMapping);
|
this.toBukkitRemapper = new LenientJarRemapper(this.toBukkitMapping);
|
||||||
this.generatedHandlerClass = generateReflectionHandler();
|
this.generatedHandlerClass = generateReflectionHandler();
|
||||||
this.generatedHandler = Type.getInternalName(generatedHandlerClass);
|
this.generatedHandler = Type.getInternalName(generatedHandlerClass);
|
||||||
|
this.generatedHandlerAdapter = new GeneratedHandlerAdapter(REPLACED_NAME, generatedHandler);
|
||||||
GlobalClassRepo.INSTANCE.addRepo(new ClassLoaderRepo(this.classLoader));
|
GlobalClassRepo.INSTANCE.addRepo(new ClassLoaderRepo(this.classLoader));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,8 +279,28 @@ public class ClassLoaderRemapper extends LenientJarRemapper {
|
||||||
return Maps.immutableEntry(owner, mapped);
|
return Maps.immutableEntry(owner, mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] remapClass(byte[] arr) {
|
public byte[] remapClass(String className, Callable<byte[]> byteSource, URLConnection connection) throws ClassNotFoundException {
|
||||||
return remapClassFile(arr, GlobalClassRepo.INSTANCE);
|
try {
|
||||||
|
ArclightClassCache.CacheSegment segment = ArclightClassCache.instance().makeSegment(connection);
|
||||||
|
Optional<byte[]> optional = segment.findByName(className);
|
||||||
|
if (optional.isPresent()) {
|
||||||
|
byte[] bytes = optional.get();
|
||||||
|
ClassWriter cw = new ClassWriter(0);
|
||||||
|
new ClassReader(bytes).accept(new ClassRemapper(cw, generatedHandlerAdapter), 0);
|
||||||
|
return cw.toByteArray();
|
||||||
|
} else {
|
||||||
|
byte[] bytes = remapClassFile(byteSource.call(), GlobalClassRepo.INSTANCE);
|
||||||
|
if (ArclightConfig.spec().getOptimization().isCachePluginClass()) {
|
||||||
|
ClassWriter cw = new ClassWriter(0);
|
||||||
|
new ClassReader(bytes).accept(new ClassRemapper(cw, new GeneratedHandlerAdapter(generatedHandler, REPLACED_NAME)), 0);
|
||||||
|
byte[] store = cw.toByteArray();
|
||||||
|
segment.addToCache(className, store);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ClassNotFoundException(className, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -368,7 +395,25 @@ public class ClassLoaderRemapper extends LenientJarRemapper {
|
||||||
}
|
}
|
||||||
return ArclightRemapper.getNmsMapper().map(node.superName);
|
return ArclightRemapper.getNmsMapper().map(node.superName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GeneratedHandlerAdapter extends Remapper {
|
||||||
|
|
||||||
|
private final String from, to;
|
||||||
|
|
||||||
|
private GeneratedHandlerAdapter(String from, String to) {
|
||||||
|
this.from = from;
|
||||||
|
this.to = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String map(String internalName) {
|
||||||
|
if (from.equals(internalName)) {
|
||||||
|
return to;
|
||||||
|
} else {
|
||||||
|
return internalName;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class WrappedMethod {
|
static class WrappedMethod {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import io.izzel.arclight.api.ArclightVersion;
|
||||||
import io.izzel.arclight.api.Unsafe;
|
import io.izzel.arclight.api.Unsafe;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.ArclightRedirectAdapter;
|
import io.izzel.arclight.common.mod.util.remapper.ArclightRedirectAdapter;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper;
|
import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper;
|
||||||
|
import io.izzel.arclight.common.mod.util.remapper.GlobalClassRepo;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.RemappingClassLoader;
|
import io.izzel.arclight.common.mod.util.remapper.RemappingClassLoader;
|
||||||
import io.izzel.arclight.common.util.Enumerations;
|
import io.izzel.arclight.common.util.Enumerations;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
|
@ -471,7 +472,7 @@ public class ArclightReflectionHandler extends ClassLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rcl != null) {
|
if (rcl != null) {
|
||||||
return rcl.getRemapper().remapClass(bytes);
|
return rcl.getRemapper().remapClassFile(bytes, GlobalClassRepo.INSTANCE);
|
||||||
} else {
|
} else {
|
||||||
ArclightRedirectAdapter.scanMethod(bytes);
|
ArclightRedirectAdapter.scanMethod(bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
|
|
|
@ -3,6 +3,7 @@ package io.izzel.arclight.common.mod.util.remapper.generated;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper;
|
import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper;
|
import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper;
|
||||||
|
import io.izzel.arclight.common.mod.util.remapper.GlobalClassRepo;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.RemappingClassLoader;
|
import io.izzel.arclight.common.mod.util.remapper.RemappingClassLoader;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -39,7 +40,7 @@ public class RemappingURLClassLoader extends URLClassLoader implements Remapping
|
||||||
if (resource != null) {
|
if (resource != null) {
|
||||||
try {
|
try {
|
||||||
URLConnection connection = resource.openConnection();
|
URLConnection connection = resource.openConnection();
|
||||||
byte[] bytes = getRemapper().remapClass(ByteStreams.toByteArray(connection.getInputStream()));
|
byte[] bytes = getRemapper().remapClassFile(ByteStreams.toByteArray(connection.getInputStream()), GlobalClassRepo.INSTANCE);
|
||||||
int i = name.lastIndexOf('.');
|
int i = name.lastIndexOf('.');
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
String pkgName = name.substring(0, i);
|
String pkgName = name.substring(0, i);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
allprojects {
|
allprojects {
|
||||||
group 'io.izzel.arclight'
|
group 'io.izzel.arclight'
|
||||||
version '1.0.17'
|
version '1.0.18-SNAPSHOT'
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
agpVersion = '1.15'
|
agpVersion = '1.15'
|
||||||
|
|
|
@ -9,7 +9,14 @@ public class OptimizationSpec {
|
||||||
@Setting("remove-stream")
|
@Setting("remove-stream")
|
||||||
private boolean removeStream;
|
private boolean removeStream;
|
||||||
|
|
||||||
|
@Setting("cache-plugin-class")
|
||||||
|
private boolean cachePluginClass;
|
||||||
|
|
||||||
public boolean isRemoveStream() {
|
public boolean isRemoveStream() {
|
||||||
return removeStream;
|
return removeStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCachePluginClass() {
|
||||||
|
return cachePluginClass;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ locale {
|
||||||
}
|
}
|
||||||
optimization {
|
optimization {
|
||||||
remove-stream = true
|
remove-stream = true
|
||||||
|
cache-plugin-class = true
|
||||||
}
|
}
|
||||||
compatibility {
|
compatibility {
|
||||||
material-property-overrides {
|
material-property-overrides {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user