refactor accesswidening to be on-demand during classloading
This commit is contained in:
parent
0bc8d555d0
commit
de57a631a2
|
@ -0,0 +1,147 @@
|
||||||
|
package org.ecorous.esnesnon.nonsense.loader.impl.launch;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentSkipListSet;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
|
public class AccessWidener {
|
||||||
|
private static final AccessWidener INSTANCE = new AccessWidener();
|
||||||
|
|
||||||
|
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<>();
|
||||||
|
|
||||||
|
private static AccessWidener get() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void load(Data data) {
|
||||||
|
get().loadFromData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFromData(Data data){
|
||||||
|
classMap.putAll(data.classMap);
|
||||||
|
methods.putAll(data.methods);
|
||||||
|
fields.putAll(data.fields);
|
||||||
|
mutations.putAll(data.mutations);
|
||||||
|
classNames.addAll(data.classNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] processClass(byte[] classBytes, String className) {
|
||||||
|
return get().process(classBytes, className);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] process(byte[] classBytes, String className) {
|
||||||
|
if (!classNames.contains(className)){
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassReader reader = new ClassReader(classBytes);
|
||||||
|
ClassWriter writer = new ClassWriter(0);
|
||||||
|
ClassVisitor mapper = new ClassVisitor(Opcodes.ASM9, writer) {
|
||||||
|
@Override
|
||||||
|
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||||
|
Entry e = classMap.get(className);
|
||||||
|
if (e != null) {
|
||||||
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
||||||
|
access |= e.type.access;
|
||||||
|
}
|
||||||
|
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_PUBLIC;
|
||||||
|
}
|
||||||
|
super.visit(version, access, name, signature, superName, interfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
||||||
|
Map<String, Entry> map = fields.get(className);
|
||||||
|
if (map != null) {
|
||||||
|
Entry e = map.get(name + descriptor);
|
||||||
|
if (e != null) {
|
||||||
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC); // remove all access modifiers
|
||||||
|
access |= e.type.access; // re-add the new one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((map = mutations.get(className)) != null) {
|
||||||
|
var e = map.get(name + descriptor);
|
||||||
|
if (e != null) {
|
||||||
|
access &= ~Opcodes.ACC_FINAL; // always AccessType.MUTABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visitField(access, name, descriptor, signature, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||||
|
Map<String, Entry> map = methods.get(className);
|
||||||
|
if (map != null) {
|
||||||
|
Entry e = map.get(name + descriptor);
|
||||||
|
if (e != null) {
|
||||||
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
||||||
|
access |= e.type.access;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.accept(mapper, 0);
|
||||||
|
return writer.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Data(Map<String, Entry> classMap,
|
||||||
|
Map<String, Map<String, Entry>> methods,
|
||||||
|
Map<String, Map<String, Entry>> fields,
|
||||||
|
Map<String, Map<String, Entry>> mutations,
|
||||||
|
Set<String> classNames) {}
|
||||||
|
|
||||||
|
public record Entry(AccessType type, String targetType, String className, String name, String descriptor) {
|
||||||
|
|
||||||
|
public Entry(String[] line) {
|
||||||
|
this(AccessType.of(line[0]), line[1], line[2], line[3], line[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAccessGreaterThan(Entry other) {
|
||||||
|
return Access.of(type().access).index < Access.of(other.type.access).index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum AccessType {
|
||||||
|
ACCESSIBLE("accessible", Opcodes.ACC_PUBLIC),
|
||||||
|
EXTENDABLE("extendable", Opcodes.ACC_PROTECTED),
|
||||||
|
MUTABLE("mutable", ~Opcodes.ACC_FINAL);
|
||||||
|
private final String id;
|
||||||
|
private final int access;
|
||||||
|
|
||||||
|
public static AccessType of(String name) {
|
||||||
|
return Arrays.stream(values()).filter(a -> a.id.equals(name)).findFirst().orElseThrow(() -> new IllegalStateException("Unknown access type: " + name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
private enum Access {
|
||||||
|
PUBLIC(1), PROTECTED(2), PACKAGE_PRIVATE(3), PRIVATE(4);
|
||||||
|
private final int index;
|
||||||
|
|
||||||
|
public static Access of(int access) {
|
||||||
|
if ((access & Opcodes.ACC_PUBLIC) != 0) {
|
||||||
|
return PUBLIC;
|
||||||
|
}
|
||||||
|
if ((access & Opcodes.ACC_PROTECTED) != 0) {
|
||||||
|
return PROTECTED;
|
||||||
|
}
|
||||||
|
if ((access & Opcodes.ACC_PRIVATE) != 0) {
|
||||||
|
return PRIVATE;
|
||||||
|
}
|
||||||
|
return PACKAGE_PRIVATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -42,7 +42,7 @@ public class MixinClassLoader extends URLClassLoader {
|
||||||
if (in == null)
|
if (in == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return NonsenseMixinService.getTransformer().transformClass(MixinEnvironment.getCurrentEnvironment(), name, in.readAllBytes());
|
return NonsenseMixinService.getTransformer().transformClass(MixinEnvironment.getCurrentEnvironment(), name, AccessWidener.processClass(in.readAllBytes(), binName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
package org.ecorous.esnesnon.nonsense.loader.impl.mixin;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentSkipListSet;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.impl.launch.AccessWidener;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.impl.mod.BuiltinExtensions;
|
||||||
|
|
||||||
|
public class AWProcessor {
|
||||||
|
|
||||||
|
private static final String AW_EXTENSION_NAME = BuiltinExtensions.ACCESSWIDENER;
|
||||||
|
private static final Predicate<String> HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate();
|
||||||
|
private static final String SEPARATOR = "[\\t ]+";
|
||||||
|
|
||||||
|
public static void load(Collection<ModProperties> mods){
|
||||||
|
Map<String, AccessWidener.Entry> classMap = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Map<String, AccessWidener.Entry>> methods = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Map<String, AccessWidener.Entry>> fields = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Map<String, AccessWidener.Entry>> mutations = new ConcurrentHashMap<>();
|
||||||
|
Set<String> classNames = new ConcurrentSkipListSet<>();
|
||||||
|
|
||||||
|
mods.stream().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(l -> l.contains("#") ? l.split("#")[0] : l).filter(l -> !l.isBlank())
|
||||||
|
.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.split(SEPARATOR)).filter(l -> l.length > 0).map(AccessWidener.Entry::new).forEach(e -> {
|
||||||
|
classNames.add(e.className());
|
||||||
|
if ("class".equals(e.targetType())) {
|
||||||
|
if (e.type() == AccessWidener.AccessType.MUTABLE) {
|
||||||
|
throw new IllegalArgumentException("aw format error: classes can not have a 'mutable' modifier (at: " + e + ")");
|
||||||
|
}
|
||||||
|
if (!classMap.containsKey(e.className())) {
|
||||||
|
classMap.put(e.className(), e);
|
||||||
|
} else {
|
||||||
|
var other = classMap.get(e.className());
|
||||||
|
if (e.isAccessGreaterThan(other)) {
|
||||||
|
classMap.put(e.className(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ("method".equals(e.targetType())) {
|
||||||
|
if (e.type() == AccessWidener.AccessType.MUTABLE) {
|
||||||
|
throw new IllegalArgumentException("aw format error: methods can not have a 'mutable' modifier (at: " + e + ")");
|
||||||
|
}
|
||||||
|
var map = methods.computeIfAbsent(e.className(), s -> new ConcurrentHashMap<>());
|
||||||
|
var id = e.name() + e.descriptor();
|
||||||
|
if (!map.containsKey(id)) {
|
||||||
|
map.put(id, e);
|
||||||
|
} else {
|
||||||
|
var other = map.get(id);
|
||||||
|
if (e.isAccessGreaterThan(other)) {
|
||||||
|
classMap.put(id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ("field".equals(e.targetType())) {
|
||||||
|
if (e.type() == AccessWidener.AccessType.EXTENDABLE) {
|
||||||
|
throw new IllegalArgumentException("aw format error: fields can not have a 'extendable' modifier (at: " + e + ")");
|
||||||
|
}
|
||||||
|
var map = fields.computeIfAbsent(e.className(), s -> new ConcurrentHashMap<>());
|
||||||
|
var id = e.name() + e.descriptor();
|
||||||
|
if (e.type() == AccessWidener.AccessType.MUTABLE) {
|
||||||
|
mutations.computeIfAbsent(e.className(), s -> new ConcurrentHashMap<>()).putIfAbsent(id, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!map.containsKey(id)) {
|
||||||
|
map.put(id, e);
|
||||||
|
} else {
|
||||||
|
var other = map.get(id);
|
||||||
|
if (e.isAccessGreaterThan(other)) {
|
||||||
|
classMap.put(id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AccessWidener.load(new AccessWidener.Data(classMap, methods, fields, mutations, classNames));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,210 +0,0 @@
|
||||||
package org.ecorous.esnesnon.nonsense.loader.impl.plugin.game.minecraft;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.file.FileSystem;
|
|
||||||
import java.nio.file.FileSystems;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentSkipListSet;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.mod.BuiltinExtensions;
|
|
||||||
import org.objectweb.asm.*;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimal implementation of accesswideners.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class AWProcessor {
|
|
||||||
|
|
||||||
private static final String AW_EXTENSION_NAME = BuiltinExtensions.ACCESSWIDENER;
|
|
||||||
private static final Predicate<String> HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate();
|
|
||||||
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 {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
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.stream().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(l -> l.contains("#") ? l.split("#")[0] : l).filter(l -> !l.isBlank())
|
|
||||||
.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.split(SEPARATOR)).filter(l -> l.length > 0).map(Entry::new).forEach(e -> {
|
|
||||||
classNames.add(e.className);
|
|
||||||
if ("class".equals(e.targetType)) {
|
|
||||||
if (e.type == AccessType.MUTABLE) {
|
|
||||||
throw new IllegalArgumentException("aw format error: classes can not have a 'mutable' modifier (at: " + e + ")");
|
|
||||||
}
|
|
||||||
if (!classMap.containsKey(e.className)) {
|
|
||||||
classMap.put(e.className, e);
|
|
||||||
} else {
|
|
||||||
var other = classMap.get(e.className);
|
|
||||||
if (e.isAccessGreaterThan(other)) {
|
|
||||||
classMap.put(e.className, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ("method".equals(e.targetType)) {
|
|
||||||
if (e.type == AccessType.MUTABLE) {
|
|
||||||
throw new IllegalArgumentException("aw format error: methods can not have a 'mutable' modifier (at: " + e + ")");
|
|
||||||
}
|
|
||||||
var map = methods.computeIfAbsent(e.className, s -> new ConcurrentHashMap<>());
|
|
||||||
var id = e.name + e.descriptor;
|
|
||||||
if (!map.containsKey(id)) {
|
|
||||||
map.put(id, e);
|
|
||||||
} else {
|
|
||||||
var other = map.get(id);
|
|
||||||
if (e.isAccessGreaterThan(other)) {
|
|
||||||
classMap.put(id, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ("field".equals(e.targetType)) {
|
|
||||||
if (e.type == AccessType.EXTENDABLE) {
|
|
||||||
throw new IllegalArgumentException("aw format error: fields can not have a 'extendable' modifier (at: " + e + ")");
|
|
||||||
}
|
|
||||||
var map = fields.computeIfAbsent(e.className, s -> new ConcurrentHashMap<>());
|
|
||||||
var id = e.name + e.descriptor;
|
|
||||||
if (e.type == AccessType.MUTABLE) {
|
|
||||||
mutations.computeIfAbsent(e.className, s -> new ConcurrentHashMap<>()).putIfAbsent(id, e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!map.containsKey(id)) {
|
|
||||||
map.put(id, e);
|
|
||||||
} else {
|
|
||||||
var other = map.get(id);
|
|
||||||
if (e.isAccessGreaterThan(other)) {
|
|
||||||
classMap.put(id, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Files.deleteIfExists(output);
|
|
||||||
Files.copy(input, output);
|
|
||||||
|
|
||||||
if (!classNames.isEmpty()) {
|
|
||||||
try (FileSystem out = FileSystems.newFileSystem(output)) {
|
|
||||||
|
|
||||||
for (String name : classNames) {
|
|
||||||
processFile(name, out.getPath("/" + name + ".class"), classMap, fields, methods, mutations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOGGER.info(String.format("Applied AccessWideners in %.2fs", (System.currentTimeMillis() - startTime) / 1000f));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void processFile(String className, 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 {
|
|
||||||
ClassReader reader = new ClassReader(Files.newInputStream(file));
|
|
||||||
ClassWriter writer = new ClassWriter(0);
|
|
||||||
ClassVisitor mapper = new ClassVisitor(Opcodes.ASM9, writer) {
|
|
||||||
@Override
|
|
||||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
|
||||||
Entry e = classMap.get(className);
|
|
||||||
if (e != null) {
|
|
||||||
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
|
||||||
access |= e.type.access;
|
|
||||||
}
|
|
||||||
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_PUBLIC;
|
|
||||||
}
|
|
||||||
super.visit(version, access, name, signature, superName, interfaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
|
||||||
Map<String, Entry> map = fields.get(className);
|
|
||||||
if (map != null) {
|
|
||||||
Entry e = map.get(name + descriptor);
|
|
||||||
if (e != null) {
|
|
||||||
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC); // remove all access modifiers
|
|
||||||
access |= e.type.access; // re-add the new one
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((map = mutations.get(className)) != null) {
|
|
||||||
var e = map.get(name + descriptor);
|
|
||||||
if (e != null) {
|
|
||||||
access &= ~Opcodes.ACC_FINAL; // always AccessType.MUTABLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visitField(access, name, descriptor, signature, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
|
||||||
Map<String, Entry> map = methods.get(className);
|
|
||||||
if (map != null) {
|
|
||||||
Entry e = map.get(name + descriptor);
|
|
||||||
if (e != null) {
|
|
||||||
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
|
||||||
access |= e.type.access;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.accept(mapper, 0);
|
|
||||||
|
|
||||||
Files.write(file, writer.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
private record Entry(AccessType type, String targetType, String className, String name, String descriptor) {
|
|
||||||
|
|
||||||
public Entry(String[] line) {
|
|
||||||
this(AccessType.of(line[0]), line[1], line[2], line[3], line[4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAccessGreaterThan(Entry other) {
|
|
||||||
return Access.of(type().access).index < Access.of(other.type.access).index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
private enum AccessType {
|
|
||||||
ACCESSIBLE("accessible", Opcodes.ACC_PUBLIC),
|
|
||||||
EXTENDABLE("extendable", Opcodes.ACC_PROTECTED),
|
|
||||||
MUTABLE("mutable", ~Opcodes.ACC_FINAL);
|
|
||||||
private final String id;
|
|
||||||
private final int access;
|
|
||||||
|
|
||||||
public static AccessType of(String name) {
|
|
||||||
return Arrays.stream(values()).filter(a -> a.id.equals(name)).findFirst().orElseThrow(() -> new IllegalStateException("Unknown access type: " + name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
private enum Access {
|
|
||||||
PUBLIC(1), PROTECTED(2), PACKAGE_PRIVATE(3), PRIVATE(4);
|
|
||||||
private final int index;
|
|
||||||
|
|
||||||
public static Access of(int access) {
|
|
||||||
if ((access & Opcodes.ACC_PUBLIC) != 0) {
|
|
||||||
return PUBLIC;
|
|
||||||
}
|
|
||||||
if ((access & Opcodes.ACC_PROTECTED) != 0) {
|
|
||||||
return PROTECTED;
|
|
||||||
}
|
|
||||||
if ((access & Opcodes.ACC_PRIVATE) != 0) {
|
|
||||||
return PRIVATE;
|
|
||||||
}
|
|
||||||
return PACKAGE_PRIVATE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,6 +15,7 @@ import org.ecorous.esnesnon.nonsense.loader.api.extensions.PreLaunchExtension;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.mod.*;
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.*;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.Discovery;
|
import org.ecorous.esnesnon.nonsense.loader.impl.Discovery;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.LoaderImpl;
|
import org.ecorous.esnesnon.nonsense.loader.impl.LoaderImpl;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.impl.mixin.AWProcessor;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.mod.BuiltinExtensions;
|
import org.ecorous.esnesnon.nonsense.loader.impl.mod.BuiltinExtensions;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.mod.ModPropertiesImpl;
|
import org.ecorous.esnesnon.nonsense.loader.impl.mod.ModPropertiesImpl;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.mod.ModPropertiesReader;
|
import org.ecorous.esnesnon.nonsense.loader.impl.mod.ModPropertiesReader;
|
||||||
|
@ -98,17 +99,9 @@ public class Minecraft implements NonsensePlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var awIn = loader.isDevelopment() ? gamePath : remappedGamePath;
|
AWProcessor.load(modProperties);
|
||||||
Path runtimePath = remappedGamePath.resolveSibling("game-" + version + "-runtime.jar");
|
|
||||||
AWProcessor.apply(modProperties, awIn, runtimePath);
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
||||||
try {
|
|
||||||
Files.deleteIfExists(runtimePath);
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
|
var runtimePath = loader.isDevelopment() ? gamePath : remappedGamePath;
|
||||||
LoaderImpl.getInstance().getClassloader().addURL(runtimePath.toUri().toURL());
|
LoaderImpl.getInstance().getClassloader().addURL(runtimePath.toUri().toURL());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue