Skip to content

Commit 485410e

Browse files
authored
Fix writer NPEs when member descriptor or metadata is null (#123)
Also fixes SRG writer omitting fields with null srcDesc
1 parent 5c7eac2 commit 485410e

31 files changed

+120
-50
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
44
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
55

66
## [Unreleased]
7+
- Added a simplified `MappingNsCompleter` constructor for completing all destination names with the source names
78
- Made `OuterClassNamePropagator` configurable
89
- Made Enigma writer always output destination names if visited explicitly, establishing consistency across all writers
9-
- Added a simplified `MappingNsCompleter` constructor for completing all destination names with the source names
1010
- Adjusted format detection to only return ENIGMA_DIR for non-empty directories with at least one `.mapping` file
11+
- Fixed writer NPEs when metadata or member source descriptors are null
12+
- Fixed SRG writer omitting fields with missing source descriptors
1113

1214
## [0.7.1] - 2025-01-07
1315
- Restored the ability to read source-namespace-only mapping files, even if not spec-compliant

src/main/java/net/fabricmc/mappingio/MappingVisitor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* </ul>
3838
*
3939
* <p>The elements with a skip-return (Header/Content/Class/Field/Method/Arg/Var/ElementContent) abort processing the
40-
* remainder of their associated item in the above listing if requested by a {@code true} return value. For example
40+
* remainder of their associated item in the above listing if requested by a {@code false} return value. For example
4141
* skipping in Class does neither DstName nor ElementContent, but continues with another class or End.
4242
*
4343
* <p>Returning {@code false} in End requests another complete visitation pass if the flag

src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaWriterBase.java

+8
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ public boolean visitClass(String srcName) throws IOException {
5959

6060
@Override
6161
public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException {
62+
if (srcDesc == null) {
63+
return false;
64+
}
65+
6266
writeIndent(0);
6367
writer.write("FIELD ");
6468
writer.write(srcName);
@@ -70,6 +74,10 @@ public boolean visitField(String srcName, @Nullable String srcDesc) throws IOExc
7074

7175
@Override
7276
public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException {
77+
if (srcDesc == null) {
78+
return false;
79+
}
80+
7381
writeIndent(0);
7482
writer.write("METHOD ");
7583
writer.write(srcName);

src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapConstants.java

+1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ private MigrationMapConstants() {
2525

2626
public static final String ORDER_KEY = "migrationmap:order";
2727
public static final String MISSING_NAME = "Unnamed migration map";
28+
public static final String DEFAULT_ORDER = "0";
2829
}

src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileWriter.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void close() throws IOException {
5555
if (!wroteOrder) {
5656
xmlWriter.writeCharacters("\n\t");
5757
xmlWriter.writeEmptyElement("order");
58-
xmlWriter.writeAttribute("value", "0");
58+
xmlWriter.writeAttribute("value", MigrationMapConstants.DEFAULT_ORDER);
5959
}
6060

6161
xmlWriter.writeCharacters("\n");
@@ -101,17 +101,22 @@ public void visitMetadata(String key, @Nullable String value) throws IOException
101101
try {
102102
switch (key) {
103103
case "name":
104+
if (value == null) return;
104105
wroteName = true;
105106
break;
106107
case MigrationMapConstants.ORDER_KEY:
108+
if (value == null) return;
107109
wroteOrder = true;
108110
key = "order";
109111
break;
110112
}
111113

112114
xmlWriter.writeCharacters("\n\t");
113115
xmlWriter.writeEmptyElement(key);
114-
xmlWriter.writeAttribute("value", value);
116+
117+
if (value != null) {
118+
xmlWriter.writeAttribute("value", value);
119+
}
115120
} catch (XMLStreamException e) {
116121
throw new IOException(e);
117122
}

src/main/java/net/fabricmc/mappingio/format/jobf/JobfFileWriter.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public boolean visitClass(String srcName) throws IOException {
6161

6262
@Override
6363
public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException {
64+
if (srcDesc == null) {
65+
return false;
66+
}
67+
6468
memberSrcName = srcName;
6569
memberSrcDesc = srcDesc;
6670
dstName = null;
@@ -70,6 +74,10 @@ public boolean visitField(String srcName, @Nullable String srcDesc) throws IOExc
7074

7175
@Override
7276
public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException {
77+
if (srcDesc == null) {
78+
return false;
79+
}
80+
7381
memberSrcName = srcName;
7482
memberSrcDesc = srcDesc;
7583
dstName = null;
@@ -114,7 +122,6 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept
114122
write("c ");
115123
} else if ((isField = targetKind == MappedElementKind.FIELD)
116124
|| targetKind == MappedElementKind.METHOD) {
117-
if (memberSrcDesc == null) return false;
118125
write(isField ? "f " : "m ");
119126
} else {
120127
throw new IllegalStateException("unexpected invocation for "+targetKind);

src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileWriter.java

+8
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ public boolean visitClass(String srcName) throws IOException {
110110

111111
@Override
112112
public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException {
113+
if (srcDesc == null) {
114+
return false;
115+
}
116+
113117
memberSrcName = srcName;
114118
memberSrcDesc = srcDesc;
115119

@@ -118,6 +122,10 @@ public boolean visitField(String srcName, @Nullable String srcDesc) throws IOExc
118122

119123
@Override
120124
public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException {
125+
if (srcDesc == null) {
126+
return false;
127+
}
128+
121129
memberSrcName = srcName;
122130
memberSrcDesc = srcDesc;
123131

src/main/java/net/fabricmc/mappingio/format/simple/RecafSimpleFileWriter.java

+4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ public boolean visitField(String srcName, String srcDesc) throws IOException {
6666

6767
@Override
6868
public boolean visitMethod(String srcName, String srcDesc) throws IOException {
69+
if (srcDesc == null) {
70+
return false;
71+
}
72+
6973
memberSrcName = srcName;
7074
memberSrcDesc = srcDesc;
7175

src/main/java/net/fabricmc/mappingio/format/srg/CsrgFileWriter.java

+4
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ public boolean visitField(String srcName, @Nullable String srcDesc) throws IOExc
6868

6969
@Override
7070
public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException {
71+
if (srcDesc == null) {
72+
return false;
73+
}
74+
7175
memberSrcName = srcName;
7276
methodSrcDesc = srcDesc;
7377

src/main/java/net/fabricmc/mappingio/format/srg/JamFileWriter.java

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public boolean visitClass(String srcName) throws IOException {
6161

6262
@Override
6363
public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException {
64+
if (srcDesc == null) {
65+
return false;
66+
}
67+
6468
memberSrcName = srcName;
6569
memberSrcDesc = srcDesc;
6670
memberDstName = null;

src/main/java/net/fabricmc/mappingio/format/srg/SrgFileWriter.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ public boolean visitClass(String srcName) throws IOException {
6363

6464
@Override
6565
public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException {
66+
if (xsrg && srcDesc == null) {
67+
return false;
68+
}
69+
6670
memberSrcName = srcName;
6771
memberSrcDesc = srcDesc;
6872
memberDstName = null;
@@ -73,6 +77,10 @@ public boolean visitField(String srcName, @Nullable String srcDesc) throws IOExc
7377

7478
@Override
7579
public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException {
80+
if (srcDesc == null) {
81+
return false;
82+
}
83+
7684
memberSrcName = srcName;
7785
memberSrcDesc = srcDesc;
7886
memberDstName = null;
@@ -123,11 +131,11 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept
123131
write("CL: ");
124132
break;
125133
case FIELD:
126-
if (memberSrcDesc == null || memberDstName == null || (xsrg && memberDstDesc == null)) return false;
134+
if (memberDstName == null || xsrg && memberDstDesc == null) return false;
127135
write("FD: ");
128136
break;
129137
case METHOD:
130-
if (memberSrcDesc == null || memberDstName == null || memberDstDesc == null) return false;
138+
if (memberDstName == null || memberDstDesc == null) return false;
131139
write("MD: ");
132140
break;
133141
default:

src/main/java/net/fabricmc/mappingio/format/srg/TsrgFileWriter.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ public boolean visitField(String srcName, @Nullable String srcDesc) throws IOExc
9191

9292
@Override
9393
public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException {
94+
if (srcDesc == null) {
95+
return false;
96+
}
97+
9498
memberSrcName = srcName;
9599
memberSrcDesc = srcDesc;
96100
hasAnyDstNames = false;
@@ -183,7 +187,7 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept
183187
write(srcName);
184188

185189
if (targetKind == MappedElementKind.METHOD
186-
|| (targetKind == MappedElementKind.FIELD && tsrg2)) {
190+
|| (targetKind == MappedElementKind.FIELD && tsrg2 && memberSrcDesc != null)) {
187191
writeSpace();
188192
write(memberSrcDesc);
189193
}

src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileWriter.java

+8
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ public boolean visitClass(String srcName) throws IOException {
100100

101101
@Override
102102
public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException {
103+
if (srcDesc == null) {
104+
return false;
105+
}
106+
103107
memberSrcName = srcName;
104108
memberSrcDesc = srcDesc;
105109

@@ -108,6 +112,10 @@ public boolean visitField(String srcName, @Nullable String srcDesc) throws IOExc
108112

109113
@Override
110114
public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException {
115+
if (srcDesc == null) {
116+
return false;
117+
}
118+
111119
memberSrcName = srcName;
112120
memberSrcDesc = srcDesc;
113121

src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileWriter.java

+8
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ public boolean visitClass(String srcName) throws IOException {
108108

109109
@Override
110110
public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException {
111+
if (srcDesc == null) {
112+
return false;
113+
}
114+
111115
write("\tf\t");
112116
writeName(srcDesc);
113117
writeTab();
@@ -118,6 +122,10 @@ public boolean visitField(String srcName, @Nullable String srcDesc) throws IOExc
118122

119123
@Override
120124
public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException {
125+
if (srcDesc == null) {
126+
return false;
127+
}
128+
121129
write("\tm\t");
122130
writeName(srcDesc);
123131
writeTab();

src/test/java/net/fabricmc/mappingio/test/NameGen.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ private String getPrefix(MappedElementKind kind) {
253253
private static final String argPrefix = "param";
254254
private static final String varPrefix = "var";
255255
private static final String comment = "This is a comment";
256-
private static final List<String> fldDescs = Collections.unmodifiableList(Arrays.asList("I", "Lcls;", "Lpkg/cls;", "[I"));
257-
private static final List<String> mthDescs = Collections.unmodifiableList(Arrays.asList("()I", "(I)V", "(Lcls;)Lcls;", "(ILcls;)Lpkg/cls;", "(Lcls;[I)[[B"));
256+
private static final List<String> fldDescs = Collections.unmodifiableList(Arrays.asList("I", "Lcls;", "Lpkg/cls;", "[I", null));
257+
private static final List<String> mthDescs = Collections.unmodifiableList(Arrays.asList("()I", "(I)V", "(Lcls;)Lcls;", "(ILcls;)Lpkg/cls;", "(Lcls;[I)[[B", null));
258258
private static final MappedElementKind clsKind = MappedElementKind.CLASS;
259259
private static final MappedElementKind fldKind = MappedElementKind.FIELD;
260260
private static final MappedElementKind mthKind = MappedElementKind.METHOD;

src/test/java/net/fabricmc/mappingio/test/TestMappings.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ public static <T extends MappingVisitor> T generateValid(T target) throws IOExce
5555
if (delegate.visitHeader()) {
5656
delegate.visitNamespaces(MappingUtil.NS_SOURCE_FALLBACK, Arrays.asList(MappingUtil.NS_TARGET_FALLBACK, MappingUtil.NS_TARGET_FALLBACK + "2"));
5757
delegate.visitMetadata("name", "valid");
58-
delegate.visitMetadata(MigrationMapConstants.ORDER_KEY, "0");
58+
delegate.visitMetadata(MigrationMapConstants.ORDER_KEY, MigrationMapConstants.DEFAULT_ORDER);
59+
delegate.visitMetadata("property-with-empty-value", "");
60+
delegate.visitMetadata("property-with-null-value", null);
5961
}
6062

6163
if (delegate.visitContent()) {

src/test/java/net/fabricmc/mappingio/test/visitors/SubsetAssertingVisitor.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ public boolean visitField(String srcClsName, String srcName, @Nullable String sr
209209
boolean subHasDstNames = subFeatures.fields().dstNames() != FeaturePresence.ABSENT;
210210
boolean supHasDstDescs = supFeatures.fields().dstDescs() != FeaturePresence.ABSENT;
211211
boolean subHasDstDescs = subFeatures.fields().dstDescs() != FeaturePresence.ABSENT;
212+
boolean supRequiresSrcDescs = supFeatures.fields().srcDescs() == FeaturePresence.REQUIRED;
212213

213214
if (supFld == null) { // supTree doesn't have this field, ensure the incoming mappings don't have any data for it
214215
String[] subDstNames = null;
@@ -217,8 +218,11 @@ public boolean visitField(String srcClsName, String srcName, @Nullable String sr
217218
if (supHasDstNames && subHasDstNames) subDstNames = supFeatures.hasNamespaces() || dstNames == null ? dstNames : new String[]{dstNames[subNsIfSupNotNamespaced]};
218219
if (supHasDstDescs && subHasDstDescs) subDstDescs = supFeatures.hasNamespaces() || dstDescs == null ? dstDescs : new String[]{dstDescs[subNsIfSupNotNamespaced]};
219220

220-
assertTrue(isEmpty(subDstNames) && isEmpty(subDstDescs), "Incoming field not contained in supTree: " + subFldId);
221-
return true;
221+
boolean noData = isEmpty(subDstNames) && isEmpty(subDstDescs);
222+
boolean missingRequiredSrcDesc = supRequiresSrcDescs && srcDesc == null;
223+
224+
assertTrue(noData || missingRequiredSrcDesc, "Incoming field not contained in supTree: " + subFldId);
225+
return !missingRequiredSrcDesc;
222226
}
223227

224228
String supFldId = srcClsName + "#" + srcName + ":" + supFld.getSrcDesc();
@@ -289,6 +293,7 @@ public boolean visitMethod(String srcClsName, String srcName, @Nullable String s
289293
boolean subHasDstNames = subFeatures.methods().dstNames() != FeaturePresence.ABSENT;
290294
boolean supHasDstDescs = supFeatures.methods().dstDescs() != FeaturePresence.ABSENT;
291295
boolean subHasDstDescs = subFeatures.methods().dstDescs() != FeaturePresence.ABSENT;
296+
boolean supRequiresSrcDescs = supFeatures.methods().srcDescs() == FeaturePresence.REQUIRED;
292297

293298
if (supMth == null) { // supTree doesn't have this method, ensure the incoming mappings don't have any data for it
294299
String[] subDstNames = null;
@@ -297,8 +302,11 @@ public boolean visitMethod(String srcClsName, String srcName, @Nullable String s
297302
if (supHasDstNames && subHasDstNames) subDstNames = supFeatures.hasNamespaces() || dstNames == null ? dstNames : new String[]{dstNames[subNsIfSupNotNamespaced]};
298303
if (supHasDstDescs && subHasDstDescs) subDstDescs = supFeatures.hasNamespaces() || dstDescs == null ? dstDescs : new String[]{dstDescs[subNsIfSupNotNamespaced]};
299304

300-
assertTrue(isEmpty(subDstNames) && isEmpty(subDstDescs), "Incoming method not contained in supTree: " + subMthId);
301-
return true;
305+
boolean noData = isEmpty(subDstNames) && isEmpty(subDstDescs);
306+
boolean missingRequiredSrcDesc = supRequiresSrcDescs && srcDesc == null;
307+
308+
assertTrue(noData || missingRequiredSrcDesc, "Incoming method not contained in supTree: " + subMthId);
309+
return !missingRequiredSrcDesc;
302310
}
303311

304312
String supMthId = srcClsName + "#" + srcName + supMth.getSrcDesc();

src/test/resources/reading/holes/enigma-dir/class_35.mapping

+3-7
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ CLASS class_35
44
FIELD field_3 Lpkg/cls;
55
FIELD field_4 [I
66
COMMENT This is a comment
7-
FIELD field_5 field5Ns0Rename I
8-
COMMENT This is a comment
9-
FIELD field_6 Lcls;
7+
FIELD field_6 I
108
COMMENT This is a comment
119
METHOD method_1 ()I
1210
METHOD method_2 method2Ns0Rename (I)V
@@ -15,9 +13,7 @@ CLASS class_35
1513
COMMENT This is a comment
1614
METHOD method_5 method5Ns0Rename (Lcls;[I)[[B
1715
COMMENT This is a comment
18-
METHOD method_6 ()I
19-
COMMENT This is a comment
20-
METHOD method_7 (I)V
16+
METHOD method_7 ()I
2117
ARG 1
2218
ARG 3
2319
ARG 5 param3Ns0Rename
@@ -27,4 +23,4 @@ CLASS class_35
2723
COMMENT This is a comment
2824
ARG 11
2925
COMMENT This is a comment
30-
METHOD method_8 (Lcls;)Lcls;
26+
METHOD method_8 (I)V

src/test/resources/reading/holes/enigma.mappings

+3-7
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ CLASS class_35
4747
FIELD field_3 Lpkg/cls;
4848
FIELD field_4 [I
4949
COMMENT This is a comment
50-
FIELD field_5 field5Ns0Rename I
51-
COMMENT This is a comment
52-
FIELD field_6 Lcls;
50+
FIELD field_6 I
5351
COMMENT This is a comment
5452
METHOD method_1 ()I
5553
METHOD method_2 method2Ns0Rename (I)V
@@ -58,9 +56,7 @@ CLASS class_35
5856
COMMENT This is a comment
5957
METHOD method_5 method5Ns0Rename (Lcls;[I)[[B
6058
COMMENT This is a comment
61-
METHOD method_6 ()I
62-
COMMENT This is a comment
63-
METHOD method_7 (I)V
59+
METHOD method_7 ()I
6460
ARG 1
6561
ARG 3
6662
ARG 5 param3Ns0Rename
@@ -70,4 +66,4 @@ CLASS class_35
7066
COMMENT This is a comment
7167
ARG 11
7268
COMMENT This is a comment
73-
METHOD method_8 (Lcls;)Lcls;
69+
METHOD method_8 (I)V

0 commit comments

Comments
 (0)