Skip to content

Commit bc58b6f

Browse files
Fix conflicts caused by MappingsMerger (#1171)
* only complete official namespaces for unobfuscated members * TinyRemapperHelper.create does not like null names * add MappingsMergerTest
1 parent 4d3c0a8 commit bc58b6f

File tree

5 files changed

+583
-12
lines changed

5 files changed

+583
-12
lines changed

src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@
3434
import java.util.regex.Pattern;
3535

3636
import com.google.common.base.Stopwatch;
37+
import org.jetbrains.annotations.VisibleForTesting;
3738
import org.slf4j.Logger;
3839
import org.slf4j.LoggerFactory;
3940

4041
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
4142
import net.fabricmc.loom.configuration.providers.mappings.IntermediateMappingsService;
4243
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
43-
import net.fabricmc.mappingio.adapter.MappingNsCompleter;
4444
import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;
4545
import net.fabricmc.mappingio.format.tiny.Tiny2FileReader;
4646
import net.fabricmc.mappingio.format.tiny.Tiny2FileWriter;
@@ -63,7 +63,8 @@ public static void mergeAndSaveMappings(Path from, Path out, MinecraftProvider m
6363
LOGGER.info(":merged mappings in " + stopwatch.stop());
6464
}
6565

66-
private static void mergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException {
66+
@VisibleForTesting
67+
public static void mergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException {
6768
MemoryMappingTree intermediaryTree = new MemoryMappingTree();
6869
intermediateMappingsService.getMemoryMappingTree().accept(new MappingSourceNsSwitch(intermediaryTree, MappingsNamespace.INTERMEDIARY.toString()));
6970

@@ -72,7 +73,7 @@ private static void mergeAndSaveMappings(Path from, Path out, IntermediateMappin
7273
}
7374

7475
MemoryMappingTree officialTree = new MemoryMappingTree();
75-
MappingNsCompleter nsCompleter = new MappingNsCompleter(officialTree, Map.of(MappingsNamespace.OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString()));
76+
UnobfuscatedMappingNsCompleter nsCompleter = new UnobfuscatedMappingNsCompleter(officialTree, MappingsNamespace.NAMED.toString(), Map.of(MappingsNamespace.OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString()));
7677
MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(nsCompleter, MappingsNamespace.OFFICIAL.toString());
7778
intermediaryTree.accept(nsSwitch);
7879

@@ -83,7 +84,8 @@ private static void mergeAndSaveMappings(Path from, Path out, IntermediateMappin
8384
}
8485
}
8586

86-
private static void legacyMergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException {
87+
@VisibleForTesting
88+
public static void legacyMergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException {
8789
MemoryMappingTree intermediaryTree = new MemoryMappingTree();
8890
intermediateMappingsService.getMemoryMappingTree().accept(intermediaryTree);
8991

@@ -92,7 +94,7 @@ private static void legacyMergeAndSaveMappings(Path from, Path out, Intermediate
9294
}
9395

9496
MemoryMappingTree officialTree = new MemoryMappingTree();
95-
MappingNsCompleter nsCompleter = new MappingNsCompleter(officialTree, Map.of(MappingsNamespace.CLIENT_OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString(), MappingsNamespace.SERVER_OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString()));
97+
UnobfuscatedMappingNsCompleter nsCompleter = new UnobfuscatedMappingNsCompleter(officialTree, MappingsNamespace.NAMED.toString(), Map.of(MappingsNamespace.CLIENT_OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString(), MappingsNamespace.SERVER_OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString()));
9698
intermediaryTree.accept(nsCompleter);
9799

98100
// versions this old strip inner class attributes
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
/*
2+
* This file is part of fabric-loom, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) 2024 FabricMC
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
package net.fabricmc.loom.configuration.providers.mappings.tiny;
26+
27+
import java.io.IOException;
28+
import java.util.Arrays;
29+
import java.util.List;
30+
import java.util.Map;
31+
32+
import org.jetbrains.annotations.Nullable;
33+
34+
import net.fabricmc.mappingio.MappedElementKind;
35+
import net.fabricmc.mappingio.MappingVisitor;
36+
import net.fabricmc.mappingio.adapter.ForwardingMappingVisitor;
37+
38+
/**
39+
* Adapted from {@link net.fabricmc.mappingio.adapter.MappingNsCompleter}.
40+
* This visitor completes any empty namespace with some alternative namespace
41+
* only if that alternative namespace is equal to some test namespace.
42+
* Mostly this will be used to complete official namespaces with intermediary
43+
* names only if those intermediary names are equal to the named names.
44+
*/
45+
public final class UnobfuscatedMappingNsCompleter extends ForwardingMappingVisitor {
46+
private final String testNs;
47+
private final Map<String, String> alternatives;
48+
private int testNsId;
49+
private int[] alternativesMapping;
50+
51+
private String srcName;
52+
private String[] dstNames;
53+
private boolean[] unobf;
54+
private boolean[] lastMethodUnobf;
55+
56+
private boolean relayHeaderOrMetadata;
57+
58+
public UnobfuscatedMappingNsCompleter(MappingVisitor next, String testNs, Map<String, String> alternatives) {
59+
super(next);
60+
61+
this.testNs = testNs;
62+
this.alternatives = alternatives;
63+
}
64+
65+
@Override
66+
public boolean visitHeader() throws IOException {
67+
relayHeaderOrMetadata = next.visitHeader();
68+
69+
return true;
70+
}
71+
72+
@Override
73+
public void visitNamespaces(String srcNamespace, List<String> dstNamespaces) throws IOException {
74+
int count = dstNamespaces.size();
75+
testNsId = -1;
76+
alternativesMapping = new int[count];
77+
dstNames = new String[count];
78+
unobf = new boolean[count + 1]; // contains src ns as well
79+
lastMethodUnobf = new boolean[count + 1]; // contains src ns as well
80+
81+
for (int i = 0; i < count; i++) {
82+
String dst = dstNamespaces.get(i);
83+
84+
if (testNs.equals(dst)) {
85+
testNsId = i;
86+
}
87+
88+
String src = alternatives.get(dst);
89+
int srcIdx;
90+
91+
if (src == null) {
92+
srcIdx = i;
93+
} else if (src.equals(srcNamespace)) {
94+
srcIdx = -1;
95+
} else {
96+
srcIdx = dstNamespaces.indexOf(src);
97+
if (srcIdx < 0) throw new RuntimeException("invalid alternative mapping ns "+src+": not in "+dstNamespaces+" or "+srcNamespace);
98+
}
99+
100+
alternativesMapping[i] = srcIdx;
101+
}
102+
103+
if (testNsId == -1 && !testNs.equals(srcNamespace)) throw new RuntimeException("test namespace " + testNs + " not present in src and dst namespaces!");
104+
105+
if (relayHeaderOrMetadata) next.visitNamespaces(srcNamespace, dstNamespaces);
106+
}
107+
108+
@Override
109+
public void visitMetadata(String key, @Nullable String value) throws IOException {
110+
if (relayHeaderOrMetadata) next.visitMetadata(key, value);
111+
}
112+
113+
@Override
114+
public boolean visitContent() throws IOException {
115+
relayHeaderOrMetadata = true; // for in-content metadata
116+
117+
return next.visitContent();
118+
}
119+
120+
@Override
121+
public boolean visitClass(String srcName) throws IOException {
122+
this.srcName = srcName;
123+
124+
return next.visitClass(srcName);
125+
}
126+
127+
@Override
128+
public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException {
129+
this.srcName = srcName;
130+
131+
return next.visitField(srcName, srcDesc);
132+
}
133+
134+
@Override
135+
public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException {
136+
this.srcName = srcName;
137+
138+
return next.visitMethod(srcName, srcDesc);
139+
}
140+
141+
@Override
142+
public boolean visitMethodArg(int argPosition, int lvIndex, @Nullable String srcName) throws IOException {
143+
this.srcName = srcName;
144+
145+
return next.visitMethodArg(argPosition, lvIndex, srcName);
146+
}
147+
148+
@Override
149+
public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, @Nullable String srcName) throws IOException {
150+
this.srcName = srcName;
151+
152+
return next.visitMethodVar(lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcName);
153+
}
154+
155+
@Override
156+
public void visitDstName(MappedElementKind targetKind, int namespace, String name) {
157+
dstNames[namespace] = name;
158+
}
159+
160+
@Override
161+
public boolean visitElementContent(MappedElementKind targetKind) throws IOException {
162+
for (int ns : alternativesMapping) {
163+
int idx = ns + 1; // offset by 1 bc src ns is -1
164+
165+
if (targetKind == MappedElementKind.METHOD_ARG || targetKind == MappedElementKind.METHOD_VAR) {
166+
unobf[idx] = lastMethodUnobf[idx];
167+
} else if (ns == testNsId) {
168+
unobf[idx] = true;
169+
170+
if (targetKind == MappedElementKind.METHOD) {
171+
lastMethodUnobf[idx] = true;
172+
}
173+
} else if (!unobf[idx]) { // only check each ns once
174+
String name = ns == -1 ? srcName : dstNames[ns];
175+
String testName = dstNames[testNsId];
176+
177+
if (testName != null && testName.equals(name)) {
178+
unobf[idx] = true;
179+
180+
if (targetKind == MappedElementKind.METHOD) {
181+
lastMethodUnobf[idx] = true;
182+
}
183+
}
184+
}
185+
}
186+
187+
nsLoop: for (int i = 0; i < dstNames.length; i++) {
188+
String name = dstNames[i];
189+
190+
if (name == null) {
191+
int src = i;
192+
long visited = 1L << src;
193+
194+
do {
195+
int newSrc = alternativesMapping[src];
196+
197+
if (newSrc < 0) { // mapping to src name
198+
if (unobf[newSrc + 1]) {
199+
name = srcName;
200+
break; // srcName must never be null
201+
} else {
202+
continue nsLoop;
203+
}
204+
} else if (newSrc == src) { // no-op (identity) mapping, explicit in case src > 64
205+
continue nsLoop; // always null
206+
} else if ((visited & 1L << newSrc) != 0) { // cyclic mapping
207+
continue nsLoop; // always null
208+
} else {
209+
if (unobf[newSrc + 1]) {
210+
src = newSrc;
211+
name = dstNames[src];
212+
visited |= 1L << src;
213+
} else {
214+
continue nsLoop;
215+
}
216+
}
217+
} while (name == null);
218+
219+
assert name != null;
220+
}
221+
222+
next.visitDstName(targetKind, i, name);
223+
}
224+
225+
Arrays.fill(dstNames, null);
226+
Arrays.fill(unobf, false);
227+
Arrays.fill(lastMethodUnobf, false);
228+
229+
return next.visitElementContent(targetKind);
230+
}
231+
}

src/main/java/net/fabricmc/loom/util/TinyRemapperHelper.java

+36-7
Original file line numberDiff line numberDiff line change
@@ -111,22 +111,51 @@ public static IMappingProvider create(MappingTree mappings, String from, String
111111

112112
for (MappingTree.ClassMapping classDef : mappings.getClasses()) {
113113
String className = classDef.getName(fromId);
114-
String dstName = classDef.getName(toId);
115114

116-
if (dstName == null) {
115+
if (className == null) {
116+
continue;
117+
}
118+
119+
String dstClassName = classDef.getName(toId);
120+
121+
if (dstClassName == null) {
117122
// Unsure if this is correct, should be better than crashing tho.
118-
dstName = className;
123+
dstClassName = className;
119124
}
120125

121-
acceptor.acceptClass(className, dstName);
126+
acceptor.acceptClass(className, dstClassName);
122127

123128
for (MappingTree.FieldMapping field : classDef.getFields()) {
124-
acceptor.acceptField(memberOf(className, field.getName(fromId), field.getDesc(fromId)), field.getName(toId));
129+
String fieldName = field.getName(fromId);
130+
131+
if (fieldName == null) {
132+
continue;
133+
}
134+
135+
String dstFieldName = field.getName(toId);
136+
137+
if (dstFieldName == null) {
138+
dstFieldName = fieldName;
139+
}
140+
141+
acceptor.acceptField(memberOf(className, fieldName, field.getDesc(fromId)), dstFieldName);
125142
}
126143

127144
for (MappingTree.MethodMapping method : classDef.getMethods()) {
128-
IMappingProvider.Member methodIdentifier = memberOf(className, method.getName(fromId), method.getDesc(fromId));
129-
acceptor.acceptMethod(methodIdentifier, method.getName(toId));
145+
String methodName = method.getName(fromId);
146+
147+
if (methodName == null) {
148+
continue;
149+
}
150+
151+
String dstMethodName = method.getName(toId);
152+
153+
if (dstMethodName == null) {
154+
dstMethodName = methodName;
155+
}
156+
157+
IMappingProvider.Member methodIdentifier = memberOf(className, methodName, method.getDesc(fromId));
158+
acceptor.acceptMethod(methodIdentifier, dstMethodName);
130159

131160
if (remapLocalVariables) {
132161
for (MappingTree.MethodArgMapping parameter : method.getArgs()) {

src/test/groovy/net/fabricmc/loom/test/unit/LoomMocks.groovy

+18
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424

2525
package net.fabricmc.loom.test.unit
2626

27+
import java.nio.file.Path
2728
import java.util.function.Function
2829

2930
import net.fabricmc.loom.configuration.providers.mappings.IntermediaryMappingsProvider
31+
import net.fabricmc.loom.configuration.providers.mappings.IntermediateMappingsService
3032
import net.fabricmc.loom.test.util.GradleTestUtil
3133
import net.fabricmc.loom.util.download.Download
3234

@@ -49,4 +51,20 @@ class LoomMocks {
4951
when(mock.getRefreshDeps()).thenReturn(refreshDeps)
5052
return mock
5153
}
54+
55+
static IntermediateMappingsService.Options intermediateMappingsServiceOptionsMock(Path intermediaryTiny, String expectedSrcNs) {
56+
def intermediaryTinyProperty = GradleTestUtil.mockProperty(intermediaryTiny)
57+
def expectedSrcNsProperty = GradleTestUtil.mockProperty(expectedSrcNs)
58+
59+
def mock = spy(IntermediateMappingsService.Options.class)
60+
when(mock.getIntermediaryTiny()).thenReturn(intermediaryTinyProperty)
61+
when(mock.getExpectedSrcNs()).thenReturn(expectedSrcNsProperty)
62+
return mock
63+
}
64+
65+
static IntermediateMappingsService intermediateMappingsServiceMock(IntermediateMappingsService.Options options) {
66+
def mock = spy(IntermediateMappingsService.class)
67+
when(mock.getOptions()).thenReturn(options)
68+
return mock
69+
}
5270
}

0 commit comments

Comments
 (0)