fix bugs, speed up processing by only reading modified classes
This commit is contained in:
parent
1fdb459433
commit
1dcd0c8c05
|
@ -1,3 +1,5 @@
|
||||||
accessWidener v2 named
|
accessWidener v2 named
|
||||||
|
|
||||||
accessible method net/minecraft/world/level/block/TransparentBlock codec ()Lcom/mojang/serialization/MapCodec;
|
accessible method net/minecraft/world/level/block/TransparentBlock codec ()Lcom/mojang/serialization/MapCodec;
|
||||||
|
mutable field net/minecraft/client/gui/GuiSpriteManager METADATA_SECTIONS Ljava/util/Set;
|
||||||
|
mutable field net/minecraft/client/gui/GuiGraphics$ScissorStack stack Ljava/util/Deque;
|
|
@ -3,21 +3,25 @@ package org.ecorous.esnesnon.nonsense.loader.impl.plugin.game.minecraft;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.file.*;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentSkipListSet;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.mod.BuiltinExtensions;
|
import org.ecorous.esnesnon.nonsense.loader.impl.mod.BuiltinExtensions;
|
||||||
import org.objectweb.asm.*;
|
import org.objectweb.asm.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal implementation of accesswideners.
|
* Minimal implementation of accesswideners.
|
||||||
* (Untested)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AWProcessor {
|
class AWProcessor {
|
||||||
|
@ -26,24 +30,26 @@ class AWProcessor {
|
||||||
private static final Predicate<String> HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate();
|
private static final Predicate<String> HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate();
|
||||||
private static final Predicate<String> COMMENT = Pattern.compile("^#.*").asMatchPredicate();
|
private static final Predicate<String> COMMENT = Pattern.compile("^#.*").asMatchPredicate();
|
||||||
private static final String SEPARATOR = "[\\t ]+";
|
private static final String SEPARATOR = "[\\t ]+";
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger("AccessWidener");
|
||||||
|
|
||||||
static void apply(Collection<ModProperties> mods, Path input, Path output) throws IOException {
|
static void apply(Collection<ModProperties> mods, Path input, Path output) throws IOException {
|
||||||
List<Entry> entries = mods.parallelStream().map(ModProperties::extensions).map(e -> (String)e.get(AW_EXTENSION_NAME))
|
long startTime = System.currentTimeMillis();
|
||||||
.filter(Objects::nonNull).map(s -> "/"+s).map(AWProcessor.class::getResourceAsStream).filter(Objects::nonNull)
|
Map<String, Entry> classMap = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Map<String, Entry>> methods = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Map<String, Entry>> fields = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Map<String, Entry>> mutations = new ConcurrentHashMap<>();
|
||||||
|
Set<String> classNames = new ConcurrentSkipListSet<>();
|
||||||
|
|
||||||
|
mods.parallelStream().map(ModProperties::extensions).map(e -> (String) e.get(AW_EXTENSION_NAME))
|
||||||
|
.filter(Objects::nonNull).map(s -> "/" + s).map(AWProcessor.class::getResourceAsStream).filter(Objects::nonNull)
|
||||||
.map(InputStreamReader::new).map(BufferedReader::new).flatMap(BufferedReader::lines)
|
.map(InputStreamReader::new).map(BufferedReader::new).flatMap(BufferedReader::lines)
|
||||||
.filter(l -> !l.isBlank()).filter(l -> !COMMENT.test(l)).filter(l -> !HEADER.test(l)).distinct()
|
.filter(l -> !l.isBlank()).filter(l -> !COMMENT.test(l)).filter(l -> !HEADER.test(l)).distinct()
|
||||||
.map(l -> l.replace("transitive-", "")) // ignore all transitive declarations (just make them normal) as they're only relevant for dev envs
|
.map(l -> l.replace("transitive-", "")) // ignore all transitive declarations (just make them normal) as they're only relevant for dev envs
|
||||||
.map(l -> l.split(SEPARATOR)).filter(l -> l.length > 0).map(Entry::new).toList();
|
.map(l -> l.split(SEPARATOR)).filter(l -> l.length > 0).map(Entry::new).forEach(e -> {
|
||||||
|
classNames.add(e.className);
|
||||||
System.out.println(entries.stream().map(Entry::toString).collect(Collectors.joining()));
|
|
||||||
Map<String, Entry> classMap = new HashMap<>();
|
|
||||||
Map<String, Map<String, Entry>> methods = new HashMap<>();
|
|
||||||
Map<String, Map<String, Entry>> fields = new HashMap<>();
|
|
||||||
Map<String, Map<String, Entry>> mutations = new HashMap<>();
|
|
||||||
entries.forEach(e -> {
|
|
||||||
if ("class".equals(e.targetType)) {
|
if ("class".equals(e.targetType)) {
|
||||||
if (e.type == AccessType.MUTABLE){
|
if (e.type == AccessType.MUTABLE) {
|
||||||
throw new IllegalArgumentException("aw format error: classes can not have a 'mutable' modifier (at: "+e+")");
|
throw new IllegalArgumentException("aw format error: classes can not have a 'mutable' modifier (at: " + e + ")");
|
||||||
}
|
}
|
||||||
if (!classMap.containsKey(e.className)) {
|
if (!classMap.containsKey(e.className)) {
|
||||||
classMap.put(e.className, e);
|
classMap.put(e.className, e);
|
||||||
|
@ -54,10 +60,10 @@ class AWProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ("method".equals(e.targetType)) {
|
} else if ("method".equals(e.targetType)) {
|
||||||
if (e.type == AccessType.MUTABLE){
|
if (e.type == AccessType.MUTABLE) {
|
||||||
throw new IllegalArgumentException("aw format error: methods can not have a 'mutable' modifier (at: "+e+")");
|
throw new IllegalArgumentException("aw format error: methods can not have a 'mutable' modifier (at: " + e + ")");
|
||||||
}
|
}
|
||||||
var map = methods.computeIfAbsent(e.className, s -> new HashMap<>());
|
var map = methods.computeIfAbsent(e.className, s -> new ConcurrentHashMap<>());
|
||||||
var id = e.name + e.descriptor;
|
var id = e.name + e.descriptor;
|
||||||
if (!map.containsKey(id)) {
|
if (!map.containsKey(id)) {
|
||||||
map.put(id, e);
|
map.put(id, e);
|
||||||
|
@ -68,13 +74,13 @@ class AWProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ("field".equals(e.targetType)) {
|
} else if ("field".equals(e.targetType)) {
|
||||||
if (e.type == AccessType.EXTENDABLE){
|
if (e.type == AccessType.EXTENDABLE) {
|
||||||
throw new IllegalArgumentException("aw format error: fields can not have a 'extendable' modifier (at: "+e+")");
|
throw new IllegalArgumentException("aw format error: fields can not have a 'extendable' modifier (at: " + e + ")");
|
||||||
}
|
}
|
||||||
var map = fields.computeIfAbsent(e.className, s -> new HashMap<>());
|
var map = fields.computeIfAbsent(e.className, s -> new ConcurrentHashMap<>());
|
||||||
var id = e.name + e.descriptor;
|
var id = e.name + e.descriptor;
|
||||||
if (e.type == AccessType.MUTABLE){
|
if (e.type == AccessType.MUTABLE) {
|
||||||
mutations.computeIfAbsent(e.className, s -> new HashMap<>()).putIfAbsent(id, e);
|
mutations.computeIfAbsent(e.className, s -> new ConcurrentHashMap<>()).putIfAbsent(id, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,15 +96,24 @@ class AWProcessor {
|
||||||
});
|
});
|
||||||
|
|
||||||
Files.deleteIfExists(output);
|
Files.deleteIfExists(output);
|
||||||
|
Files.copy(input, output);
|
||||||
|
|
||||||
try (FileSystem in = FileSystems.newFileSystem(input.toAbsolutePath());
|
if (!classNames.isEmpty()) {
|
||||||
FileSystem out = FileSystems.newFileSystem(output, Map.of("create", "true"))) {
|
try (FileSystem out = FileSystems.newFileSystem(output)) {
|
||||||
|
|
||||||
Files.walkFileTree(in.getPath("/"), new SimpleFileVisitor<>() {
|
classNames.parallelStream().forEach(name -> {
|
||||||
@Override
|
try {
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
processFile(out.getPath("/" + name + ".class"), classMap, fields, methods, mutations);
|
||||||
Path output = out.getPath(file.toString());
|
} catch (IOException e) {
|
||||||
Files.createDirectories(output.getParent());
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGGER.info(String.format("Applied AccessWideners in %.2fs", (System.currentTimeMillis() - startTime) / 1000f));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processFile(Path file, Map<String, Entry> classMap, Map<String, Map<String, Entry>> fields, Map<String, Map<String, Entry>> methods, Map<String, Map<String, Entry>> mutations) throws IOException {
|
||||||
var className = file.toString().substring(1, file.toString().length() - 6);
|
var className = file.toString().substring(1, file.toString().length() - 6);
|
||||||
if (file.getFileName().toString().endsWith(".class") && (classMap.containsKey(className) || fields.containsKey(className) || methods.containsKey(className) || mutations.containsKey(className))) {
|
if (file.getFileName().toString().endsWith(".class") && (classMap.containsKey(className) || fields.containsKey(className) || methods.containsKey(className) || mutations.containsKey(className))) {
|
||||||
ClassReader reader = new ClassReader(Files.newInputStream(file));
|
ClassReader reader = new ClassReader(Files.newInputStream(file));
|
||||||
|
@ -110,7 +125,8 @@ class AWProcessor {
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
||||||
access |= e.type.access;
|
access |= e.type.access;
|
||||||
} else if (fields.containsKey(className) || methods.containsKey(className) || mutations.containsKey(className)) { // make all classes with modifications public as well
|
}
|
||||||
|
if (fields.containsKey(className) || methods.containsKey(className) || mutations.containsKey(className)) { // make all classes with modifications public as well
|
||||||
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
||||||
access |= Opcodes.ACC_PUBLIC;
|
access |= Opcodes.ACC_PUBLIC;
|
||||||
}
|
}
|
||||||
|
@ -127,10 +143,10 @@ class AWProcessor {
|
||||||
access |= e.type.access; // re-add the new one
|
access |= e.type.access; // re-add the new one
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((map = mutations.get(className)) != null){
|
if ((map = mutations.get(className)) != null) {
|
||||||
var e = map.get(name+descriptor);
|
var e = map.get(name + descriptor);
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
access |= ~Opcodes.ACC_FINAL; // always AccessType.MUTABLE
|
access &= ~Opcodes.ACC_FINAL; // always AccessType.MUTABLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visitField(access, name, descriptor, signature, value);
|
return super.visitField(access, name, descriptor, signature, value);
|
||||||
|
@ -142,10 +158,8 @@ class AWProcessor {
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
Entry e = map.get(name + descriptor);
|
Entry e = map.get(name + descriptor);
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
int old = access;
|
|
||||||
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
||||||
access |= e.type.access;
|
access |= e.type.access;
|
||||||
System.out.println("set access of "+name+" from "+old+" to "+access);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
|
@ -154,13 +168,7 @@ class AWProcessor {
|
||||||
|
|
||||||
reader.accept(mapper, 0);
|
reader.accept(mapper, 0);
|
||||||
|
|
||||||
Files.write(output, writer.toByteArray());
|
Files.write(file, writer.toByteArray());
|
||||||
} else {
|
|
||||||
Files.copy(file, output);
|
|
||||||
}
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,8 +187,7 @@ class AWProcessor {
|
||||||
private enum AccessType {
|
private enum AccessType {
|
||||||
ACCESSIBLE("accessible", Opcodes.ACC_PUBLIC),
|
ACCESSIBLE("accessible", Opcodes.ACC_PUBLIC),
|
||||||
EXTENDABLE("extendable", Opcodes.ACC_PROTECTED),
|
EXTENDABLE("extendable", Opcodes.ACC_PROTECTED),
|
||||||
MUTABLE("mutable", ~Opcodes.ACC_FINAL)
|
MUTABLE("mutable", ~Opcodes.ACC_FINAL);
|
||||||
;
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final int access;
|
private final int access;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue