diff --git a/src/main/java/net/fabricmc/mappingio/FlatMappingVisitor.java b/src/main/java/net/fabricmc/mappingio/FlatMappingVisitor.java index 056020ed..da650649 100644 --- a/src/main/java/net/fabricmc/mappingio/FlatMappingVisitor.java +++ b/src/main/java/net/fabricmc/mappingio/FlatMappingVisitor.java @@ -57,16 +57,23 @@ default boolean visitContent() throws IOException { } boolean visitClass(String srcName, String[] dstNames) throws IOException; + void visitClassMetadata(String srcName, String[] dstNames, String propertyKey, String[] propertyValues) throws IOException; void visitClassComment(String srcName, String[] dstNames, String comment) throws IOException; boolean visitField(String srcClsName, String srcName, String srcDesc, String[] dstClsNames, String[] dstNames, String[] dstDescs) throws IOException; + void visitFieldMetadata(String srcClsName, String srcName, String srcDesc, + String[] dstClsNames, String[] dstNames, String[] dstDescs, + String propertyKey, String[] propertyValues) throws IOException; void visitFieldComment(String srcClsName, String srcName, String srcDesc, String[] dstClsNames, String[] dstNames, String[] dstDescs, String comment) throws IOException; boolean visitMethod(String srcClsName, String srcName, String srcDesc, String[] dstClsNames, String[] dstNames, String[] dstDescs) throws IOException; + void visitMethodMetadata(String srcClsName, String srcName, String srcDesc, + String[] dstClsNames, String[] dstNames, String[] dstDescs, + String propertyKey, String[] propertyValues) throws IOException; void visitMethodComment(String srcClsName, String srcName, String srcDesc, String[] dstClsNames, String[] dstNames, String[] dstDescs, String comment) throws IOException; @@ -74,6 +81,10 @@ void visitMethodComment(String srcClsName, String srcName, String srcDesc, boolean visitMethodArg(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames) throws IOException; + void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, + int argPosition, int lvIndex, String srcArgName, + String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames, + String propertyKey, String[] propertyValues) throws IOException; void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames, @@ -82,6 +93,10 @@ void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMe boolean visitMethodVar(String srcClsName, String srcMethodName, String srcMethodDesc, int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames) throws IOException; + void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, + int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, + String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames, + String propertyKey, String[] propertyValues) throws IOException; void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc, int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames, @@ -135,37 +150,53 @@ default boolean visitMethodVar(String srcClsName, String srcMethodName, String s // convenience / potentially higher efficiency visit methods for only one dst name + // Class default boolean visitClass(String srcName, String dstName) throws IOException { return visitClass(srcName, toArray(dstName)); } - + default void visitClassMetadata(String srcName, String propertyKey, String[] propertyValues) throws IOException { + visitClassMetadata(srcName, (String) null, propertyKey, propertyValues); + } + default void visitClassMetadata(String srcName, String dstName, String propertyKey, String[] propertyValues) throws IOException { + visitClassMetadata(srcName, toArray(dstName), propertyKey, propertyValues); + } default void visitClassComment(String srcName, String comment) throws IOException { visitClassComment(srcName, (String) null, comment); } - default void visitClassComment(String srcName, String dstName, String comment) throws IOException { visitClassComment(srcName, toArray(dstName), comment); } + // Field default boolean visitField(String srcClsName, String srcName, String srcDesc, String dstName) throws IOException { return visitField(srcClsName, srcName, srcDesc, null, dstName, null); } - default boolean visitField(String srcClsName, String srcName, String srcDesc, String dstClsName, String dstName, String dstDesc) throws IOException { return visitField(srcClsName, srcName, srcDesc, toArray(dstClsName), toArray(dstName), toArray(dstDesc)); } - + default void visitFieldMetadata(String srcClsName, String srcName, String srcDesc, + String propertyKey, String[] propertyValues) throws IOException { + visitFieldMetadata(srcClsName, srcName, srcDesc, + (String) null, null, null, + propertyKey, propertyValues); + } + default void visitFieldMetadata(String srcClsName, String srcName, String srcDesc, + String dstClsName, String dstName, String dstDesc, + String propertyKey, String[] propertyValues) throws IOException { + visitFieldMetadata(srcClsName, srcName, srcDesc, + toArray(dstClsName), toArray(dstName), toArray(dstDesc), + propertyKey, propertyValues); + } default void visitFieldComment(String srcClsName, String srcName, String srcDesc, String comment) throws IOException { visitFieldComment(srcClsName, srcName, srcDesc, (String) null, null, null, comment); } - default void visitFieldComment(String srcClsName, String srcName, String srcDesc, String dstClsName, String dstName, String dstDesc, String comment) throws IOException { @@ -174,25 +205,36 @@ default void visitFieldComment(String srcClsName, String srcName, String srcDesc comment); } + // Method default boolean visitMethod(String srcClsName, String srcName, String srcDesc, String dstName) throws IOException { return visitMethod(srcClsName, srcName, srcDesc, null, dstName, null); } - default boolean visitMethod(String srcClsName, String srcName, String srcDesc, String dstClsName, String dstName, String dstDesc) throws IOException { return visitMethod(srcClsName, srcName, srcDesc, toArray(dstClsName), toArray(dstName), toArray(dstDesc)); } - + default void visitMethodMetadata(String srcClsName, String srcName, String srcDesc, + String propertyKey, String[] propertyValues) throws IOException { + visitMethodMetadata(srcClsName, srcName, srcDesc, + (String) null, null, null, + propertyKey, propertyValues); + } + default void visitMethodMetadata(String srcClsName, String srcName, String srcDesc, + String dstClsName, String dstName, String dstDesc, + String propertyKey, String[] propertyValues) throws IOException { + visitMethodMetadata(srcClsName, srcName, srcDesc, + toArray(dstClsName), toArray(dstName), toArray(dstDesc), + propertyKey, propertyValues); + } default void visitMethodComment(String srcClsName, String srcName, String srcDesc, String comment) throws IOException { visitMethodComment(srcClsName, srcName, srcDesc, (String) null, null, null, comment); } - default void visitMethodComment(String srcClsName, String srcName, String srcDesc, String dstClsName, String dstName, String dstDesc, String comment) throws IOException { @@ -201,6 +243,7 @@ default void visitMethodComment(String srcClsName, String srcName, String srcDes comment); } + // Method Arg default boolean visitMethodArg(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, String dstArgName) throws IOException { @@ -208,7 +251,6 @@ default boolean visitMethodArg(String srcClsName, String srcMethodName, String s argPosition, lvIndex, srcArgName, null, null, null, dstArgName); } - default boolean visitMethodArg(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName) throws IOException { @@ -216,7 +258,22 @@ default boolean visitMethodArg(String srcClsName, String srcMethodName, String s argPosition, lvIndex, srcArgName, toArray(dstClsName), toArray(dstMethodName), toArray(dstMethodDesc), toArray(dstArgName)); } - + default void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, + int argPosition, int lvIndex, String srcArgName, + String propertyKey, String[] propertyValues) throws IOException { + visitMethodArgMetadata(srcClsName, srcMethodName, srcMethodDesc, + argPosition, lvIndex, srcArgName, + (String) null, null, null, null, + propertyKey, propertyValues); + } + default void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, + int argPosition, int lvIndex, String srcArgName, + String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName, + String propertyKey, String[] propertyValues) throws IOException { + visitMethodArgMetadata(srcClsName, srcMethodName, srcMethodDesc, argPosition, lvIndex, srcArgName, + toArray(dstClsName), toArray(dstMethodName), toArray(dstMethodDesc), toArray(dstArgName), + propertyKey, propertyValues); + } default void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, String comment) throws IOException { @@ -225,7 +282,6 @@ default void visitMethodArgComment(String srcClsName, String srcMethodName, Stri (String) null, null, null, null, comment); } - default void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName, @@ -235,6 +291,7 @@ default void visitMethodArgComment(String srcClsName, String srcMethodName, Stri comment); } + // Method Var default boolean visitMethodVar(String srcClsName, String srcMethodName, String srcMethodDesc, int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, String dstVarName) throws IOException { @@ -242,7 +299,6 @@ default boolean visitMethodVar(String srcClsName, String srcMethodName, String s lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName, null, null, null, dstVarName); } - default boolean visitMethodVar(String srcClsName, String srcMethodName, String srcMethodDesc, int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, String dstClsName, String dstMethodName, String dstMethodDesc, String dstVarName) throws IOException { @@ -250,7 +306,23 @@ default boolean visitMethodVar(String srcClsName, String srcMethodName, String s lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName, toArray(dstClsName), toArray(dstMethodName), toArray(dstMethodDesc), toArray(dstVarName)); } - + default void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, + int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, + String propertyKey, String[] propertyValues) throws IOException { + visitMethodVarMetadata(srcClsName, srcMethodName, srcMethodDesc, + lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName, + (String) null, null, null, null, + propertyKey, propertyValues); + } + default void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, + int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, + String dstClsName, String dstMethodName, String dstMethodDesc, String dstVarName, + String propertyKey, String[] propertyValues) throws IOException { + visitMethodVarMetadata(srcClsName, srcMethodName, srcMethodDesc, + lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName, + toArray(dstClsName), toArray(dstMethodName), toArray(dstMethodDesc), toArray(dstVarName), + propertyKey, propertyValues); + } default void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc, int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, String comment) throws IOException { @@ -259,7 +331,6 @@ default void visitMethodVarComment(String srcClsName, String srcMethodName, Stri (String) null, null, null, null, comment); } - default void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc, int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, String dstClsName, String dstMethodName, String dstMethodDesc, String dstVarName, diff --git a/src/main/java/net/fabricmc/mappingio/MappingReader.java b/src/main/java/net/fabricmc/mappingio/MappingReader.java index 65dae0b4..fd6e1b1d 100644 --- a/src/main/java/net/fabricmc/mappingio/MappingReader.java +++ b/src/main/java/net/fabricmc/mappingio/MappingReader.java @@ -29,6 +29,7 @@ import net.fabricmc.mappingio.format.MappingFormat; import net.fabricmc.mappingio.format.enigma.EnigmaDirReader; import net.fabricmc.mappingio.format.enigma.EnigmaFileReader; +import net.fabricmc.mappingio.format.match.MatchFileReader; import net.fabricmc.mappingio.format.proguard.ProGuardFileReader; import net.fabricmc.mappingio.format.srg.SrgFileReader; import net.fabricmc.mappingio.format.tiny.Tiny1FileReader; @@ -75,6 +76,8 @@ public static MappingFormat detectFormat(Reader reader) throws IOException { case "MD:": case "FD:": return MappingFormat.SRG_FILE; + case "Mat": + return MappingFormat.MATCH_FILE; } String headerStr = String.valueOf(buffer, 0, pos); @@ -200,6 +203,9 @@ public static void read(Reader reader, MappingFormat format, MappingVisitor visi case PROGUARD_FILE: ProGuardFileReader.read(reader, visitor); break; + case MATCH_FILE: + MatchFileReader.read(reader, visitor); + break; default: throw new IllegalStateException(); } diff --git a/src/main/java/net/fabricmc/mappingio/MappingVisitor.java b/src/main/java/net/fabricmc/mappingio/MappingVisitor.java index 5c2455ce..f14752ee 100644 --- a/src/main/java/net/fabricmc/mappingio/MappingVisitor.java +++ b/src/main/java/net/fabricmc/mappingio/MappingVisitor.java @@ -27,11 +27,11 @@ * * *

The elements with a skip-return (Header/Content/Class/Field/Method/Arg/Var/ElementContent) abort processing the @@ -118,6 +118,11 @@ default boolean visitElementContent(MappedElementKind targetKind) throws IOExcep return true; } + /** + * Metadata for the specified element (last content-visited or any parent). + */ + default void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) throws IOException { } + /** * Comment for the specified element (last content-visited or any parent). * diff --git a/src/main/java/net/fabricmc/mappingio/adapter/FlatAsRegularMappingVisitor.java b/src/main/java/net/fabricmc/mappingio/adapter/FlatAsRegularMappingVisitor.java index 34da310f..0a681934 100644 --- a/src/main/java/net/fabricmc/mappingio/adapter/FlatAsRegularMappingVisitor.java +++ b/src/main/java/net/fabricmc/mappingio/adapter/FlatAsRegularMappingVisitor.java @@ -18,7 +18,9 @@ import java.io.IOException; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; import net.fabricmc.mappingio.FlatMappingVisitor; @@ -75,7 +77,8 @@ public boolean visitContent() throws IOException { } @Override - public boolean visitClass(String srcName) { + public boolean visitClass(String srcName) throws IOException { + relayPendingElementMetadata(); this.srcClsName = srcName; Arrays.fill(dstNames, null); @@ -85,7 +88,8 @@ public boolean visitClass(String srcName) { } @Override - public boolean visitField(String srcName, String srcDesc) { + public boolean visitField(String srcName, String srcDesc) throws IOException { + relayPendingElementMetadata(); this.srcMemberName = srcName; this.srcMemberDesc = srcDesc; @@ -97,7 +101,8 @@ public boolean visitField(String srcName, String srcDesc) { } @Override - public boolean visitMethod(String srcName, String srcDesc) { + public boolean visitMethod(String srcName, String srcDesc) throws IOException { + relayPendingElementMetadata(); this.srcMemberName = srcName; this.srcMemberDesc = srcDesc; @@ -109,7 +114,8 @@ public boolean visitMethod(String srcName, String srcDesc) { } @Override - public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) { + public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) throws IOException { + relayPendingElementMetadata(); this.srcMemberSubName = srcName; this.argIdx = argPosition; this.lvIndex = lvIndex; @@ -120,7 +126,8 @@ public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) { } @Override - public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName) { + public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName) throws IOException { + relayPendingElementMetadata(); this.srcMemberSubName = srcName; this.argIdx = lvtRowIndex; this.lvIndex = lvIndex; @@ -134,6 +141,7 @@ public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int @Override public boolean visitEnd() throws IOException { + relayPendingElementMetadata(); return next.visitEnd(); } @@ -149,6 +157,7 @@ public void visitDstDesc(MappedElementKind targetKind, int namespace, String des @Override public boolean visitElementContent(MappedElementKind targetKind) throws IOException { + currentElementKind = targetKind; boolean relay; switch (targetKind) { @@ -181,8 +190,17 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept return relay; } + @Override + public void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) { + String[] values = elementMetadata.getOrDefault(key, new String[dstNames.length + 1]); + values[namespace] = value; + elementMetadata.put(key, values); + } + @Override public void visitComment(MappedElementKind targetKind, String comment) throws IOException { + relayPendingElementMetadata(); + switch (targetKind) { case CLASS: next.visitClassComment(srcClsName, dstClassNames, comment); @@ -206,8 +224,46 @@ public void visitComment(MappedElementKind targetKind, String comment) throws IO } } + private void relayPendingElementMetadata() throws IOException { + if (elementMetadata.isEmpty()) return; + + for (Map.Entry entry : elementMetadata.entrySet()) { + String key = entry.getKey(); + String[] values = entry.getValue(); + + switch (currentElementKind) { + case CLASS: + next.visitClassMetadata(srcClsName, dstClassNames, key, values); + break; + case FIELD: + next.visitFieldMetadata(srcClsName, srcMemberName, srcMemberDesc, + dstClassNames, dstMemberNames, dstMemberDescs, key, values); + break; + case METHOD: + next.visitMethodMetadata(srcClsName, srcMemberName, srcMemberDesc, + dstClassNames, dstMemberNames, dstMemberDescs, key, values); + break; + case METHOD_ARG: + next.visitMethodArgMetadata(srcClsName, srcMemberName, srcMemberDesc, argIdx, lvIndex, srcMemberSubName, + dstClassNames, dstMemberNames, dstMemberDescs, dstNames, key, values); + break; + case METHOD_VAR: + next.visitMethodVarMetadata(srcClsName, srcMemberName, srcMemberDesc, argIdx, lvIndex, startOpIdx, endOpIdx, srcMemberSubName, + dstClassNames, dstMemberNames, dstMemberDescs, dstNames, key, values); + break; + default: + throw new IllegalStateException(); + } + } + + elementMetadata.clear(); + currentElementKind = null; + } + private final FlatMappingVisitor next; + private final LinkedHashMap elementMetadata = new LinkedHashMap<>(); + private MappedElementKind currentElementKind; private String srcClsName; private String srcMemberName; private String srcMemberDesc; diff --git a/src/main/java/net/fabricmc/mappingio/adapter/ForwardingMappingVisitor.java b/src/main/java/net/fabricmc/mappingio/adapter/ForwardingMappingVisitor.java index 71d9574f..b06b617c 100644 --- a/src/main/java/net/fabricmc/mappingio/adapter/ForwardingMappingVisitor.java +++ b/src/main/java/net/fabricmc/mappingio/adapter/ForwardingMappingVisitor.java @@ -107,6 +107,11 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept return next.visitElementContent(targetKind); } + @Override + public void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) throws IOException { + next.visitElementMetadata(targetKind, key, namespace, value); + } + @Override public void visitComment(MappedElementKind targetKind, String comment) throws IOException { next.visitComment(targetKind, comment); diff --git a/src/main/java/net/fabricmc/mappingio/adapter/MappingDstNsReorder.java b/src/main/java/net/fabricmc/mappingio/adapter/MappingDstNsReorder.java index b0232d25..1086828a 100644 --- a/src/main/java/net/fabricmc/mappingio/adapter/MappingDstNsReorder.java +++ b/src/main/java/net/fabricmc/mappingio/adapter/MappingDstNsReorder.java @@ -66,6 +66,15 @@ public void visitDstDesc(MappedElementKind targetKind, int namespace, String des } } + @Override + public void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) throws IOException { + if (namespace >= 0) namespace = nsMap[namespace]; + + if (namespace >= 0) { + super.visitElementMetadata(targetKind, key, namespace, value); + } + } + private final List newDstNs; private int[] nsMap; } diff --git a/src/main/java/net/fabricmc/mappingio/adapter/MappingSourceNsSwitch.java b/src/main/java/net/fabricmc/mappingio/adapter/MappingSourceNsSwitch.java index de52a900..91da8920 100644 --- a/src/main/java/net/fabricmc/mappingio/adapter/MappingSourceNsSwitch.java +++ b/src/main/java/net/fabricmc/mappingio/adapter/MappingSourceNsSwitch.java @@ -300,6 +300,17 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept return relay; } + @Override + public void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) throws IOException { + if (namespace == newSourceNs) { + namespace = -1; + } else if (namespace == -1) { + namespace = newSourceNs; + } + + next.visitElementMetadata(targetKind, key, namespace, value); + } + private final String newSourceNsName; private final boolean dropMissingNewSrcName; diff --git a/src/main/java/net/fabricmc/mappingio/adapter/RegularAsFlatMappingVisitor.java b/src/main/java/net/fabricmc/mappingio/adapter/RegularAsFlatMappingVisitor.java index 2c6408e8..153b3dc1 100644 --- a/src/main/java/net/fabricmc/mappingio/adapter/RegularAsFlatMappingVisitor.java +++ b/src/main/java/net/fabricmc/mappingio/adapter/RegularAsFlatMappingVisitor.java @@ -89,6 +89,18 @@ private boolean visitClass(String srcName, String[] dstNames, String dstName) th return relayLastClass; } + @Override + public void visitClassMetadata(String srcName, String[] dstNames, String propertyKey, String[] propertyValues) throws IOException { + if (!visitClass(srcName, dstNames, null)) return; + visitElementMetadata(MappedElementKind.CLASS, propertyKey, propertyValues); + } + + @Override + public void visitClassMetadata(String srcName, String dstName, String propertyKey, String[] propertyValues) throws IOException { + if (!visitClass(srcName, null, dstName)) return; + visitElementMetadata(MappedElementKind.CLASS, propertyKey, propertyValues); + } + @Override public void visitClassComment(String srcName, String[] dstNames, String comment) throws IOException { if (!visitClass(srcName, dstNames, null)) return; @@ -128,6 +140,22 @@ private boolean visitField(String srcClsName, String srcName, String srcDesc, return relayLastMember; } + @Override + public void visitFieldMetadata(String srcClsName, String srcName, String srcDesc, + String[] dstClsNames, String[] dstNames, String[] dstDescs, + String propertyKey, String[] propertyValues) throws IOException { + if (!visitField(srcClsName, srcName, srcDesc, dstClsNames, dstNames, dstDescs, null, null, null)) return; + visitElementMetadata(MappedElementKind.FIELD, propertyKey, propertyValues); + } + + @Override + public void visitFieldMetadata(String srcClsName, String srcName, String srcDesc, + String dstClsName, String dstName, String dstDesc, + String propertyKey, String[] propertyValues) throws IOException { + if (!visitField(srcClsName, srcName, srcDesc, null, null, null, dstClsName, dstName, dstDesc)) return; + visitElementMetadata(MappedElementKind.FIELD, propertyKey, propertyValues); + } + @Override public void visitFieldComment(String srcClsName, String srcName, String srcDesc, String[] dstClsNames, String[] dstNames, String[] dstDescs, @@ -171,6 +199,22 @@ private boolean visitMethod(String srcClsName, String srcName, String srcDesc, return relayLastMember; } + @Override + public void visitMethodMetadata(String srcClsName, String srcName, String srcDesc, + String[] dstClsNames, String[] dstNames, String[] dstDescs, + String propertyKey, String[] propertyValues) throws IOException { + if (!visitMethod(srcClsName, srcName, srcDesc, dstClsNames, dstNames, dstDescs, null, null, null)) return; + visitElementMetadata(MappedElementKind.METHOD, propertyKey, propertyValues); + } + + @Override + public void visitMethodMetadata(String srcClsName, String srcName, String srcDesc, + String dstClsName, String dstName, String dstDesc, + String propertyKey, String[] propertyValues) throws IOException { + if (!visitMethod(srcClsName, srcName, srcDesc, null, null, null, dstClsName, dstName, dstDesc)) return; + visitElementMetadata(MappedElementKind.METHOD, propertyKey, propertyValues); + } + @Override public void visitMethodComment(String srcClsName, String srcName, String srcDesc, String[] dstClsNames, String[] dstNames, String[] dstDescs, @@ -219,6 +263,30 @@ private boolean visitMethodArg(String srcClsName, String srcMethodName, String s return relayLastMethodSub; } + @Override + public void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, + String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames, + String propertyKey, String[] propertyValues) throws IOException { + if (!visitMethodArg(srcClsName, srcMethodName, srcMethodDesc, argPosition, lvIndex, srcArgName, + dstClsNames, dstMethodNames, dstMethodDescs, dstArgNames, null, null, null, null)) { + return; + } + + visitElementMetadata(MappedElementKind.METHOD_ARG, propertyKey, propertyValues); + } + + @Override + public void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, + int lvIndex, String srcArgName, String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName, + String propertyKey, String[] propertyValues) throws IOException { + if (!visitMethodArg(srcClsName, srcMethodName, srcMethodDesc, argPosition, lvIndex, srcArgName, + null, null, null, null, dstClsName, dstMethodName, dstMethodDesc, dstArgName)) { + return; + } + + visitElementMetadata(MappedElementKind.METHOD_ARG, propertyKey, propertyValues); + } + @Override public void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames, @@ -233,8 +301,8 @@ public void visitMethodArgComment(String srcClsName, String srcMethodName, Strin @Override public void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, - int lvIndex, String srcArgName, String dstClsName, String dstMethodName, String dstMethodDesc, - String dstArgName, String comment) throws IOException { + int lvIndex, String srcArgName, String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName, + String comment) throws IOException { if (!visitMethodArg(srcClsName, srcMethodName, srcMethodDesc, argPosition, lvIndex, srcArgName, null, null, null, null, dstClsName, dstMethodName, dstMethodDesc, dstArgName)) { return; @@ -274,6 +342,32 @@ private boolean visitMethodVar(String srcClsName, String srcMethodName, String s return relayLastMethodSub; } + @Override + public void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, + int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, + String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames, + String propertyKey, String[] propertyValues) throws IOException { + if (!visitMethodVar(srcClsName, srcMethodName, srcMethodDesc, lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName, + dstClsNames, dstMethodNames, dstMethodDescs, dstVarNames, null, null, null, null)) { + return; + } + + visitElementMetadata(MappedElementKind.METHOD_VAR, propertyKey, propertyValues); + } + + @Override + public void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, + int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, + String dstClsName, String dstMethodName, String dstMethodDesc, String dstVarName, + String propertyKey, String[] propertyValues) throws IOException { + if (!visitMethodVar(srcClsName, srcMethodName, srcMethodDesc, lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName, + null, null, null, null, dstClsName, dstMethodName, dstMethodDesc, dstVarName)) { + return; + } + + visitElementMetadata(MappedElementKind.METHOD_VAR, propertyKey, propertyValues); + } + @Override public void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc, int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, @@ -344,6 +438,15 @@ private boolean visitDstNamesDescs(MappedElementKind targetKind, String[] dstNam return next.visitElementContent(targetKind); } + private void visitElementMetadata(MappedElementKind targetKind, String key, String[] values) throws IOException { + if (values != null) { + for (int i = -1; i < values.length; i++) { + String value = values[i]; + if (value != null) next.visitElementMetadata(targetKind, key, i, value); + } + } + } + private final MappingVisitor next; private boolean relayDstFieldDescs; diff --git a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java index ac7bfcf6..cb318c4c 100644 --- a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java +++ b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java @@ -17,19 +17,21 @@ package net.fabricmc.mappingio.format; public enum MappingFormat { - TINY_FILE("Tiny file", "tiny", true, true, false, false, false), - TINY_2_FILE("Tiny v2 file", "tiny", true, true, true, true, true), - ENIGMA_FILE("Enigma file", "mappings", false, true, true, true, false), - ENIGMA_DIR("Enigma directory", null, false, true, true, true, false), - MCP_DIR("MCP directory", null, false, false, true, true, false), - SRG_FILE("SRG file", "srg", false, false, false, false, false), - TSRG_FILE("TSRG file", "tsrg", false, false, false, false, false), - TSRG_2_FILE("TSRG2 file", "tsrg", true, false, false, true, false), - PROGUARD_FILE("ProGuard file", "map", false, true, false, false, false); + TINY_FILE("Tiny file", "tiny", true, true, false, false, false, MetadataSupport.ARBITRARY, MetadataSupport.NONE), + TINY_2_FILE("Tiny v2 file", "tiny", true, true, true, true, true, MetadataSupport.ARBITRARY, MetadataSupport.NONE), + ENIGMA_FILE("Enigma file", "mappings", false, true, true, true, false, MetadataSupport.NONE, MetadataSupport.HARDCODED), + ENIGMA_DIR("Enigma directory", null, false, true, true, true, false, MetadataSupport.NONE, MetadataSupport.HARDCODED), + MCP_DIR("MCP directory", null, false, false, true, true, false, MetadataSupport.NONE, MetadataSupport.NONE), + SRG_FILE("SRG file", "srg", false, false, false, false, false, MetadataSupport.NONE, MetadataSupport.NONE), + TSRG_FILE("TSRG file", "tsrg", false, false, false, false, false, MetadataSupport.NONE, MetadataSupport.NONE), + TSRG_2_FILE("TSRG2 file", "tsrg", true, false, false, true, false, MetadataSupport.NONE, MetadataSupport.HARDCODED), + PROGUARD_FILE("ProGuard file", "map", false, true, false, false, false, MetadataSupport.NONE, MetadataSupport.HARDCODED), + MATCH_FILE("Match file", "match", false, true, false, true, true, MetadataSupport.HARDCODED, MetadataSupport.HARDCODED); MappingFormat(String name, String fileExt, boolean hasNamespaces, boolean hasFieldDescriptors, - boolean supportsComments, boolean supportsArgs, boolean supportsLocals) { + boolean supportsComments, boolean supportsArgs, boolean supportsLocals, + MetadataSupport fileMetadataSupport, MetadataSupport elementMetadataSupport) { this.name = name; this.fileExt = fileExt; this.hasNamespaces = hasNamespaces; @@ -37,6 +39,8 @@ public enum MappingFormat { this.supportsComments = supportsComments; this.supportsArgs = supportsArgs; this.supportsLocals = supportsLocals; + this.fileMetadataSupport = fileMetadataSupport; + this.elementMetadataSupport = elementMetadataSupport; } public boolean hasSingleFile() { @@ -56,4 +60,17 @@ public String getGlobPattern() { public final boolean supportsComments; public final boolean supportsArgs; public final boolean supportsLocals; + public final MetadataSupport fileMetadataSupport; + public final MetadataSupport elementMetadataSupport; + + public enum MetadataSupport { + /** No metadata at all. */ + NONE, + + /** Only some select properties. */ + HARDCODED, + + /** Arbitrary metadata may be attached. */ + ARBITRARY + } } diff --git a/src/main/java/net/fabricmc/mappingio/format/MioTempProperties.java b/src/main/java/net/fabricmc/mappingio/format/MioTempProperties.java new file mode 100644 index 00000000..24747d69 --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/format/MioTempProperties.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.format; + +/** + * Internal properties which mapping readers attach while parsing. + * They are removed again when saving via a {@link net.fabricmc.mappingio.MappingWriter MappingWriter}. + */ +public final class MioTempProperties { + public static final String MIO_PREFIX = "mio:"; + + private static String register(String name) { + return MIO_PREFIX + name; + } +} diff --git a/src/main/java/net/fabricmc/mappingio/format/StandardProperties.java b/src/main/java/net/fabricmc/mappingio/format/StandardProperties.java new file mode 100644 index 00000000..1280a65e --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/format/StandardProperties.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2023 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.format; + +import java.util.AbstractMap.SimpleEntry; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.ApiStatus; + +import net.fabricmc.mappingio.MappedElementKind; + +public final class StandardProperties { + private StandardProperties() { + } + + public static Set values() { + return Collections.unmodifiableSet(values); + } + + public static StandardProperty getByName(String name) { + return valuesByName.get(name); + } + + @ApiStatus.Internal + public static StandardProperty getById(String id) { + return valuesById.get(id); + } + + public static final StandardProperty NEXT_INTERMEDIARY_CLASS; + public static final StandardProperty NEXT_INTERMEDIARY_FIELD; + public static final StandardProperty NEXT_INTERMEDIARY_METHOD; + public static final StandardProperty NEXT_INTERMEDIARY_COMPONENT; + public static final StandardProperty MISSING_LVT_INDICES; + public static final StandardProperty ESCAPED_NAMES; + public static final StandardProperty MODIFIED_ACCESS; + public static final StandardProperty IS_STATIC; + public static final StandardProperty START_LINE_NUMBER; + public static final StandardProperty END_LINE_NUMBER; + public static final StandardProperty PARAM_DEST_POS; + private static final Set values = new HashSet<>(); + private static final Map valuesByName = new HashMap<>(); + private static final Map valuesById = new HashMap<>(); + + static { + NEXT_INTERMEDIARY_CLASS = register( + "next-intermediary-class", + new HashMap() {{ + put(MappingFormat.TINY_FILE, "INTERMEDIARY_COUNTER class"); + put(MappingFormat.TINY_2_FILE, "next-intermediary-class"); + }}, + Collections.emptyMap()); + NEXT_INTERMEDIARY_FIELD = register( + "next-intermediary-field", + new HashMap() {{ + put(MappingFormat.TINY_FILE, "INTERMEDIARY_COUNTER field"); + put(MappingFormat.TINY_2_FILE, "next-intermediary-field"); + }}, + Collections.emptyMap()); + NEXT_INTERMEDIARY_METHOD = register( + "next-intermediary-method", + new HashMap() {{ + put(MappingFormat.TINY_FILE, "INTERMEDIARY_COUNTER method"); + put(MappingFormat.TINY_2_FILE, "next-intermediary-method"); + }}, + Collections.emptyMap()); + NEXT_INTERMEDIARY_COMPONENT = register( + "next-intermediary-component", + new HashMap() {{ + put(MappingFormat.TINY_FILE, "INTERMEDIARY_COUNTER component"); + put(MappingFormat.TINY_2_FILE, "next-intermediary-component"); + }}, + Collections.emptyMap()); + MISSING_LVT_INDICES = register( + "missing-lvt-indices", + new HashMap() {{ + put(MappingFormat.TINY_2_FILE, "missing-lvt-indices"); + }}, + Collections.emptyMap()); + ESCAPED_NAMES = register( + "escaped-names", + new HashMap() {{ + put(MappingFormat.TINY_2_FILE, "escaped-names"); + }}, + Collections.emptyMap()); + MODIFIED_ACCESS = register( + "modified-access", + Collections.emptyMap(), + new HashMap, String>() {{ + put(new SimpleEntry<>(MappingFormat.ENIGMA_FILE, MappedElementKind.CLASS), "ACC:"); + put(new SimpleEntry<>(MappingFormat.ENIGMA_FILE, MappedElementKind.FIELD), "ACC:"); + put(new SimpleEntry<>(MappingFormat.ENIGMA_FILE, MappedElementKind.METHOD), "ACC:"); + put(new SimpleEntry<>(MappingFormat.ENIGMA_DIR, MappedElementKind.CLASS), "ACC:"); + put(new SimpleEntry<>(MappingFormat.ENIGMA_DIR, MappedElementKind.FIELD), "ACC:"); + put(new SimpleEntry<>(MappingFormat.ENIGMA_DIR, MappedElementKind.METHOD), "ACC:"); + }}); + IS_STATIC = register( + "is-static", + Collections.emptyMap(), + new HashMap, String>() {{ + put(new SimpleEntry<>(MappingFormat.TSRG_2_FILE, MappedElementKind.METHOD), "static"); + }}); + START_LINE_NUMBER = register( + "start-line-number", + Collections.emptyMap(), + new HashMap, String>() {{ + put(new SimpleEntry<>(MappingFormat.PROGUARD_FILE, MappedElementKind.FIELD), null); + put(new SimpleEntry<>(MappingFormat.PROGUARD_FILE, MappedElementKind.FIELD), null); + }}); + END_LINE_NUMBER = register( + "end-line-number", + Collections.emptyMap(), + new HashMap, String>() {{ + put(new SimpleEntry<>(MappingFormat.PROGUARD_FILE, MappedElementKind.FIELD), null); + put(new SimpleEntry<>(MappingFormat.PROGUARD_FILE, MappedElementKind.FIELD), null); + }}); + PARAM_DEST_POS = register( + "parameter-destination-position", + Collections.emptyMap(), + new HashMap, String>() {{ + put(new SimpleEntry<>(MappingFormat.MATCH_FILE, MappedElementKind.METHOD_ARG), null); + }}); + } + + private static StandardProperty register(String id, Map filePropNameByFormat, + Map, String> elementPropNameByFormat) { + StandardProperty ret = new StandardPropertyImpl(id, filePropNameByFormat, elementPropNameByFormat); + values.add(ret); + valuesById.put(id, ret); + + for (String name : filePropNameByFormat.values()) { + valuesByName.putIfAbsent(name, ret); + } + + for (String name : elementPropNameByFormat.values()) { + valuesByName.putIfAbsent(name, ret); + } + + return ret; + } + + static class StandardPropertyImpl implements StandardProperty { + StandardPropertyImpl(String id, Map filePropNameByFormat, + Map, String> elementPropNameByFormat) { + this.id = id; + this.filePropNameByFormat = filePropNameByFormat; + this.elementPropNameByFormat = elementPropNameByFormat; + this.propElementKindByFormat = elementPropNameByFormat.entrySet().stream() + .map(entry -> entry.getKey()) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + } + + @Override + public boolean isFileProperty() { + return !filePropNameByFormat.isEmpty(); + } + + @Override + public boolean isElementProperty() { + return !elementPropNameByFormat.isEmpty(); + } + + @Override + public Set getApplicableFormats() { + return filePropNameByFormat.keySet(); + } + + @Override + public Map getApplicableElementKinds() { + return propElementKindByFormat; + } + + @Override + public boolean isApplicableTo(MappingFormat format) { + return filePropNameByFormat.containsKey(format); + } + + @Override + public boolean isApplicableTo(MappingFormat format, MappedElementKind elementKind) { + return elementPropNameByFormat.containsKey(new SimpleEntry<>(format, elementKind)); + } + + @Override + public String getNameFor(MappingFormat format) { + return filePropNameByFormat.get(format); + } + + @Override + public String getNameFor(MappingFormat format, MappedElementKind elementKind) { + return elementPropNameByFormat.get(new SimpleEntry<>(format, elementKind)); + } + + @Override + public String getId() { + return id; + } + + private final String id; + private final Map filePropNameByFormat; + private final Map, String> elementPropNameByFormat; + private final Map propElementKindByFormat; + } +} diff --git a/src/main/java/net/fabricmc/mappingio/format/StandardProperty.java b/src/main/java/net/fabricmc/mappingio/format/StandardProperty.java new file mode 100644 index 00000000..84d5ea8e --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/format/StandardProperty.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.format; + +import java.util.Map; +import java.util.Set; + +import org.jetbrains.annotations.ApiStatus; + +import net.fabricmc.mappingio.MappedElementKind; + +public interface StandardProperty { + boolean isFileProperty(); + boolean isElementProperty(); + + Set getApplicableFormats(); + Map getApplicableElementKinds(); + + boolean isApplicableTo(MappingFormat format); + boolean isApplicableTo(MappingFormat format, MappedElementKind elementKind); + + String getNameFor(MappingFormat format); + String getNameFor(MappingFormat format, MappedElementKind elementKind); + + /** + * Used internally by MappingTrees, consistency between JVM sessions + * or library versions isn't guaranteed! + */ + @ApiStatus.Internal + String getId(); +} diff --git a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileReader.java b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileReader.java index 05ffce65..f8885567 100644 --- a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileReader.java +++ b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileReader.java @@ -19,13 +19,17 @@ import java.io.IOException; import java.io.Reader; import java.util.Collections; +import java.util.Locale; import java.util.Set; +import org.jetbrains.annotations.Nullable; + import net.fabricmc.mappingio.MappedElementKind; import net.fabricmc.mappingio.MappingFlag; import net.fabricmc.mappingio.MappingUtil; import net.fabricmc.mappingio.MappingVisitor; import net.fabricmc.mappingio.format.ColumnFileReader; +import net.fabricmc.mappingio.format.StandardProperties; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; @@ -61,7 +65,7 @@ public static void read(ColumnFileReader reader, String sourceNs, String targetN final MappingVisitor finalVisitor = visitor; do { - if (reader.nextCol("CLASS")) { // class: CLASS [] + if (reader.nextCol("CLASS")) { // class: CLASS [] [] readClass(reader, 0, null, null, commentSb, finalVisitor); } } while (reader.nextLine(0)); @@ -75,8 +79,11 @@ public static void read(ColumnFileReader reader, String sourceNs, String targetN } private static void readClass(ColumnFileReader reader, int indent, String outerSrcClass, String outerDstClass, StringBuilder commentSb, MappingVisitor visitor) throws IOException { - String srcInnerName = reader.nextCol(); - if (srcInnerName == null || srcInnerName.isEmpty()) throw new IOException("missing class-name-a in line "+reader.getLineNumber()); + String line = reader.nextCols(false); + String[] parts = line.split(" "); + + if (parts.length == 0 || parts[0].isEmpty()) throw new IOException("missing class-name-a in line "+reader.getLineNumber()); + String srcInnerName = parts[0]; String srcName = srcInnerName; @@ -84,7 +91,22 @@ private static void readClass(ColumnFileReader reader, int indent, String outerS srcName = String.format("%s$%s", outerSrcClass, srcInnerName); } - String dstInnerName = reader.nextCol(); + String dstInnerName = null; + String accessModifier = null; + + if (parts.length == 2) { // | + String parsedModifier = parseModifier(parts[1]); + + if (parsedModifier == null) { + dstInnerName = parts[1]; + } else { + accessModifier = parsedModifier; + } + } else { + dstInnerName = parts[1]; + accessModifier = parts[2]; + } + String dstName = dstInnerName; // merge with outer name if available @@ -96,19 +118,20 @@ private static void readClass(ColumnFileReader reader, int indent, String outerS dstName = String.format("%s$%s", outerDstClass, dstInnerName); } - readClassBody(reader, indent, srcName, dstName, commentSb, visitor); + readClassBody(reader, indent, srcName, dstName, accessModifier, commentSb, visitor); } - private static void readClassBody(ColumnFileReader reader, int indent, String srcClass, String dstClass, StringBuilder commentSb, MappingVisitor visitor) throws IOException { + private static void readClassBody(ColumnFileReader reader, int indent, String srcClass, String dstClass, + String classAccess, StringBuilder commentSb, MappingVisitor visitor) throws IOException { boolean visited = false; int state = 0; // 0=invalid 1=visit -1=skip while (reader.nextLine(indent + 1)) { boolean isMethod; - if (reader.nextCol("CLASS")) { // nested class: CLASS [] + if (reader.nextCol("CLASS")) { // nested class: CLASS [] [] if (!visited || commentSb.length() > 0) { - visitClass(srcClass, dstClass, state, commentSb, visitor); + visitClass(srcClass, dstClass, state, classAccess, commentSb, visitor); visited = true; } @@ -116,46 +139,64 @@ private static void readClassBody(ColumnFileReader reader, int indent, String sr state = 0; } else if (reader.nextCol("COMMENT")) { // comment: COMMENT readComment(reader, commentSb); - } else if ((isMethod = reader.nextCol("METHOD")) || reader.nextCol("FIELD")) { // method: METHOD [] or field: FIELD [] - state = visitClass(srcClass, dstClass, state, commentSb, visitor); + } else if ((isMethod = reader.nextCol("METHOD")) || reader.nextCol("FIELD")) { // METHOD|FIELD [] [] + state = visitClass(srcClass, dstClass, state, classAccess, commentSb, visitor); visited = true; if (state < 0) continue; - String srcName = reader.nextCol(); - if (srcName == null || srcName.isEmpty()) throw new IOException("missing field-name-a in line "+reader.getLineNumber()); + String line = reader.nextCols(false); + String[] parts = line.split(" "); + + if (parts.length == 0 || parts[0].isEmpty()) throw new IOException("missing member-name-a in line "+reader.getLineNumber()); + if (parts.length == 1 || parts[1].isEmpty()) throw new IOException("missing member-desc-a in line "+reader.getLineNumber()); + String srcName = parts[0]; + String dstName = null; + String modifier = null; + String srcDesc; + + if (parts.length == 2) { // + srcDesc = parts[1]; + } else if (parts.length == 3) { // | + String parsedModifier = parseModifier(parts[2]); + + if (parsedModifier == null) { + dstName = parts[1]; + srcDesc = parts[2]; + } else { + srcDesc = parts[1]; + modifier = parsedModifier; + } + } else { // + dstName = parts[1]; + srcDesc = parts[2]; + modifier = parts[3]; + } - String dstNameOrSrcDesc = reader.nextCol(); - if (dstNameOrSrcDesc == null || dstNameOrSrcDesc.isEmpty()) throw new IOException("missing field-desc-b in line "+reader.getLineNumber()); + MappedElementKind targetKind = isMethod && visitor.visitMethod(srcName, srcDesc) ? MappedElementKind.METHOD + : !isMethod && visitor.visitField(srcName, srcDesc) ? MappedElementKind.FIELD : null; - String srcDesc = reader.nextCol(); - String dstName; + if (targetKind != null) { + if (dstName != null && !dstName.isEmpty()) visitor.visitDstName(targetKind, 0, dstName); + if (modifier != null) visitAccessModifier(targetKind, modifier, visitor); - if (srcDesc == null) { - dstName = null; - srcDesc = dstNameOrSrcDesc; - } else { - dstName = dstNameOrSrcDesc; - } - - if (isMethod && visitor.visitMethod(srcName, srcDesc)) { - if (dstName != null && !dstName.isEmpty()) visitor.visitDstName(MappedElementKind.METHOD, 0, dstName); - readMethod(reader, indent, commentSb, visitor); - } else if (!isMethod && visitor.visitField(srcName, srcDesc)) { - if (dstName != null && !dstName.isEmpty()) visitor.visitDstName(MappedElementKind.FIELD, 0, dstName); - readElement(reader, MappedElementKind.FIELD, indent, commentSb, visitor); + if (targetKind == MappedElementKind.METHOD) { + readMethod(reader, indent, commentSb, visitor); + } else { + readElement(reader, targetKind, indent, commentSb, visitor); + } } } } if (!visited || commentSb.length() > 0) { - visitClass(srcClass, dstClass, state, commentSb, visitor); + visitClass(srcClass, dstClass, state, classAccess, commentSb, visitor); } } /** * Re-visit a class if necessary and visit its comment if available. */ - private static int visitClass(String srcClass, String dstClass, int state, StringBuilder commentSb, MappingVisitor visitor) throws IOException { + private static int visitClass(String srcClass, String dstClass, int state, String accessModifier, StringBuilder commentSb, MappingVisitor visitor) throws IOException { // state: 0=invalid 1=visit -1=skip if (state == 0) { @@ -168,6 +209,10 @@ private static int visitClass(String srcClass, String dstClass, int state, Strin state = visitContent ? 1 : -1; + if (accessModifier != null) { + visitAccessModifier(MappedElementKind.CLASS, accessModifier, visitor); + } + if (commentSb.length() > 0) { if (state > 0) visitor.visitComment(MappedElementKind.CLASS, commentSb.toString()); @@ -178,6 +223,10 @@ private static int visitClass(String srcClass, String dstClass, int state, Strin return state; } + private static void visitAccessModifier(MappedElementKind targetKind, String modifier, MappingVisitor visitor) throws IOException { + visitor.visitElementMetadata(targetKind, StandardProperties.MODIFIED_ACCESS.getId(), 0, modifier); + } + private static void readMethod(ColumnFileReader reader, int indent, StringBuilder commentSb, MappingVisitor visitor) throws IOException { if (!visitor.visitElementContent(MappedElementKind.METHOD)) return; @@ -233,4 +282,13 @@ private static void submitComment(MappedElementKind kind, StringBuilder commentS visitor.visitComment(kind, commentSb.toString()); commentSb.setLength(0); } + + @Nullable + private static String parseModifier(String token) { + if (!token.startsWith("ACC:")) { + return null; + } + + return token.substring(4).toLowerCase(Locale.ROOT); + } } diff --git a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileWriter.java b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileWriter.java index 4c285707..c568e349 100644 --- a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileWriter.java +++ b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileWriter.java @@ -33,9 +33,6 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept } else if (targetKind == MappedElementKind.FIELD || targetKind == MappedElementKind.METHOD) { writer.write(' '); writer.write(desc); - writer.write('\n'); - } else { - writer.write('\n'); } return true; diff --git a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaWriterBase.java b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaWriterBase.java index 11277530..9490e5c9 100644 --- a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaWriterBase.java +++ b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaWriterBase.java @@ -19,12 +19,16 @@ import java.io.IOException; import java.io.Writer; import java.util.EnumSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.Set; import net.fabricmc.mappingio.MappedElementKind; import net.fabricmc.mappingio.MappingFlag; import net.fabricmc.mappingio.MappingWriter; +import net.fabricmc.mappingio.format.StandardProperties; abstract class EnigmaWriterBase implements MappingWriter { EnigmaWriterBase(Writer writer) throws IOException { @@ -46,12 +50,14 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) { } @Override public boolean visitClass(String srcName) throws IOException { + writePendingElementMetadata(); srcClassName = srcName; return true; } @Override public boolean visitField(String srcName, String srcDesc) throws IOException { + writePendingElementMetadata(); writeIndent(0); writer.write("FIELD "); writer.write(srcName); @@ -63,6 +69,7 @@ public boolean visitField(String srcName, String srcDesc) throws IOException { @Override public boolean visitMethod(String srcName, String srcDesc) throws IOException { + writePendingElementMetadata(); writeIndent(0); writer.write("METHOD "); writer.write(srcName); @@ -74,6 +81,7 @@ public boolean visitMethod(String srcName, String srcDesc) throws IOException { @Override public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) throws IOException { + writePendingElementMetadata(); writeIndent(1); writer.write("ARG "); writer.write(Integer.toString(lvIndex)); @@ -82,12 +90,14 @@ public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) thro } @Override - public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName) { + public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName) throws IOException { + writePendingElementMetadata(); return false; } @Override public boolean visitEnd() throws IOException { + writePendingElementMetadata(); close(); return true; @@ -108,6 +118,11 @@ public void visitDstName(MappedElementKind targetKind, int namespace, String nam @Override public abstract boolean visitElementContent(MappedElementKind targetKind) throws IOException; + @Override + public void visitElementMetadata(MappedElementKind target, String key, int namespace, String value) { + if (namespace == 0) elementMetadata.put(key, value); + } + protected static int getNextOuterEnd(String name, int startPos) { int pos; @@ -121,6 +136,7 @@ protected static int getNextOuterEnd(String name, int startPos) { @Override public void visitComment(MappedElementKind targetKind, String comment) throws IOException { + writePendingElementMetadata(); int start = 0; int pos; @@ -203,15 +219,38 @@ protected void writeMismatchedOrMissingClasses() throws IOException { dstName = null; } + protected void writePendingElementMetadata() throws IOException { + if (!elementMetadata.isEmpty()) { + for (Map.Entry entry : elementMetadata.entrySet()) { + String key = entry.getKey(); + if (StandardProperties.getById(key) != StandardProperties.MODIFIED_ACCESS) continue; + + writer.write(key); + writer.write(" ACC:"); + writer.write(entry.getValue().toUpperCase(Locale.ROOT)); + break; + } + + elementMetadata.clear(); + } + + writer.write('\n'); + } + protected void writeIndent(int extra) throws IOException { for (int i = 0; i < indent + extra; i++) { writer.write('\t'); } } - protected static final Set flags = EnumSet.of(MappingFlag.NEEDS_UNIQUENESS, MappingFlag.NEEDS_SRC_FIELD_DESC, MappingFlag.NEEDS_SRC_METHOD_DESC); + protected static final Set flags = EnumSet.of( + MappingFlag.NEEDS_HEADER_METADATA, + MappingFlag.NEEDS_UNIQUENESS, + MappingFlag.NEEDS_SRC_FIELD_DESC, + MappingFlag.NEEDS_SRC_METHOD_DESC); protected static final String toEscape = "\\\n\r\0\t"; protected static final String escaped = "\\nr0t"; + protected static final LinkedHashMap elementMetadata = new LinkedHashMap<>(); protected Writer writer; protected int indent; @@ -220,7 +259,6 @@ protected void writeIndent(int extra) throws IOException { protected String currentClass; protected String lastWrittenClass = ""; protected String dstName; - protected String[] dstNames; protected String desc; } diff --git a/src/main/java/net/fabricmc/mappingio/format/match/MatchFileReader.java b/src/main/java/net/fabricmc/mappingio/format/match/MatchFileReader.java new file mode 100644 index 00000000..726e134d --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/format/match/MatchFileReader.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.format.match; + +import java.io.IOException; +import java.io.Reader; +import java.util.Arrays; + +import net.fabricmc.mappingio.MappedElementKind; +import net.fabricmc.mappingio.MappingFlag; +import net.fabricmc.mappingio.MappingVisitor; +import net.fabricmc.mappingio.format.ColumnFileReader; +import net.fabricmc.mappingio.format.StandardProperties; + +public class MatchFileReader { + public static void read(Reader reader, MappingVisitor visitor) throws IOException { + read(new ColumnFileReader(reader, '\t'), visitor); + } + + private static void read(ColumnFileReader reader, MappingVisitor visitor) throws IOException { + if (!reader.nextCol().startsWith("Matches saved")) { + throw new IOException("invalid/unsupported match file: incorrect header"); + } + + if (visitor.getFlags().contains(MappingFlag.NEEDS_MULTIPLE_PASSES)) { + reader.mark(); + } + + boolean escapeNames = false; + + for (;;) { + boolean visitHeader = visitor.visitHeader(); + + if (visitHeader) { + visitor.visitNamespaces("a", Arrays.asList("b")); + } + + if (visitor.visitContent()) { + while (reader.nextLine(0)) { + if (reader.nextCol("c")) { // class: c + String srcName = reader.nextCol(escapeNames); + if (srcName == null || srcName.isEmpty()) throw new IOException("missing class-name-a in line "+reader.getLineNumber()); + + if (visitor.visitClass(srcName)) { + readClass(reader, escapeNames, visitor); + } + } + } + } + + if (visitor.visitEnd()) break; + + reader.reset(); + } + } + + private static void readClass(ColumnFileReader reader, boolean escapeNames, MappingVisitor visitor) throws IOException { + visitor.visitDstName(MappedElementKind.CLASS, 0, reader.nextCol(escapeNames)); + if (!visitor.visitElementContent(MappedElementKind.CLASS)) return; + + while (reader.nextLine(1)) { + boolean field = false; + boolean method = false; + + if ((field = reader.nextCol("f")) || (method = reader.nextCol("m"))) { + // field: f + // method: m (but no spaces between name and desc) + + String[] from; + String[] to; + + if (field) { + from = reader.nextCol(escapeNames).split(";;"); + to = reader.nextCol(escapeNames).split(";;"); + } else { + from = toMethodArray(reader.nextCol(escapeNames)); + to = toMethodArray(reader.nextCol(escapeNames)); + } + + if (from.length != 2 || to.length != 2) throw new IOException("invalid member mapping in line "+reader.getLineNumber()); + + String srcName = from[0]; + String srcDesc = from[1]; + String dstName = to[0]; + String dstDesc = to[1]; + + if (field && visitor.visitField(srcName, srcDesc)) { + visitor.visitDstName(MappedElementKind.FIELD, 0, dstName); + visitor.visitDstDesc(MappedElementKind.FIELD, 0, dstDesc); + } else if (method && visitor.visitMethod(srcName, srcDesc)) { + visitor.visitDstName(MappedElementKind.METHOD, 0, dstName); + visitor.visitDstDesc(MappedElementKind.METHOD, 0, dstDesc); + readMethodContent(reader, escapeNames, visitor); + } + } else if (reader.nextCol("c")) { // comment: c + readComment(reader, MappedElementKind.CLASS, visitor); + } + } + } + + private static String[] toMethodArray(String nameWithDescriptor) { + int parenPos = nameWithDescriptor.indexOf('('); + return new String[] { + nameWithDescriptor.substring(0, parenPos), + nameWithDescriptor.substring(parenPos) + }; + } + + private static void readMethodContent(ColumnFileReader reader, boolean escapeNames, MappingVisitor visitor) throws IOException { + if (!visitor.visitElementContent(MappedElementKind.METHOD)) return; + + while (reader.nextLine(2)) { + if (reader.nextCol("ma")) { + // method arg: ma + + int srcArgPos = reader.nextIntCol(); + int dstArgPos = reader.nextIntCol(); + if (srcArgPos < 0 || dstArgPos < 0) throw new IOException("missing/invalid method arg position in line "+reader.getLineNumber()); + + if (visitor.visitMethodArg(srcArgPos, -1, null)) { + visitor.visitElementMetadata(MappedElementKind.METHOD_ARG, StandardProperties.PARAM_DEST_POS.getId(), 0, String.valueOf(dstArgPos)); + } + } else if (reader.nextCol("c")) { // comment: c + readComment(reader, MappedElementKind.METHOD, visitor); + } + } + } + + private static void readComment(ColumnFileReader reader, MappedElementKind subjectKind, MappingVisitor visitor) throws IOException { + String comment = reader.nextEscapedCol(); + if (comment == null) throw new IOException("missing comment in line "+reader.getLineNumber()); + + visitor.visitComment(subjectKind, comment); + } +} diff --git a/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileReader.java b/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileReader.java index d1670b7d..9193e79a 100644 --- a/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileReader.java +++ b/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileReader.java @@ -27,6 +27,7 @@ import net.fabricmc.mappingio.MappingFlag; import net.fabricmc.mappingio.MappingUtil; import net.fabricmc.mappingio.MappingVisitor; +import net.fabricmc.mappingio.format.StandardProperties; public final class ProGuardFileReader { private ProGuardFileReader() { @@ -115,6 +116,8 @@ private static void read(BufferedReader reader, String sourceNs, String targetNs // lineStart, lineEndIncl, rtype String part0 = parts[0]; int pos = part0.indexOf(':'); + String lineStart = null; + String lineEnd = null; String retType; @@ -124,6 +127,8 @@ private static void read(BufferedReader reader, String sourceNs, String targetNs int pos2 = part0.indexOf(':', pos + 1); assert pos2 != -1; + lineStart = part0.substring(0, pos); + lineEnd = part0.substring(pos + 1, pos2); retType = part0.substring(pos2 + 1); } @@ -141,7 +146,11 @@ private static void read(BufferedReader reader, String sourceNs, String targetNs if (visitor.visitMethod(name, desc)) { String mappedName = parts[3]; visitor.visitDstName(MappedElementKind.METHOD, 0, mappedName); - visitor.visitElementContent(MappedElementKind.METHOD); + + if (visitor.visitElementContent(MappedElementKind.METHOD) && lineStart != null) { + visitor.visitElementMetadata(MappedElementKind.METHOD, StandardProperties.START_LINE_NUMBER.getId(), 0, lineStart); + visitor.visitElementMetadata(MappedElementKind.METHOD, StandardProperties.END_LINE_NUMBER.getId(), 0, lineEnd); + } } } } diff --git a/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileWriter.java b/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileWriter.java index f41d4450..b53c144b 100644 --- a/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileWriter.java +++ b/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileWriter.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.io.Writer; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -25,6 +26,8 @@ import net.fabricmc.mappingio.MappedElementKind; import net.fabricmc.mappingio.MappingWriter; +import net.fabricmc.mappingio.format.StandardProperties; +import net.fabricmc.mappingio.format.StandardProperty; /** * A mapping writer for the ProGuard mapping format. @@ -36,8 +39,11 @@ */ public final class ProGuardFileWriter implements MappingWriter { private final Writer writer; - private int dstNamespace = -1; private final String dstNamespaceString; + private int dstNamespace = -1; + private MappedElementKind pendingMemberType; + /** srcName, srcDesc, dstName, [lineStart, lineEnd]. */ + private String[] pendingMemberData = new String[5]; /** * Constructs a ProGuard mapping writer that uses @@ -103,6 +109,8 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) thr @Override public boolean visitClass(String srcName) throws IOException { + writePendingMember(); + writer.write(toJavaClassName(srcName)); writeArrow(); return true; @@ -110,34 +118,21 @@ public boolean visitClass(String srcName) throws IOException { @Override public boolean visitField(String srcName, String srcDesc) throws IOException { - writeIndent(); - writer.write(toJavaType(srcDesc)); - writer.write(' '); - writer.write(srcName); - writeArrow(); + writePendingMember(); + + pendingMemberType = MappedElementKind.FIELD; + pendingMemberData[0] = srcName; + pendingMemberData[1] = srcDesc; return true; } @Override public boolean visitMethod(String srcName, String srcDesc) throws IOException { - Type type = Type.getMethodType(srcDesc); - writeIndent(); - writer.write(toJavaType(type.getReturnType().getDescriptor())); - writer.write(' '); - writer.write(srcName); - writer.write('('); - Type[] args = type.getArgumentTypes(); - - for (int i = 0; i < args.length; i++) { - if (i > 0) { - writer.write(','); - } - - writer.write(toJavaType(args[i].getDescriptor())); - } + writePendingMember(); - writer.write(')'); - writeArrow(); + pendingMemberType = MappedElementKind.METHOD; + pendingMemberData[0] = srcName; + pendingMemberData[1] = srcDesc; return true; } @@ -162,11 +157,22 @@ public void visitDstName(MappedElementKind targetKind, int namespace, String nam if (targetKind == MappedElementKind.CLASS) { writer.write(toJavaClassName(name)); writer.write(':'); + writer.write('\n'); } else { - writer.write(name); + pendingMemberData[2] = name; } + } - writer.write('\n'); + @Override + public void visitElementMetadata(MappedElementKind target, String key, int namespace, String value) { + assert target == pendingMemberType; + StandardProperty property = StandardProperties.getById(key); + + if (property == StandardProperties.START_LINE_NUMBER) { + pendingMemberData[3] = value; + } else if (property == StandardProperties.END_LINE_NUMBER) { + pendingMemberData[4] = value; + } } @Override @@ -174,6 +180,62 @@ public void visitComment(MappedElementKind targetKind, String comment) throws IO // ignored } + @Override + public boolean visitEnd() throws IOException { + writePendingMember(); + return true; + } + + private void writePendingMember() throws IOException { + if (pendingMemberType == null) return; + String srcName = pendingMemberData[0]; + String srcDesc = pendingMemberData[1]; + String startLine = pendingMemberData[2]; + String endLine = pendingMemberData[3]; + + writeIndent(); + + if (startLine != null) { + writer.write(startLine); + writer.write(':'); + writer.write(endLine); + writer.write(':'); + } + + if (pendingMemberType == MappedElementKind.FIELD) { + writer.write(toJavaType(srcDesc)); + writer.write(' '); + writer.write(srcName); + writeArrow(); + } else { + Type type = Type.getMethodType(srcDesc); + writer.write(toJavaType(type.getReturnType().getDescriptor())); + writer.write(' '); + writer.write(srcName); + writer.write('('); + Type[] args = type.getArgumentTypes(); + + for (int i = 0; i < args.length; i++) { + if (i > 0) { + writer.write(','); + } + + writer.write(toJavaType(args[i].getDescriptor())); + } + + writer.write(')'); + writeArrow(); + } + + if (pendingMemberData[2] != null) { + writer.write(pendingMemberData[2]); + writer.write('\n'); + } + + Arrays.fill(pendingMemberData, null); + pendingMemberType = null; + } + private void writeArrow() throws IOException { writer.write(" -> "); } diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java index 56e2517c..9d807b6d 100644 --- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java +++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java @@ -26,6 +26,9 @@ import net.fabricmc.mappingio.MappingFlag; import net.fabricmc.mappingio.MappingVisitor; import net.fabricmc.mappingio.format.ColumnFileReader; +import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.mappingio.format.StandardProperties; +import net.fabricmc.mappingio.format.StandardProperty; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; @@ -134,28 +137,27 @@ private static void read(ColumnFileReader reader, MappingVisitor visitor) throws } } else { String line = reader.nextCol(); - final String prefix = "# INTERMEDIARY-COUNTER "; - String[] parts; - - if (line.startsWith(prefix) - && (parts = line.substring(prefix.length()).split(" ")).length == 2) { - String property = null; - - switch (parts[0]) { - case "class": - property = nextIntermediaryClassProperty; - break; - case "field": - property = nextIntermediaryFieldProperty; - break; - case "method": - property = nextIntermediaryMethodProperty; - break; + + if (line.startsWith("# ") && line.length() >= 3 && line.charAt(3) != ' ') { // Metadata + line = line.substring(2); + String[] parts = line.split(" "); + String value = parts[parts.length - 1]; + String key = line.substring(0, line.lastIndexOf(value)); + + if (key.isEmpty()) { + String oldValue = value; + value = key; + key = oldValue; } + StandardProperty property = StandardProperties.getByName(key); + if (property != null) { - visitor.visitMetadata(property, parts[1]); + if (!property.isApplicableTo(format)) continue; // How did it get there? + key = property.getId(); } + + visitor.visitMetadata(key, value); } } } @@ -180,7 +182,5 @@ private static void readDstNames(ColumnFileReader reader, MappedElementKind subj } } - static final String nextIntermediaryClassProperty = "next-intermediary-class"; - static final String nextIntermediaryFieldProperty = "next-intermediary-field"; - static final String nextIntermediaryMethodProperty = "next-intermediary-method"; + private static final MappingFormat format = MappingFormat.TINY_FILE; } diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileWriter.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileWriter.java index bcdf2406..c249d4e8 100644 --- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileWriter.java +++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileWriter.java @@ -26,6 +26,9 @@ import net.fabricmc.mappingio.MappedElementKind; import net.fabricmc.mappingio.MappingFlag; import net.fabricmc.mappingio.MappingWriter; +import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.mappingio.format.StandardProperties; +import net.fabricmc.mappingio.format.StandardProperty; public final class Tiny1FileWriter implements MappingWriter { public Tiny1FileWriter(Writer writer) { @@ -59,30 +62,18 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) thr @Override public void visitMetadata(String key, String value) throws IOException { - switch (key) { - case Tiny1FileReader.nextIntermediaryClassProperty: - case Tiny1FileReader.nextIntermediaryFieldProperty: - case Tiny1FileReader.nextIntermediaryMethodProperty: - write("# INTERMEDIARY-COUNTER "); - - switch (key) { - case Tiny1FileReader.nextIntermediaryClassProperty: - write("class"); - break; - case Tiny1FileReader.nextIntermediaryFieldProperty: - write("field"); - break; - case Tiny1FileReader.nextIntermediaryMethodProperty: - write("method"); - break; - default: - throw new IllegalStateException(); - } + StandardProperty property = StandardProperties.getById(key); - write(" "); - write(value); - writeLn(); + if (property != null) { + if (!property.isApplicableTo(format)) return; + key = property.getNameFor(format); } + + write("# "); + write(key); + write(" "); + write(value); + writeLn(); } @Override @@ -191,6 +182,7 @@ private void writeTab() throws IOException { } private static final Set flags = EnumSet.of(MappingFlag.NEEDS_SRC_FIELD_DESC, MappingFlag.NEEDS_SRC_METHOD_DESC); + private static final MappingFormat format = MappingFormat.TINY_FILE; private final Writer writer; private String classSrcName; diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java index 6381d80f..7c174ea1 100644 --- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java +++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java @@ -25,6 +25,9 @@ import net.fabricmc.mappingio.MappingFlag; import net.fabricmc.mappingio.MappingVisitor; import net.fabricmc.mappingio.format.ColumnFileReader; +import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.mappingio.format.StandardProperties; +import net.fabricmc.mappingio.format.StandardProperty; public final class Tiny2FileReader { private Tiny2FileReader() { @@ -89,16 +92,22 @@ private static void read(ColumnFileReader reader, MappingVisitor visitor) throws if (visitHeader || firstIteration) { while (reader.nextLine(1)) { if (!visitHeader) { - if (!escapeNames && reader.nextCol(Tiny2Util.escapedNamesProperty)) { + if (!escapeNames && reader.nextCol(StandardProperties.ESCAPED_NAMES.getNameFor(format))) { escapeNames = true; } } else { String key = reader.nextCol(); if (key == null) throw new IOException("missing property key in line "+reader.getLineNumber()); String value = reader.nextEscapedCol(); // may be missing -> null + StandardProperty property = StandardProperties.getByName(key); - if (key.equals(Tiny2Util.escapedNamesProperty)) { - escapeNames = true; + if (property != null) { + if (!property.isApplicableTo(format)) continue; // How did it get there? + key = property.getId(); + + if (property == StandardProperties.ESCAPED_NAMES) { + escapeNames = true; + } } visitor.visitMetadata(key, value); @@ -215,4 +224,6 @@ private static void readDstNames(ColumnFileReader reader, MappedElementKind subj if (!name.isEmpty()) visitor.visitDstName(subjectKind, dstNs, name); } } + + private static final MappingFormat format = MappingFormat.TINY_2_FILE; } diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileWriter.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileWriter.java index aee1bdc0..2a37e263 100644 --- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileWriter.java +++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileWriter.java @@ -26,6 +26,9 @@ import net.fabricmc.mappingio.MappedElementKind; import net.fabricmc.mappingio.MappingFlag; import net.fabricmc.mappingio.MappingWriter; +import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.mappingio.format.StandardProperties; +import net.fabricmc.mappingio.format.StandardProperty; public final class Tiny2FileWriter implements MappingWriter { public Tiny2FileWriter(Writer writer, boolean escapeNames) { @@ -60,9 +63,16 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) thr @Override public void visitMetadata(String key, String value) throws IOException { - if (key.equals(Tiny2Util.escapedNamesProperty)) { - escapeNames = true; - wroteEscapedNamesProperty = true; + StandardProperty property = StandardProperties.getById(key); + + if (property != null) { + if (!property.isApplicableTo(format)) return; + key = property.getNameFor(format); + + if (property == StandardProperties.ESCAPED_NAMES) { + escapeNames = true; + wroteEscapedNamesProperty = true; + } } writeTab(); @@ -80,7 +90,7 @@ public void visitMetadata(String key, String value) throws IOException { public boolean visitContent() throws IOException { if (escapeNames && !wroteEscapedNamesProperty) { write("\t"); - write(Tiny2Util.escapedNamesProperty); + write(StandardProperties.ESCAPED_NAMES.getNameFor(format)); writeLn(); } @@ -201,6 +211,7 @@ private void writeTabs(int count) throws IOException { } private static final Set flags = EnumSet.of(MappingFlag.NEEDS_HEADER_METADATA, MappingFlag.NEEDS_UNIQUENESS, MappingFlag.NEEDS_SRC_FIELD_DESC, MappingFlag.NEEDS_SRC_METHOD_DESC); + private static final MappingFormat format = MappingFormat.TINY_2_FILE; private final Writer writer; private boolean escapeNames; diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2Util.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2Util.java index 68bee840..341d297d 100644 --- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2Util.java +++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2Util.java @@ -84,6 +84,4 @@ public static String unescape(String str) { private static final String toEscape = "\\\n\r\0\t"; private static final String escaped = "\\nr0t"; - - static final String escapedNamesProperty = "escaped-names"; } diff --git a/src/main/java/net/fabricmc/mappingio/format/tsrg/TsrgFileReader.java b/src/main/java/net/fabricmc/mappingio/format/tsrg/TsrgFileReader.java index 8ef79883..9eb1def0 100644 --- a/src/main/java/net/fabricmc/mappingio/format/tsrg/TsrgFileReader.java +++ b/src/main/java/net/fabricmc/mappingio/format/tsrg/TsrgFileReader.java @@ -28,6 +28,8 @@ import net.fabricmc.mappingio.MappingUtil; import net.fabricmc.mappingio.MappingVisitor; import net.fabricmc.mappingio.format.ColumnFileReader; +import net.fabricmc.mappingio.format.StandardProperties; +import net.fabricmc.mappingio.tree.MappingTree; public final class TsrgFileReader { private TsrgFileReader() { @@ -185,8 +187,8 @@ private static void readMethod(ColumnFileReader reader, int dstNsCount, MappingV while (reader.nextLine(2)) { if (reader.hasExtraIndents()) continue; - if (reader.nextCol("static")) { - // method is static + if (reader.nextCol("static")) { // method is static + visitor.visitElementMetadata(MappedElementKind.METHOD, StandardProperties.IS_STATIC.getId(), MappingTree.SRC_NAMESPACE_ID, "true"); } else { int lvIndex = reader.nextIntCol(); if (lvIndex < 0) throw new IOException("missing/invalid parameter lv-index in line "+reader.getLineNumber()); diff --git a/src/main/java/net/fabricmc/mappingio/tree/MappingTree.java b/src/main/java/net/fabricmc/mappingio/tree/MappingTree.java index b99d749b..e26aa085 100644 --- a/src/main/java/net/fabricmc/mappingio/tree/MappingTree.java +++ b/src/main/java/net/fabricmc/mappingio/tree/MappingTree.java @@ -64,6 +64,9 @@ interface ElementMapping extends ElementMappingView { MappingTree getTree(); void setDstName(String name, int namespace); + + void addMetadata(String key, int namespace, String value); + String[] removeMetadata(String key); void setComment(String comment); } diff --git a/src/main/java/net/fabricmc/mappingio/tree/MappingTreeView.java b/src/main/java/net/fabricmc/mappingio/tree/MappingTreeView.java index 3de70989..77f4895e 100644 --- a/src/main/java/net/fabricmc/mappingio/tree/MappingTreeView.java +++ b/src/main/java/net/fabricmc/mappingio/tree/MappingTreeView.java @@ -206,6 +206,9 @@ default String getName(String namespace) { } } + Collection> getMetadata(); + String[] getMetadata(String key); + String getMetadata(String key, int namespace); String getComment(); } diff --git a/src/main/java/net/fabricmc/mappingio/tree/MemoryMappingTree.java b/src/main/java/net/fabricmc/mappingio/tree/MemoryMappingTree.java index 9956cce0..be3dab61 100644 --- a/src/main/java/net/fabricmc/mappingio/tree/MemoryMappingTree.java +++ b/src/main/java/net/fabricmc/mappingio/tree/MemoryMappingTree.java @@ -17,14 +17,12 @@ package net.fabricmc.mappingio.tree; import java.io.IOException; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -206,36 +204,22 @@ private void updateDstNames(int[] nameMap) { @Override public Collection> getMetadata() { - return metadata; + return Collections.unmodifiableSet(metadata.entrySet()); } @Override public String getMetadata(String key) { - for (Map.Entry entry : metadata) { - if (entry.getKey().equals(key)) return entry.getValue(); - } - - return null; + return metadata.get(key); } @Override public void addMetadata(String key, String value) { - metadata.add(new AbstractMap.SimpleEntry<>(key, value)); + metadata.put(key, value); } @Override public String removeMetadata(String key) { - for (Iterator> it = metadata.iterator(); it.hasNext(); ) { - Map.Entry entry = it.next(); - - if (entry.getKey().equals(key)) { - it.remove(); - - return entry.getValue(); - } - } - - return null; + return metadata.remove(key); } @Override @@ -304,7 +288,7 @@ public void accept(MappingVisitor visitor, VisitOrder order) throws IOException if (visitor.visitHeader()) { visitor.visitNamespaces(srcNamespace, dstNamespaces); - for (Map.Entry entry : metadata) { + for (Map.Entry entry : metadata.entrySet()) { visitor.visitMetadata(entry.getKey(), entry.getValue()); } } @@ -391,7 +375,7 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) { @Override public void visitMetadata(String key, String value) { - this.metadata.add(new AbstractMap.SimpleEntry<>(key, value)); + addMetadata(key, value); } @Override @@ -670,8 +654,17 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept return targetKind != MappedElementKind.CLASS || currentClass.getSrcName() != null; // reject classes that never received a src name } + @Override + public void visitElementMetadata(MappedElementKind targetKind, String propertyKey, int namespace, String propertyValue) { + getCurrentEntry(targetKind).addMetadata(propertyKey, namespace, propertyValue); + } + @Override public void visitComment(MappedElementKind targetKind, String comment) { + getCurrentEntry(targetKind).setComment(comment); + } + + private Entry getCurrentEntry(MappedElementKind targetKind) { Entry entry; switch (targetKind) { @@ -686,7 +679,7 @@ public void visitComment(MappedElementKind targetKind, String comment) { } if (entry == null) throw new UnsupportedOperationException("Tried to visit comment before owning target"); - entry.setComment(comment); + return entry; } abstract static class Entry> implements ElementMapping { @@ -744,6 +737,33 @@ void updateDstNames(int[] map) { dstNames = newDstNames; } + @Override + public Collection> getMetadata() { + return Collections.unmodifiableSet(metadata.entrySet()); + } + + @Override + public String[] getMetadata(String key) { + return metadata.get(key); + } + + @Override + public String getMetadata(String key, int namespace) { + return metadata.get(key)[namespace]; + } + + @Override + public void addMetadata(String key, int namespace, String value) { + String[] values = metadata.getOrDefault(key, new String[dstNames.length + 1]); + values[namespace] = value; + metadata.put(key, values); + } + + @Override + public String[] removeMetadata(String key) { + return metadata.remove(key); + } + @Override public final String getComment() { return comment; @@ -794,6 +814,7 @@ protected void copyFrom(T o, boolean replace) { // TODO: copy args+vars } + private final Map metadata = new HashMap<>(); protected String srcName; protected String[] dstNames; protected String comment; @@ -1739,7 +1760,7 @@ public String toString() { private boolean indexByDstNames; private String srcNamespace; private List dstNamespaces = Collections.emptyList(); - private final List> metadata = new ArrayList<>(); + private final Map metadata = new HashMap<>(); private final Map classesBySrcName = new LinkedHashMap<>(); private Map[] classesByDstNames;