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;
|
||||
|
||||
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.ClassName;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
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.AnnotationAwareSignatures;
|
||||
import net.fabricmc.mappingpoet.signature.ClassSignature;
|
||||
import net.fabricmc.mappingpoet.signature.ClassStaticContext;
|
||||
import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping;
|
||||
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 {
|
||||
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 ClassNode classNode;
|
||||
|
@ -51,21 +62,24 @@ public class ClassBuilder {
|
|||
private final TypeSpec.Builder builder;
|
||||
private final List<ClassBuilder> innerClasses = new ArrayList<>();
|
||||
private final Function<String, Collection<String>> superGetter;
|
||||
private final Predicate<String> sealChecker;
|
||||
private final ClassStaticContext context;
|
||||
|
||||
private final ClassSignature signature; // not really signature
|
||||
private final TypeAnnotationMapping typeAnnotations;
|
||||
private boolean annotationClass;
|
||||
private boolean enumClass;
|
||||
private boolean recordClass;
|
||||
private boolean instanceInner = false;
|
||||
// only nonnull if any class in the inner class chain creates a generic <T> decl
|
||||
// omits L and ;
|
||||
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.classNode = classNode;
|
||||
this.superGetter = superGetter;
|
||||
this.sealChecker = sealChecker;
|
||||
this.context = context;
|
||||
this.typeAnnotations = setupAnnotations();
|
||||
this.signature = setupSignature();
|
||||
|
@ -144,6 +158,9 @@ public class ClassBuilder {
|
|||
} else if (classNode.superName.equals("java/lang/Enum")) {
|
||||
enumClass = true;
|
||||
builder = TypeSpec.enumBuilder(name);
|
||||
} else if (classNode.superName.equals("java/lang/Record")) {
|
||||
recordClass = true;
|
||||
builder = TypeSpec.recordBuilder(name);
|
||||
} else {
|
||||
builder = TypeSpec.classBuilder(name)
|
||||
.superclass(signature.superclass());
|
||||
|
@ -162,7 +179,10 @@ public class ClassBuilder {
|
|||
}
|
||||
|
||||
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() {
|
||||
|
@ -197,6 +217,7 @@ public class ClassBuilder {
|
|||
|
||||
private void addMethods() {
|
||||
if (classNode.methods == null) return;
|
||||
methodsLoop:
|
||||
for (MethodNode method : classNode.methods) {
|
||||
if ((method.access & Opcodes.ACC_SYNTHETIC) != 0 || (method.access & Opcodes.ACC_MANDATED) != 0) {
|
||||
continue;
|
||||
|
@ -217,6 +238,21 @@ public class ClassBuilder {
|
|||
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 (method.name.equals("<init>")) {
|
||||
formalParamStartIndex = 1; // 0 this$0
|
||||
|
@ -229,6 +265,19 @@ public class ClassBuilder {
|
|||
private void addFields() {
|
||||
if (classNode.fields == null) return;
|
||||
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) {
|
||||
continue; // hide synthetic stuff
|
||||
}
|
||||
|
@ -285,7 +334,10 @@ public class ClassBuilder {
|
|||
classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC);
|
||||
} else {
|
||||
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)) {
|
||||
classBuilder.instanceInner = true;
|
||||
}
|
||||
|
|
|
@ -16,20 +16,21 @@
|
|||
|
||||
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.ArrayTypeName;
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.CodeBlock;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
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.Type;
|
||||
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.FieldNode;
|
||||
|
||||
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 java.util.AbstractMap;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FieldBuilder {
|
||||
private final MappingsStore mappings;
|
||||
private final ClassNode classNode;
|
||||
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 ClassStaticContext context;
|
||||
|
||||
|
@ -129,90 +129,90 @@ public class FieldBuilder {
|
|||
|
||||
TypeName current;
|
||||
switch (desc.charAt(index)) {
|
||||
case 'B': {
|
||||
current = TypeName.BYTE;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
current = TypeName.CHAR;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'D': {
|
||||
current = TypeName.DOUBLE;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'F': {
|
||||
current = TypeName.FLOAT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'I': {
|
||||
current = TypeName.INT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'J': {
|
||||
current = TypeName.LONG;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
current = TypeName.SHORT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'Z': {
|
||||
current = TypeName.BOOLEAN;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'V': {
|
||||
current = TypeName.VOID;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'L': {
|
||||
int classNameSeparator = index;
|
||||
index++;
|
||||
int nameStart = index;
|
||||
ClassName currentClassName = null;
|
||||
|
||||
char ch;
|
||||
do {
|
||||
ch = desc.charAt(index);
|
||||
|
||||
if (ch == '$' || ch == ';') {
|
||||
// collect class name
|
||||
if (currentClassName == null) {
|
||||
String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : "";
|
||||
String simpleName = desc.substring(classNameSeparator + 1, index);
|
||||
currentClassName = ClassName.get(packageName, simpleName);
|
||||
} else {
|
||||
String simpleName = desc.substring(classNameSeparator + 1, index);
|
||||
currentClassName = currentClassName.nestedClass(simpleName);
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '/' || ch == '$') {
|
||||
// Start of simple name
|
||||
classNameSeparator = index;
|
||||
}
|
||||
|
||||
case 'B': {
|
||||
current = TypeName.BYTE;
|
||||
index++;
|
||||
} while (ch != ';');
|
||||
|
||||
if (currentClassName == null) {
|
||||
throw invalidDesc(desc, index);
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
current = TypeName.CHAR;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'D': {
|
||||
current = TypeName.DOUBLE;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'F': {
|
||||
current = TypeName.FLOAT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'I': {
|
||||
current = TypeName.INT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'J': {
|
||||
current = TypeName.LONG;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
current = TypeName.SHORT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'Z': {
|
||||
current = TypeName.BOOLEAN;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'V': {
|
||||
current = TypeName.VOID;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'L': {
|
||||
int classNameSeparator = index;
|
||||
index++;
|
||||
int nameStart = index;
|
||||
ClassName currentClassName = null;
|
||||
|
||||
current = currentClassName;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw invalidDesc(desc, index);
|
||||
char ch;
|
||||
do {
|
||||
ch = desc.charAt(index);
|
||||
|
||||
if (ch == '$' || ch == ';') {
|
||||
// collect class name
|
||||
if (currentClassName == null) {
|
||||
String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : "";
|
||||
String simpleName = desc.substring(classNameSeparator + 1, index);
|
||||
currentClassName = ClassName.get(packageName, simpleName);
|
||||
} else {
|
||||
String simpleName = desc.substring(classNameSeparator + 1, index);
|
||||
currentClassName = currentClassName.nestedClass(simpleName);
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '/' || ch == '$') {
|
||||
// Start of simple name
|
||||
classNameSeparator = index;
|
||||
}
|
||||
|
||||
index++;
|
||||
} while (ch != ';');
|
||||
|
||||
if (currentClassName == null) {
|
||||
throw invalidDesc(desc, index);
|
||||
}
|
||||
|
||||
current = currentClassName;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw invalidDesc(desc, index);
|
||||
}
|
||||
|
||||
for (int i = 0; i < arrayLevel; i++) {
|
||||
|
@ -233,101 +233,101 @@ public class FieldBuilder {
|
|||
|
||||
TypeName current;
|
||||
switch (desc.charAt(index)) {
|
||||
case 'B': {
|
||||
current = TypeName.BYTE;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
current = TypeName.CHAR;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'D': {
|
||||
current = TypeName.DOUBLE;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'F': {
|
||||
current = TypeName.FLOAT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'I': {
|
||||
current = TypeName.INT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'J': {
|
||||
current = TypeName.LONG;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
current = TypeName.SHORT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'Z': {
|
||||
current = TypeName.BOOLEAN;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'V': {
|
||||
current = TypeName.VOID;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'L': {
|
||||
int classNameSeparator = index;
|
||||
index++;
|
||||
int nameStart = index;
|
||||
ClassName currentClassName = null;
|
||||
boolean instanceInner = false;
|
||||
case 'B': {
|
||||
current = TypeName.BYTE;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
current = TypeName.CHAR;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'D': {
|
||||
current = TypeName.DOUBLE;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'F': {
|
||||
current = TypeName.FLOAT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'I': {
|
||||
current = TypeName.INT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'J': {
|
||||
current = TypeName.LONG;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
current = TypeName.SHORT;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'Z': {
|
||||
current = TypeName.BOOLEAN;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'V': {
|
||||
current = TypeName.VOID;
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
case 'L': {
|
||||
int classNameSeparator = index;
|
||||
index++;
|
||||
int nameStart = index;
|
||||
ClassName currentClassName = null;
|
||||
boolean instanceInner = false;
|
||||
|
||||
char ch;
|
||||
do {
|
||||
ch = desc.charAt(index);
|
||||
char ch;
|
||||
do {
|
||||
ch = desc.charAt(index);
|
||||
|
||||
if (ch == '$' || ch == ';') {
|
||||
// collect class name
|
||||
if (currentClassName == null) {
|
||||
String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : "";
|
||||
String simpleName = desc.substring(classNameSeparator + 1, index);
|
||||
currentClassName = ClassName.get(packageName, simpleName);
|
||||
} else {
|
||||
String simpleName = desc.substring(classNameSeparator + 1, index);
|
||||
if (ch == '$' || ch == ';') {
|
||||
// collect class name
|
||||
if (currentClassName == null) {
|
||||
String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : "";
|
||||
String simpleName = desc.substring(classNameSeparator + 1, index);
|
||||
currentClassName = ClassName.get(packageName, simpleName);
|
||||
} else {
|
||||
String simpleName = desc.substring(classNameSeparator + 1, index);
|
||||
|
||||
if (!instanceInner && context.isInstanceInner(desc.substring(nameStart, index))) {
|
||||
instanceInner = true;
|
||||
}
|
||||
if (!instanceInner && context.isInstanceInner(desc.substring(nameStart, index))) {
|
||||
instanceInner = true;
|
||||
}
|
||||
|
||||
currentClassName = currentClassName.nestedClass(simpleName);
|
||||
currentClassName = currentClassName.nestedClass(simpleName);
|
||||
|
||||
if (instanceInner) {
|
||||
currentClassName = AnnotationAwareDescriptors.annotate(currentClassName, annotations);
|
||||
annotations = annotations.advance(TypePath.INNER_TYPE, 0);
|
||||
if (instanceInner) {
|
||||
currentClassName = AnnotationAwareDescriptors.annotate(currentClassName, annotations);
|
||||
annotations = annotations.advance(TypePath.INNER_TYPE, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '/' || ch == '$') {
|
||||
// Start of simple name
|
||||
classNameSeparator = index;
|
||||
}
|
||||
|
||||
index++;
|
||||
} while (ch != ';');
|
||||
|
||||
if (currentClassName == null) {
|
||||
throw invalidDesc(desc, index);
|
||||
}
|
||||
|
||||
if (ch == '/' || ch == '$') {
|
||||
// Start of simple name
|
||||
classNameSeparator = index;
|
||||
}
|
||||
|
||||
index++;
|
||||
} while (ch != ';');
|
||||
|
||||
if (currentClassName == null) {
|
||||
throw invalidDesc(desc, index);
|
||||
current = currentClassName;
|
||||
break;
|
||||
}
|
||||
|
||||
current = currentClassName;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw invalidDesc(desc, index);
|
||||
default:
|
||||
throw invalidDesc(desc, index);
|
||||
}
|
||||
|
||||
while (!arrayAnnos.isEmpty()) {
|
||||
|
@ -348,24 +348,24 @@ public class FieldBuilder {
|
|||
@Deprecated // use typeFromDesc, non-recursive
|
||||
public static TypeName getFieldType(String desc) {
|
||||
switch (desc) {
|
||||
case "B":
|
||||
return TypeName.BYTE;
|
||||
case "C":
|
||||
return TypeName.CHAR;
|
||||
case "S":
|
||||
return TypeName.SHORT;
|
||||
case "Z":
|
||||
return TypeName.BOOLEAN;
|
||||
case "I":
|
||||
return TypeName.INT;
|
||||
case "J":
|
||||
return TypeName.LONG;
|
||||
case "F":
|
||||
return TypeName.FLOAT;
|
||||
case "D":
|
||||
return TypeName.DOUBLE;
|
||||
case "V":
|
||||
return TypeName.VOID;
|
||||
case "B":
|
||||
return TypeName.BYTE;
|
||||
case "C":
|
||||
return TypeName.CHAR;
|
||||
case "S":
|
||||
return TypeName.SHORT;
|
||||
case "Z":
|
||||
return TypeName.BOOLEAN;
|
||||
case "I":
|
||||
return TypeName.INT;
|
||||
case "J":
|
||||
return TypeName.LONG;
|
||||
case "F":
|
||||
return TypeName.FLOAT;
|
||||
case "D":
|
||||
return TypeName.DOUBLE;
|
||||
case "V":
|
||||
return TypeName.VOID;
|
||||
}
|
||||
if (desc.startsWith("[")) {
|
||||
return ArrayTypeName.of(getFieldType(desc.substring(1)));
|
||||
|
@ -390,48 +390,48 @@ public class FieldBuilder {
|
|||
private CodeBlock makeInitializer(String desc) {
|
||||
// fake initializers exclude fields from constant values
|
||||
switch (desc.charAt(0)) {
|
||||
case 'B':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
return CodeBlock.builder().add("(byte) $L", fieldNode.value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'C':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
int value = (int) fieldNode.value;
|
||||
char c = (char) value;
|
||||
return printChar(CodeBlock.builder(), c, value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'D':
|
||||
if (fieldNode.value instanceof Double) {
|
||||
return CodeBlock.builder().add("$LD", fieldNode.value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'I':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
return CodeBlock.builder().add("$L", fieldNode.value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'J':
|
||||
if (fieldNode.value instanceof Long) {
|
||||
return CodeBlock.builder().add("$LL", fieldNode.value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'S':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
return CodeBlock.builder().add("(short) $L", fieldNode.value).build();
|
||||
}
|
||||
return CodeBlock.builder().add("java.lang.Byte.parseByte(\"dummy\")").build();
|
||||
case 'F':
|
||||
if (fieldNode.value instanceof Float) {
|
||||
return CodeBlock.builder().add("$LF", fieldNode.value).build();
|
||||
}
|
||||
return CodeBlock.builder().add("java.lang.Float.parseFloat(\"dummy\")").build();
|
||||
case 'Z':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
return CodeBlock.builder().add("$L", ((int) fieldNode.value) != 0).build();
|
||||
}
|
||||
return CodeBlock.builder().add("java.lang.Boolean.parseBoolean(\"dummy\")").build();
|
||||
case 'B':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
return CodeBlock.builder().add("(byte) $L", fieldNode.value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'C':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
int value = (int) fieldNode.value;
|
||||
char c = (char) value;
|
||||
return printChar(CodeBlock.builder(), c, value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'D':
|
||||
if (fieldNode.value instanceof Double) {
|
||||
return CodeBlock.builder().add("$LD", fieldNode.value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'I':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
return CodeBlock.builder().add("$L", fieldNode.value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'J':
|
||||
if (fieldNode.value instanceof Long) {
|
||||
return CodeBlock.builder().add("$LL", fieldNode.value).build();
|
||||
}
|
||||
// fake initializer falls through
|
||||
case 'S':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
return CodeBlock.builder().add("(short) $L", fieldNode.value).build();
|
||||
}
|
||||
return CodeBlock.builder().add("java.lang.Byte.parseByte(\"dummy\")").build();
|
||||
case 'F':
|
||||
if (fieldNode.value instanceof Float) {
|
||||
return CodeBlock.builder().add("$LF", fieldNode.value).build();
|
||||
}
|
||||
return CodeBlock.builder().add("java.lang.Float.parseFloat(\"dummy\")").build();
|
||||
case 'Z':
|
||||
if (fieldNode.value instanceof Integer) {
|
||||
return CodeBlock.builder().add("$L", ((int) fieldNode.value) != 0).build();
|
||||
}
|
||||
return CodeBlock.builder().add("java.lang.Boolean.parseBoolean(\"dummy\")").build();
|
||||
}
|
||||
if (desc.equals("Ljava/lang/String;") && fieldNode.value instanceof String) {
|
||||
return CodeBlock.builder().add("$S", fieldNode.value).build();
|
||||
|
@ -448,20 +448,20 @@ public class FieldBuilder {
|
|||
// See https://docs.oracle.com/javase/specs/jls/se16/html/jls-3.html#jls-EscapeSequence
|
||||
// ignore space or ", just use direct in those cases
|
||||
switch (c) {
|
||||
case '\b':
|
||||
return builder.add("'\\b'");
|
||||
case '\t':
|
||||
return builder.add("'\\t'");
|
||||
case '\n':
|
||||
return builder.add("'\\n'");
|
||||
case '\f':
|
||||
return builder.add("'\\f'");
|
||||
case '\r':
|
||||
return builder.add("'\\r'");
|
||||
case '\'':
|
||||
return builder.add("'\\''");
|
||||
case '\\':
|
||||
return builder.add("'\\\\'");
|
||||
case '\b':
|
||||
return builder.add("'\\b'");
|
||||
case '\t':
|
||||
return builder.add("'\\t'");
|
||||
case '\n':
|
||||
return builder.add("'\\n'");
|
||||
case '\f':
|
||||
return builder.add("'\\f'");
|
||||
case '\r':
|
||||
return builder.add("'\\r'");
|
||||
case '\'':
|
||||
return builder.add("'\\''");
|
||||
case '\\':
|
||||
return builder.add("'\\\\'");
|
||||
}
|
||||
|
||||
return builder.add("'$L'", c);
|
||||
|
@ -471,6 +471,10 @@ public class FieldBuilder {
|
|||
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() {
|
||||
addDirectAnnotations(fieldNode.invisibleAnnotations);
|
||||
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) {
|
||||
return AnnotationAwareSignatures.parseFieldSignature(fieldNode.signature, annotations, context);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,14 @@
|
|||
|
||||
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.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -33,22 +41,16 @@ import java.util.Collections;
|
|||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.jar.JarEntry;
|
||||
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 static void main(String[] args) {
|
||||
|
@ -90,7 +92,7 @@ public class Main {
|
|||
public static void generate(Path mappings, Path inputJar, Path outputDirectory, Path librariesDir) {
|
||||
final MappingsStore mapping = new MappingsStore(mappings);
|
||||
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()
|
||||
.filter(classBuilder -> !classBuilder.getClassName().contains("$"))
|
||||
|
@ -111,6 +113,7 @@ public class Main {
|
|||
private static void forEachClass(Path jar, ClassNodeConsumer classNodeConsumer, Path librariesDir) {
|
||||
List<ClassNode> classes = new ArrayList<>();
|
||||
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<>();
|
||||
|
||||
|
@ -149,6 +152,10 @@ public class Main {
|
|||
}
|
||||
}
|
||||
|
||||
if (classNode.permittedSubclasses != null) {
|
||||
sealedClasses.add(classNode.name);
|
||||
}
|
||||
|
||||
classes.add(classNode);
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +168,7 @@ public class Main {
|
|||
|
||||
ClassStaticContext innerClassContext = new InnerClassStats(instanceInnerClasses);
|
||||
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) {
|
||||
|
@ -218,7 +225,7 @@ public class Main {
|
|||
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;
|
||||
{
|
||||
//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("$")) {
|
||||
String parentClass = name.substring(0, name.lastIndexOf("$"));
|
||||
|
@ -249,7 +256,7 @@ public class Main {
|
|||
|
||||
@FunctionalInterface
|
||||
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 {
|
||||
|
|
|
@ -16,19 +16,44 @@
|
|||
|
||||
package net.fabricmc.mappingpoet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ModifierBuilder {
|
||||
|
||||
private final int access;
|
||||
private boolean needsUnseal;
|
||||
|
||||
public ModifierBuilder(int 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) {
|
||||
List<Modifier> modifiers = new ArrayList<>();
|
||||
|
||||
|
@ -36,7 +61,7 @@ public class ModifierBuilder {
|
|||
if (java.lang.reflect.Modifier.isFinal(access)) {
|
||||
modifiers.add(Modifier.FINAL);
|
||||
}
|
||||
return modifiers.toArray(new Modifier[] {});
|
||||
return modifiers.toArray(new Modifier[]{});
|
||||
}
|
||||
|
||||
if (java.lang.reflect.Modifier.isPublic(access)) {
|
||||
|
@ -57,7 +82,7 @@ public class ModifierBuilder {
|
|||
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);
|
||||
}
|
||||
if (java.lang.reflect.Modifier.isTransient(access) && type == Type.FIELD) {
|
||||
|
@ -76,12 +101,21 @@ public class ModifierBuilder {
|
|||
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 {
|
||||
CLASS,
|
||||
ENUM,
|
||||
RECORD,
|
||||
METHOD,
|
||||
FIELD,
|
||||
PARAM
|
||||
|
|
Loading…
Reference in New Issue