mirror of https://github.com/FabricMC/yarn.git
Preliminary work on getting records and sealed classes right
Signed-off-by: liach <liach@users.noreply.github.com>
This commit is contained in:
parent
defbde19d8
commit
099ddebbd0
|
@ -16,34 +16,45 @@
|
||||||
|
|
||||||
package net.fabricmc.mappingpoet;
|
package net.fabricmc.mappingpoet;
|
||||||
|
|
||||||
import static net.fabricmc.mappingpoet.FieldBuilder.parseAnnotation;
|
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import com.squareup.javapoet.AnnotationSpec;
|
import com.squareup.javapoet.AnnotationSpec;
|
||||||
import com.squareup.javapoet.ClassName;
|
import com.squareup.javapoet.ClassName;
|
||||||
|
import com.squareup.javapoet.ParameterSpec;
|
||||||
import com.squareup.javapoet.TypeSpec;
|
import com.squareup.javapoet.TypeSpec;
|
||||||
import com.squareup.javapoet.TypeVariableName;
|
import com.squareup.javapoet.TypeVariableName;
|
||||||
import org.objectweb.asm.Opcodes;
|
|
||||||
import org.objectweb.asm.TypeReference;
|
|
||||||
import org.objectweb.asm.tree.AnnotationNode;
|
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
|
||||||
import org.objectweb.asm.tree.FieldNode;
|
|
||||||
import org.objectweb.asm.tree.InnerClassNode;
|
|
||||||
import org.objectweb.asm.tree.MethodNode;
|
|
||||||
|
|
||||||
import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors;
|
import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors;
|
||||||
import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures;
|
import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures;
|
||||||
import net.fabricmc.mappingpoet.signature.ClassSignature;
|
import net.fabricmc.mappingpoet.signature.ClassSignature;
|
||||||
import net.fabricmc.mappingpoet.signature.ClassStaticContext;
|
import net.fabricmc.mappingpoet.signature.ClassStaticContext;
|
||||||
import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping;
|
import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping;
|
||||||
import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage;
|
import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage;
|
||||||
|
import org.objectweb.asm.Handle;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.TypeReference;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.AnnotationNode;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.FieldNode;
|
||||||
|
import org.objectweb.asm.tree.InnerClassNode;
|
||||||
|
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static net.fabricmc.mappingpoet.FieldBuilder.parseAnnotation;
|
||||||
|
|
||||||
public class ClassBuilder {
|
public class ClassBuilder {
|
||||||
|
static final Handle OBJ_MTH_BOOTSTRAP = new Handle(
|
||||||
|
Opcodes.H_INVOKESTATIC,
|
||||||
|
"java/lang/runtime/ObjectMethods",
|
||||||
|
"bootstrap",
|
||||||
|
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
private final MappingsStore mappings;
|
private final MappingsStore mappings;
|
||||||
private final ClassNode classNode;
|
private final ClassNode classNode;
|
||||||
|
@ -51,21 +62,24 @@ public class ClassBuilder {
|
||||||
private final TypeSpec.Builder builder;
|
private final TypeSpec.Builder builder;
|
||||||
private final List<ClassBuilder> innerClasses = new ArrayList<>();
|
private final List<ClassBuilder> innerClasses = new ArrayList<>();
|
||||||
private final Function<String, Collection<String>> superGetter;
|
private final Function<String, Collection<String>> superGetter;
|
||||||
|
private final Predicate<String> sealChecker;
|
||||||
private final ClassStaticContext context;
|
private final ClassStaticContext context;
|
||||||
|
|
||||||
private final ClassSignature signature; // not really signature
|
private final ClassSignature signature; // not really signature
|
||||||
private final TypeAnnotationMapping typeAnnotations;
|
private final TypeAnnotationMapping typeAnnotations;
|
||||||
private boolean annotationClass;
|
private boolean annotationClass;
|
||||||
private boolean enumClass;
|
private boolean enumClass;
|
||||||
|
private boolean recordClass;
|
||||||
private boolean instanceInner = false;
|
private boolean instanceInner = false;
|
||||||
// only nonnull if any class in the inner class chain creates a generic <T> decl
|
// only nonnull if any class in the inner class chain creates a generic <T> decl
|
||||||
// omits L and ;
|
// omits L and ;
|
||||||
private String receiverSignature;
|
private String receiverSignature;
|
||||||
|
|
||||||
public ClassBuilder(MappingsStore mappings, ClassNode classNode, Function<String, Collection<String>> superGetter, ClassStaticContext context) {
|
public ClassBuilder(MappingsStore mappings, ClassNode classNode, Function<String, Collection<String>> superGetter, Predicate<String> sealChecker, ClassStaticContext context) {
|
||||||
this.mappings = mappings;
|
this.mappings = mappings;
|
||||||
this.classNode = classNode;
|
this.classNode = classNode;
|
||||||
this.superGetter = superGetter;
|
this.superGetter = superGetter;
|
||||||
|
this.sealChecker = sealChecker;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.typeAnnotations = setupAnnotations();
|
this.typeAnnotations = setupAnnotations();
|
||||||
this.signature = setupSignature();
|
this.signature = setupSignature();
|
||||||
|
@ -144,6 +158,9 @@ public class ClassBuilder {
|
||||||
} else if (classNode.superName.equals("java/lang/Enum")) {
|
} else if (classNode.superName.equals("java/lang/Enum")) {
|
||||||
enumClass = true;
|
enumClass = true;
|
||||||
builder = TypeSpec.enumBuilder(name);
|
builder = TypeSpec.enumBuilder(name);
|
||||||
|
} else if (classNode.superName.equals("java/lang/Record")) {
|
||||||
|
recordClass = true;
|
||||||
|
builder = TypeSpec.recordBuilder(name);
|
||||||
} else {
|
} else {
|
||||||
builder = TypeSpec.classBuilder(name)
|
builder = TypeSpec.classBuilder(name)
|
||||||
.superclass(signature.superclass());
|
.superclass(signature.superclass());
|
||||||
|
@ -162,7 +179,10 @@ public class ClassBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder
|
return builder
|
||||||
.addModifiers(new ModifierBuilder(classNode.access).getModifiers(enumClass ? ModifierBuilder.Type.ENUM : ModifierBuilder.Type.CLASS));
|
.addModifiers(new ModifierBuilder(classNode.access)
|
||||||
|
.checkUnseal(classNode, sealChecker)
|
||||||
|
.getModifiers(ModifierBuilder.getType(enumClass, recordClass))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addInterfaces() {
|
private void addInterfaces() {
|
||||||
|
@ -197,6 +217,7 @@ public class ClassBuilder {
|
||||||
|
|
||||||
private void addMethods() {
|
private void addMethods() {
|
||||||
if (classNode.methods == null) return;
|
if (classNode.methods == null) return;
|
||||||
|
methodsLoop:
|
||||||
for (MethodNode method : classNode.methods) {
|
for (MethodNode method : classNode.methods) {
|
||||||
if ((method.access & Opcodes.ACC_SYNTHETIC) != 0 || (method.access & Opcodes.ACC_MANDATED) != 0) {
|
if ((method.access & Opcodes.ACC_SYNTHETIC) != 0 || (method.access & Opcodes.ACC_MANDATED) != 0) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -217,6 +238,21 @@ public class ClassBuilder {
|
||||||
formalParamStartIndex = 2; // 0 String 1 int
|
formalParamStartIndex = 2; // 0 String 1 int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (recordClass) {
|
||||||
|
// skip record sugars
|
||||||
|
if (method.name.equals("equals") && method.desc.equals("(Ljava/lang/Object;)Z")
|
||||||
|
|| method.name.equals("toString") && method.desc.equals("()Ljava/lang/String;")
|
||||||
|
|| method.name.equals("hashCode") && method.desc.equals("()I")) {
|
||||||
|
for (AbstractInsnNode insn : method.instructions) {
|
||||||
|
if (insn instanceof InvokeDynamicInsnNode indy
|
||||||
|
&& indy.bsm.equals(OBJ_MTH_BOOTSTRAP)
|
||||||
|
&& indy.name.equals(method.name))
|
||||||
|
continue methodsLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo test component getters
|
||||||
|
}
|
||||||
if (instanceInner) {
|
if (instanceInner) {
|
||||||
if (method.name.equals("<init>")) {
|
if (method.name.equals("<init>")) {
|
||||||
formalParamStartIndex = 1; // 0 this$0
|
formalParamStartIndex = 1; // 0 this$0
|
||||||
|
@ -229,6 +265,19 @@ public class ClassBuilder {
|
||||||
private void addFields() {
|
private void addFields() {
|
||||||
if (classNode.fields == null) return;
|
if (classNode.fields == null) return;
|
||||||
for (FieldNode field : classNode.fields) {
|
for (FieldNode field : classNode.fields) {
|
||||||
|
if (recordClass && !Modifier.isStatic(field.access)) {
|
||||||
|
if (!Modifier.isFinal(field.access) || !Modifier.isPrivate(field.access)) {
|
||||||
|
System.out.println("abnormal instance field " + field.name + " in record " + getClassName() + ", skipping");
|
||||||
|
} else {
|
||||||
|
var fieldBuilder = new FieldBuilder(mappings, classNode, field, context);
|
||||||
|
var paramBuilder = ParameterSpec.builder(fieldBuilder.calculateType(), field.name);
|
||||||
|
fieldBuilder.addJavaDoc(paramBuilder);
|
||||||
|
fieldBuilder.addDirectAnnotations(paramBuilder);
|
||||||
|
builder.addRecordComponent(paramBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if ((field.access & Opcodes.ACC_SYNTHETIC) != 0 || (field.access & Opcodes.ACC_MANDATED) != 0) {
|
if ((field.access & Opcodes.ACC_SYNTHETIC) != 0 || (field.access & Opcodes.ACC_MANDATED) != 0) {
|
||||||
continue; // hide synthetic stuff
|
continue; // hide synthetic stuff
|
||||||
}
|
}
|
||||||
|
@ -285,7 +334,10 @@ public class ClassBuilder {
|
||||||
classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC);
|
classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC);
|
||||||
} else {
|
} else {
|
||||||
classBuilder.builder.modifiers.remove(javax.lang.model.element.Modifier.PUBLIC); // this modifier may come from class access
|
classBuilder.builder.modifiers.remove(javax.lang.model.element.Modifier.PUBLIC); // this modifier may come from class access
|
||||||
classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access).getModifiers(classBuilder.enumClass ? ModifierBuilder.Type.ENUM : ModifierBuilder.Type.CLASS));
|
classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access)
|
||||||
|
.checkUnseal(classBuilder.classNode, sealChecker)
|
||||||
|
.getModifiers(ModifierBuilder.getType(classBuilder.enumClass, classBuilder.recordClass))
|
||||||
|
);
|
||||||
if (!Modifier.isStatic(innerClassNode.access)) {
|
if (!Modifier.isStatic(innerClassNode.access)) {
|
||||||
classBuilder.instanceInner = true;
|
classBuilder.instanceInner = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,20 +16,21 @@
|
||||||
|
|
||||||
package net.fabricmc.mappingpoet;
|
package net.fabricmc.mappingpoet;
|
||||||
|
|
||||||
import java.util.AbstractMap;
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.squareup.javapoet.AnnotationSpec;
|
import com.squareup.javapoet.AnnotationSpec;
|
||||||
import com.squareup.javapoet.ArrayTypeName;
|
import com.squareup.javapoet.ArrayTypeName;
|
||||||
import com.squareup.javapoet.ClassName;
|
import com.squareup.javapoet.ClassName;
|
||||||
import com.squareup.javapoet.CodeBlock;
|
import com.squareup.javapoet.CodeBlock;
|
||||||
import com.squareup.javapoet.FieldSpec;
|
import com.squareup.javapoet.FieldSpec;
|
||||||
|
import com.squareup.javapoet.ParameterSpec;
|
||||||
import com.squareup.javapoet.TypeName;
|
import com.squareup.javapoet.TypeName;
|
||||||
import com.squareup.javapoet.TypeSpec;
|
import com.squareup.javapoet.TypeSpec;
|
||||||
|
import net.fabricmc.mapping.util.EntryTriple;
|
||||||
|
import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors;
|
||||||
|
import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures;
|
||||||
|
import net.fabricmc.mappingpoet.signature.ClassStaticContext;
|
||||||
|
import net.fabricmc.mappingpoet.signature.TypeAnnotationBank;
|
||||||
|
import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping;
|
||||||
|
import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
import org.objectweb.asm.TypePath;
|
import org.objectweb.asm.TypePath;
|
||||||
|
@ -38,19 +39,18 @@ import org.objectweb.asm.tree.AnnotationNode;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
import org.objectweb.asm.tree.FieldNode;
|
import org.objectweb.asm.tree.FieldNode;
|
||||||
|
|
||||||
import net.fabricmc.mapping.util.EntryTriple;
|
import java.util.AbstractMap;
|
||||||
import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors;
|
import java.util.ArrayDeque;
|
||||||
import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures;
|
import java.util.Deque;
|
||||||
import net.fabricmc.mappingpoet.signature.ClassStaticContext;
|
import java.util.Iterator;
|
||||||
import net.fabricmc.mappingpoet.signature.TypeAnnotationBank;
|
import java.util.List;
|
||||||
import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping;
|
import java.util.Map;
|
||||||
import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage;
|
|
||||||
|
|
||||||
public class FieldBuilder {
|
public class FieldBuilder {
|
||||||
private final MappingsStore mappings;
|
private final MappingsStore mappings;
|
||||||
private final ClassNode classNode;
|
private final ClassNode classNode;
|
||||||
private final FieldNode fieldNode;
|
private final FieldNode fieldNode;
|
||||||
private final FieldSpec.Builder builder;
|
private final FieldSpec.Builder builder; // todo extract interface to build both record param/field easily
|
||||||
private final TypeAnnotationMapping annotations;
|
private final TypeAnnotationMapping annotations;
|
||||||
private final ClassStaticContext context;
|
private final ClassStaticContext context;
|
||||||
|
|
||||||
|
@ -471,6 +471,10 @@ public class FieldBuilder {
|
||||||
mappings.addFieldDoc(builder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc));
|
mappings.addFieldDoc(builder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addJavaDoc(ParameterSpec.Builder paramBuilder) {
|
||||||
|
mappings.addFieldDoc(paramBuilder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc));
|
||||||
|
}
|
||||||
|
|
||||||
private void addDirectAnnotations() {
|
private void addDirectAnnotations() {
|
||||||
addDirectAnnotations(fieldNode.invisibleAnnotations);
|
addDirectAnnotations(fieldNode.invisibleAnnotations);
|
||||||
addDirectAnnotations(fieldNode.visibleAnnotations);
|
addDirectAnnotations(fieldNode.visibleAnnotations);
|
||||||
|
@ -485,7 +489,21 @@ public class FieldBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypeName calculateType() {
|
void addDirectAnnotations(ParameterSpec.Builder paramBuilder) {
|
||||||
|
addDirectAnnotations(paramBuilder, fieldNode.invisibleAnnotations);
|
||||||
|
addDirectAnnotations(paramBuilder, fieldNode.visibleAnnotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDirectAnnotations(ParameterSpec.Builder paramBuilder, List<AnnotationNode> regularAnnotations) {
|
||||||
|
if (regularAnnotations == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (AnnotationNode annotation : regularAnnotations) {
|
||||||
|
paramBuilder.addAnnotation(parseAnnotation(annotation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeName calculateType() {
|
||||||
if (fieldNode.signature != null) {
|
if (fieldNode.signature != null) {
|
||||||
return AnnotationAwareSignatures.parseFieldSignature(fieldNode.signature, annotations, context);
|
return AnnotationAwareSignatures.parseFieldSignature(fieldNode.signature, annotations, context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,14 @@
|
||||||
|
|
||||||
package net.fabricmc.mappingpoet;
|
package net.fabricmc.mappingpoet;
|
||||||
|
|
||||||
|
import com.squareup.javapoet.JavaFile;
|
||||||
|
import net.fabricmc.mappingpoet.signature.ClassStaticContext;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassVisitor;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.InnerClassNode;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -33,22 +41,16 @@ import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
import com.squareup.javapoet.JavaFile;
|
|
||||||
import org.objectweb.asm.ClassReader;
|
|
||||||
import org.objectweb.asm.ClassVisitor;
|
|
||||||
import org.objectweb.asm.Opcodes;
|
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
|
||||||
import org.objectweb.asm.tree.InnerClassNode;
|
|
||||||
|
|
||||||
import net.fabricmc.mappingpoet.signature.ClassStaticContext;
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -90,7 +92,7 @@ public class Main {
|
||||||
public static void generate(Path mappings, Path inputJar, Path outputDirectory, Path librariesDir) {
|
public static void generate(Path mappings, Path inputJar, Path outputDirectory, Path librariesDir) {
|
||||||
final MappingsStore mapping = new MappingsStore(mappings);
|
final MappingsStore mapping = new MappingsStore(mappings);
|
||||||
Map<String, ClassBuilder> classes = new HashMap<>();
|
Map<String, ClassBuilder> classes = new HashMap<>();
|
||||||
forEachClass(inputJar, (superGetter, classNode, context) -> writeClass(mapping, classNode, classes, superGetter, context), librariesDir);
|
forEachClass(inputJar, (superGetter, classNode, context, sealChecker) -> writeClass(mapping, classNode, classes, superGetter, sealChecker, context), librariesDir);
|
||||||
|
|
||||||
classes.values().stream()
|
classes.values().stream()
|
||||||
.filter(classBuilder -> !classBuilder.getClassName().contains("$"))
|
.filter(classBuilder -> !classBuilder.getClassName().contains("$"))
|
||||||
|
@ -111,6 +113,7 @@ public class Main {
|
||||||
private static void forEachClass(Path jar, ClassNodeConsumer classNodeConsumer, Path librariesDir) {
|
private static void forEachClass(Path jar, ClassNodeConsumer classNodeConsumer, Path librariesDir) {
|
||||||
List<ClassNode> classes = new ArrayList<>();
|
List<ClassNode> classes = new ArrayList<>();
|
||||||
Map<String, Collection<String>> supers = new HashMap<>();
|
Map<String, Collection<String>> supers = new HashMap<>();
|
||||||
|
Set<String> sealedClasses = new HashSet<>(); // their subclsses/impls need non-sealed modifier
|
||||||
|
|
||||||
Map<String, Boolean> instanceInnerClasses = new ConcurrentHashMap<>();
|
Map<String, Boolean> instanceInnerClasses = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@ -149,6 +152,10 @@ public class Main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (classNode.permittedSubclasses != null) {
|
||||||
|
sealedClasses.add(classNode.name);
|
||||||
|
}
|
||||||
|
|
||||||
classes.add(classNode);
|
classes.add(classNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +168,7 @@ public class Main {
|
||||||
|
|
||||||
ClassStaticContext innerClassContext = new InnerClassStats(instanceInnerClasses);
|
ClassStaticContext innerClassContext = new InnerClassStats(instanceInnerClasses);
|
||||||
Function<String, Collection<String>> superGetter = k -> supers.getOrDefault(k, Collections.emptyList());
|
Function<String, Collection<String>> superGetter = k -> supers.getOrDefault(k, Collections.emptyList());
|
||||||
classes.forEach(node -> classNodeConsumer.accept(superGetter, node, innerClassContext));
|
classes.forEach(node -> classNodeConsumer.accept(superGetter, node, innerClassContext, sealedClasses::contains));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void scanInnerClasses(Map<String, Boolean> instanceInnerClasses, Path librariesDir) {
|
private static void scanInnerClasses(Map<String, Boolean> instanceInnerClasses, Path librariesDir) {
|
||||||
|
@ -218,7 +225,7 @@ public class Main {
|
||||||
return ch >= '0' && ch <= '9';
|
return ch >= '0' && ch <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeClass(MappingsStore mappings, ClassNode classNode, Map<String, ClassBuilder> existingClasses, Function<String, Collection<String>> superGetter, ClassStaticContext context) {
|
private static void writeClass(MappingsStore mappings, ClassNode classNode, Map<String, ClassBuilder> existingClasses, Function<String, Collection<String>> superGetter, Predicate<String> sealChecker, ClassStaticContext context) {
|
||||||
String name = classNode.name;
|
String name = classNode.name;
|
||||||
{
|
{
|
||||||
//Block anonymous class and their nested classes
|
//Block anonymous class and their nested classes
|
||||||
|
@ -232,7 +239,7 @@ public class Main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassBuilder classBuilder = new ClassBuilder(mappings, classNode, superGetter, context);
|
ClassBuilder classBuilder = new ClassBuilder(mappings, classNode, superGetter, sealChecker, context);
|
||||||
|
|
||||||
if (name.contains("$")) {
|
if (name.contains("$")) {
|
||||||
String parentClass = name.substring(0, name.lastIndexOf("$"));
|
String parentClass = name.substring(0, name.lastIndexOf("$"));
|
||||||
|
@ -249,7 +256,7 @@ public class Main {
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
private interface ClassNodeConsumer {
|
private interface ClassNodeConsumer {
|
||||||
void accept(Function<String, Collection<String>> superGetter, ClassNode node, ClassStaticContext staticContext);
|
void accept(Function<String, Collection<String>> superGetter, ClassNode node, ClassStaticContext staticContext, Predicate<String> sealedChecker);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class InnerClassStats implements ClassStaticContext {
|
private static final class InnerClassStats implements ClassStaticContext {
|
||||||
|
|
|
@ -16,19 +16,44 @@
|
||||||
|
|
||||||
package net.fabricmc.mappingpoet;
|
package net.fabricmc.mappingpoet;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class ModifierBuilder {
|
public class ModifierBuilder {
|
||||||
|
|
||||||
private final int access;
|
private final int access;
|
||||||
|
private boolean needsUnseal;
|
||||||
|
|
||||||
public ModifierBuilder(int access) {
|
public ModifierBuilder(int access) {
|
||||||
this.access = access;
|
this.access = access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ModifierBuilder checkUnseal(ClassNode node, Predicate<String> sealChecker) {
|
||||||
|
if (java.lang.reflect.Modifier.isFinal(node.access)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.interfaces != null) {
|
||||||
|
for (String itf : node.interfaces) {
|
||||||
|
if (sealChecker.test(itf)) {
|
||||||
|
needsUnseal = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.superName != null && sealChecker.test(node.superName)) {
|
||||||
|
needsUnseal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Modifier[] getModifiers(Type type) {
|
public Modifier[] getModifiers(Type type) {
|
||||||
List<Modifier> modifiers = new ArrayList<>();
|
List<Modifier> modifiers = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -36,7 +61,7 @@ public class ModifierBuilder {
|
||||||
if (java.lang.reflect.Modifier.isFinal(access)) {
|
if (java.lang.reflect.Modifier.isFinal(access)) {
|
||||||
modifiers.add(Modifier.FINAL);
|
modifiers.add(Modifier.FINAL);
|
||||||
}
|
}
|
||||||
return modifiers.toArray(new Modifier[] {});
|
return modifiers.toArray(new Modifier[]{});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isPublic(access)) {
|
if (java.lang.reflect.Modifier.isPublic(access)) {
|
||||||
|
@ -57,7 +82,7 @@ public class ModifierBuilder {
|
||||||
modifiers.add(Modifier.DEFAULT);
|
modifiers.add(Modifier.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (java.lang.reflect.Modifier.isFinal(access) && type != Type.ENUM) {
|
if (java.lang.reflect.Modifier.isFinal(access) && type != Type.ENUM && type != Type.RECORD) {
|
||||||
modifiers.add(Modifier.FINAL);
|
modifiers.add(Modifier.FINAL);
|
||||||
}
|
}
|
||||||
if (java.lang.reflect.Modifier.isTransient(access) && type == Type.FIELD) {
|
if (java.lang.reflect.Modifier.isTransient(access) && type == Type.FIELD) {
|
||||||
|
@ -76,12 +101,21 @@ public class ModifierBuilder {
|
||||||
modifiers.add(Modifier.STRICTFP);
|
modifiers.add(Modifier.STRICTFP);
|
||||||
}
|
}
|
||||||
|
|
||||||
return modifiers.toArray(new Modifier[] {});
|
if (needsUnseal && type == Type.CLASS) {
|
||||||
|
modifiers.add(Modifier.NON_SEALED);
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiers.toArray(new Modifier[]{});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type getType(boolean enumType, boolean recordType) {
|
||||||
|
return enumType ? Type.ENUM : recordType ? Type.RECORD : Type.CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
CLASS,
|
CLASS,
|
||||||
ENUM,
|
ENUM,
|
||||||
|
RECORD,
|
||||||
METHOD,
|
METHOD,
|
||||||
FIELD,
|
FIELD,
|
||||||
PARAM
|
PARAM
|
||||||
|
|
Loading…
Reference in New Issue