From 53db9b3c25a93e8aca594fcb725704d30cf746d0 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Tue, 20 Jul 2021 17:30:02 +0800 Subject: [PATCH] Stub intermediaries Signed-off-by: shedaniel --- .../loom/api/LoomGradleExtensionAPI.java | 2 + .../mappings/MappingsProviderImpl.java | 73 ++++++ .../extension/LoomGradleExtensionApiImpl.java | 8 + .../extension/MinecraftGradleExtension.java | 6 + .../net/fabricmc/loom/util/srg/MCPReader.java | 216 +++++++++++++----- 5 files changed, 244 insertions(+), 61 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 1e939f3c..18a7f2e1 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -270,4 +270,6 @@ public interface LoomGradleExtensionAPI { ForgeExtensionAPI getForge(); void forge(Action action); + + Property getStubIntermediaries(); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java index 23bf71c8..d61a8843 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -37,6 +37,7 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -63,6 +64,7 @@ import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerJarProcessor; import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider; +import net.fabricmc.loom.configuration.providers.MinecraftProvider; import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider; import net.fabricmc.loom.configuration.providers.forge.SrgProvider; @@ -74,6 +76,7 @@ import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.loom.util.srg.MCPReader; import net.fabricmc.loom.util.srg.SrgMerger; import net.fabricmc.loom.util.srg.SrgNamedWriter; +import net.fabricmc.mappingio.MappedElementKind; import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.adapter.MappingNsCompleter; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; @@ -86,6 +89,11 @@ import net.fabricmc.stitch.Command; import net.fabricmc.stitch.commands.CommandProposeFieldNames; import net.fabricmc.stitch.commands.tinyv2.TinyFile; import net.fabricmc.stitch.commands.tinyv2.TinyV2Writer; +import net.fabricmc.stitch.representation.JarClassEntry; +import net.fabricmc.stitch.representation.JarFieldEntry; +import net.fabricmc.stitch.representation.JarMethodEntry; +import net.fabricmc.stitch.representation.JarReader; +import net.fabricmc.stitch.representation.JarRootEntry; public class MappingsProviderImpl extends DependencyProvider implements MappingsProvider { public MinecraftMappedProvider mappedProvider; @@ -573,6 +581,22 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings if (intermediaryTiny == null) { intermediaryTiny = getMinecraftProvider().file("intermediary-v2.tiny").toPath(); + if (getExtension().getStubIntermediaries().get()) { + intermediaryTiny = getMinecraftProvider().file("stub-intermediary-v2.tiny").toPath(); + + if (isRefreshDeps() && !hasRefreshed) { + Files.deleteIfExists(intermediaryTiny); + } + + if (Files.exists(intermediaryTiny)) { + return intermediaryTiny; + } + + hasRefreshed = true; + generateIntermediary(intermediaryTiny); + return intermediaryTiny; + } + if (!Files.exists(intermediaryTiny) || (isRefreshDeps() && !hasRefreshed)) { hasRefreshed = true; @@ -588,6 +612,55 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings return intermediaryTiny; } + private void generateIntermediary(Path output) throws IOException { + MinecraftProviderImpl provider = getExtension().getMinecraftProvider(); + JarRootEntry entry = new JarRootEntry(provider.getMergedJar()); + MemoryMappingTree tree = new MemoryMappingTree(); + + MappingNsCompleter visitor = new MappingNsCompleter(tree, Collections.singletonMap(MappingsNamespace.INTERMEDIARY.toString(), MappingsNamespace.OFFICIAL.toString()), true); + + if (visitor.visitHeader()) { + visitor.visitNamespaces(MappingsNamespace.OFFICIAL.toString(), Collections.emptyList()); + } + + if (visitor.visitContent()) { + try { + JarReader reader = new JarReader(entry); + reader.apply(); + } catch (IOException e) { + e.printStackTrace(); + } + + for (JarClassEntry classEntry : entry.getAllClasses()) { + if (visitor.visitClass(classEntry.getFullyQualifiedName())) { + if (!visitor.visitElementContent(MappedElementKind.CLASS)) return; + + for (JarFieldEntry field : classEntry.getFields()) { + if (visitor.visitField(field.getName(), field.getDescriptor())) { + if (!visitor.visitElementContent(MappedElementKind.FIELD)) return; + } + } + + for (JarMethodEntry method : classEntry.getMethods()) { + if (method.getName().startsWith("<")) continue; + + if (visitor.visitMethod(method.getName(), method.getDescriptor())) { + if (!visitor.visitElementContent(MappedElementKind.METHOD)) return; + } + } + } + } + } + + visitor.visitEnd(); + + try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(output, StandardOpenOption.CREATE), false)) { + tree.accept(writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + @Override public Path mappingsWorkingDir() { return mappingsWorkingDir; diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index d3f6d671..ce5b558f 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -94,6 +94,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA private final List tasksBeforeRun = Collections.synchronizedList(new ArrayList<>()); public final List> settingsPostEdit = new ArrayList<>(); private NamedDomainObjectContainer launchConfigs; + private final Property stubIntermediaries; protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) { this.runConfigs = project.container(RunConfigSettings.class, @@ -140,6 +141,8 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA baseName -> new LaunchProviderSettings(project, baseName)); this.archDecompilers = project.getObjects().listProperty(ArchitecturyLoomDecompiler.class) .empty(); + this.stubIntermediaries = project.getObjects().property(Boolean.class) + .convention(false); } @Override @@ -295,6 +298,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return archDecompilers; } + @Override + public Property getStubIntermediaries() { + return stubIntermediaries; + } + // This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods private final class EnsureCompile extends LoomGradleExtensionApiImpl { private EnsureCompile() { diff --git a/src/main/java/net/fabricmc/loom/extension/MinecraftGradleExtension.java b/src/main/java/net/fabricmc/loom/extension/MinecraftGradleExtension.java index bd33fd62..fb377e51 100644 --- a/src/main/java/net/fabricmc/loom/extension/MinecraftGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/extension/MinecraftGradleExtension.java @@ -243,4 +243,10 @@ public class MinecraftGradleExtension implements LoomGradleExtensionAPI { reportDeprecation(); parent.forge(action); } + + @Override + public Property getStubIntermediaries() { + reportDeprecation(); + return parent.getStubIntermediaries(); + } } diff --git a/src/main/java/net/fabricmc/loom/util/srg/MCPReader.java b/src/main/java/net/fabricmc/loom/util/srg/MCPReader.java index 96382bb5..a1216547 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/MCPReader.java +++ b/src/main/java/net/fabricmc/loom/util/srg/MCPReader.java @@ -37,11 +37,14 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.function.BiConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.opencsv.CSVReader; import com.opencsv.exceptions.CsvValidationException; +import dev.architectury.refmapremapper.utils.DescriptorRemapper; import org.apache.commons.io.IOUtils; import org.cadixdev.lorenz.MappingSet; import org.cadixdev.lorenz.io.srg.tsrg.TSrgReader; @@ -72,11 +75,11 @@ public class MCPReader { } public TinyFile read(Path mcpJar) throws IOException { - Map srgTokens = readSrg(); + Map, String> srgTokens = readSrg(); TinyFile intermediaryTiny = TinyV2Reader.read(intermediaryTinyPath); - Map intermediaryToMCPMap = createIntermediaryToMCPMap(intermediaryTiny, srgTokens); - Map intermediaryToDocsMap = new HashMap<>(); - Map> intermediaryToParamsMap = new HashMap<>(); + Map, String> intermediaryToMCPMap = createIntermediaryToMCPMap(intermediaryTiny, srgTokens); + Map, String[]> intermediaryToDocsMap = new HashMap<>(); + Map, Map> intermediaryToParamsMap = new HashMap<>(); try { injectMcp(mcpJar, intermediaryToMCPMap, intermediaryToDocsMap, intermediaryToParamsMap); @@ -88,43 +91,54 @@ public class MCPReader { return intermediaryTiny; } - private Map createIntermediaryToMCPMap(TinyFile tiny, Map officialToMCP) { - Map map = new HashMap<>(); + private Map, String> createIntermediaryToMCPMap(TinyFile tiny, Map, String> officialToMCP) { + Map, String> map = new HashMap<>(); + BiConsumer, MemberToken> adder = (intermediary, obf) -> { + String mcp = officialToMCP.get(obf); + + if (mcp != null && !intermediary.name.equals(mcp)) { + map.put(intermediary, mcp); + } + }; for (TinyClass tinyClass : tiny.getClassEntries()) { String classObf = tinyClass.getMapping().get(0); String classIntermediary = tinyClass.getMapping().get(1); - MemberToken classTokenObf = MemberToken.ofClass(classObf); + MemberToken classTokenIntermediary = MemberToken.ofClass(classIntermediary); + MemberToken classTokenObf = MemberToken.ofClass(classObf); - if (officialToMCP.containsKey(classTokenObf)) { - map.put(classIntermediary, officialToMCP.get(classTokenObf)); - } + adder.accept(classTokenIntermediary, classTokenObf); for (TinyField tinyField : tinyClass.getFields()) { String fieldObf = tinyField.getMapping().get(0); String fieldIntermediary = tinyField.getMapping().get(1); - MemberToken fieldTokenObf = MemberToken.ofField(classTokenObf, fieldObf); + MemberToken fieldTokenObf = MemberToken.ofField(classTokenObf, fieldObf); - if (officialToMCP.containsKey(fieldTokenObf)) { - map.put(fieldIntermediary, officialToMCP.get(fieldTokenObf)); - } + adder.accept(MemberToken.ofField(classTokenIntermediary, fieldIntermediary), fieldTokenObf); } for (TinyMethod tinyMethod : tinyClass.getMethods()) { String methodObf = tinyMethod.getMapping().get(0); String methodIntermediary = tinyMethod.getMapping().get(1); - MemberToken methodTokenObf = MemberToken.ofMethod(classTokenObf, methodObf, tinyMethod.getMethodDescriptorInFirstNamespace()); + String methodDescIntermediary = remapDescriptor(tinyMethod.getMethodDescriptorInFirstNamespace(), tiny); + MemberToken methodTokenObf = MemberToken.ofMethod(classTokenObf, methodObf, tinyMethod.getMethodDescriptorInFirstNamespace()); - if (officialToMCP.containsKey(methodTokenObf)) { - map.put(methodIntermediary, officialToMCP.get(methodTokenObf)); - } + adder.accept(MemberToken.ofMethod(classTokenIntermediary, methodIntermediary, methodDescIntermediary), methodTokenObf); } } return map; } - private void mergeTokensIntoIntermediary(TinyFile tiny, Map intermediaryToMCPMap, Map intermediaryToDocsMap, Map> intermediaryToParamsMap) { + private String remapDescriptor(String descriptor, TinyFile file) { + return DescriptorRemapper.remapDescriptor(descriptor, s -> { + TinyClass tinyClass = file.mapClassesByFirstNamespace().get(s); + return tinyClass == null ? s : tinyClass.getMapping().get(1); + }); + } + + private void mergeTokensIntoIntermediary(TinyFile tiny, Map, String> intermediaryToMCPMap, Map, String[]> intermediaryToDocsMap, + Map, Map> intermediaryToParamsMap) { stripTinyWithParametersAndLocal(tiny); // We will be adding the "named" namespace with MCP @@ -132,12 +146,14 @@ public class MCPReader { for (TinyClass tinyClass : tiny.getClassEntries()) { String classIntermediary = tinyClass.getMapping().get(1); - tinyClass.getMapping().add(intermediaryToMCPMap.getOrDefault(classIntermediary, classIntermediary)); + MemberToken classMemberToken = MemberToken.ofClass(classIntermediary); + tinyClass.getMapping().add(intermediaryToMCPMap.getOrDefault(classMemberToken, classIntermediary)); for (TinyField tinyField : tinyClass.getFields()) { String fieldIntermediary = tinyField.getMapping().get(1); - String[] docs = intermediaryToDocsMap.get(fieldIntermediary); - tinyField.getMapping().add(intermediaryToMCPMap.getOrDefault(fieldIntermediary, fieldIntermediary)); + MemberToken fieldMemberToken = MemberToken.ofField(classMemberToken, fieldIntermediary); + String[] docs = intermediaryToDocsMap.get(fieldMemberToken); + tinyField.getMapping().add(intermediaryToMCPMap.getOrDefault(fieldMemberToken, fieldIntermediary)); if (docs != null) { tinyField.getComments().clear(); @@ -147,15 +163,17 @@ public class MCPReader { for (TinyMethod tinyMethod : tinyClass.getMethods()) { String methodIntermediary = tinyMethod.getMapping().get(1); - String[] docs = intermediaryToDocsMap.get(methodIntermediary); - tinyMethod.getMapping().add(intermediaryToMCPMap.getOrDefault(methodIntermediary, methodIntermediary)); + String methodDescIntermediary = remapDescriptor(tinyMethod.getMethodDescriptorInFirstNamespace(), tiny); + MemberToken methodMemberToken = MemberToken.ofMethod(classMemberToken, methodIntermediary, methodDescIntermediary); + String[] docs = intermediaryToDocsMap.get(methodMemberToken); + tinyMethod.getMapping().add(intermediaryToMCPMap.getOrDefault(methodMemberToken, methodIntermediary)); if (docs != null) { tinyMethod.getComments().clear(); tinyMethod.getComments().addAll(Arrays.asList(docs)); } - Map params = intermediaryToParamsMap.get(methodIntermediary); + Map params = intermediaryToParamsMap.get(methodMemberToken); if (params != null) { for (Map.Entry entry : params.entrySet()) { @@ -182,8 +200,8 @@ public class MCPReader { } } - private Map readSrg() throws IOException { - Map tokens = new HashMap<>(); + private Map, String> readSrg() throws IOException { + Map, String> tokens = new HashMap<>(); try (BufferedReader reader = Files.newBufferedReader(srgTsrgPath, StandardCharsets.UTF_8)) { String content = IOUtils.toString(reader); @@ -202,14 +220,14 @@ public class MCPReader { return tokens; } - private void readTsrg2(Map tokens, String content) throws IOException { + private void readTsrg2(Map, String> tokens, String content) throws IOException { MemoryMappingTree tree = new MemoryMappingTree(); MappingReader.read(new StringReader(content), tree); int obfIndex = tree.getNamespaceId("obf"); int srgIndex = tree.getNamespaceId("srg"); for (MappingTree.ClassMapping classDef : tree.getClasses()) { - MemberToken ofClass = MemberToken.ofClass(classDef.getName(obfIndex)); + MemberToken ofClass = MemberToken.ofClass(classDef.getName(obfIndex)); tokens.put(ofClass, classDef.getName(srgIndex)); for (MappingTree.FieldMapping fieldDef : classDef.getFields()) { @@ -223,17 +241,19 @@ public class MCPReader { } } - private void injectMcp(Path mcpJar, Map intermediaryToSrgMap, Map intermediaryToDocsMap, Map> intermediaryToParamsMap) + private void injectMcp(Path mcpJar, Map, String> intermediaryToSrgMap, Map, String[]> intermediaryToDocsMap, + Map, Map> intermediaryToParamsMap) throws IOException, CsvValidationException { - Map> srgToIntermediary = inverseMap(intermediaryToSrgMap); - Map> simpleSrgToIntermediary = new HashMap<>(); + Map>> srgToIntermediary = inverseMap(intermediaryToSrgMap); + Map>> simpleSrgToIntermediary = new HashMap<>(); Pattern methodPattern = Pattern.compile("(func_\\d*)_.*"); - for (Map.Entry> entry : srgToIntermediary.entrySet()) { + for (Map.Entry>> entry : srgToIntermediary.entrySet()) { Matcher matcher = methodPattern.matcher(entry.getKey()); if (matcher.matches()) { - simpleSrgToIntermediary.put(matcher.group(1), entry.getValue()); + simpleSrgToIntermediary.put(matcher.group(1), + (List>) (List>) entry.getValue()); } } @@ -248,11 +268,11 @@ public class MCPReader { String[] line; while ((line = reader.readNext()) != null) { - List intermediaryField = srgToIntermediary.get(line[0]); + List> intermediaryField = (List>) (List>) srgToIntermediary.get(line[0]); String[] docs = line[3].split("\n"); if (intermediaryField != null) { - for (String s : intermediaryField) { + for (MemberToken s : intermediaryField) { intermediaryToSrgMap.put(s, line[1]); if (!line[3].trim().isEmpty() && docs.length > 0) { @@ -268,11 +288,11 @@ public class MCPReader { String[] line; while ((line = reader.readNext()) != null) { - List intermediaryMethod = srgToIntermediary.get(line[0]); + List> intermediaryMethod = (List>) (List>) srgToIntermediary.get(line[0]); String[] docs = line[3].split("\n"); if (intermediaryMethod != null) { - for (String s : intermediaryMethod) { + for (MemberToken s : intermediaryMethod) { intermediaryToSrgMap.put(s, line[1]); if (!line[3].trim().isEmpty() && docs.length > 0) { @@ -295,10 +315,10 @@ public class MCPReader { String named = line[1]; String srgMethodStartWith = "func_" + param.group(1); int lvIndex = Integer.parseInt(param.group(2)); - List intermediaryMethod = simpleSrgToIntermediary.get(srgMethodStartWith); + List> intermediaryMethod = simpleSrgToIntermediary.get(srgMethodStartWith); if (intermediaryMethod != null) { - for (String s : intermediaryMethod) { + for (MemberToken s : intermediaryMethod) { intermediaryToParamsMap.computeIfAbsent(s, s1 -> new HashMap<>()).put(lvIndex, named); } } @@ -309,18 +329,18 @@ public class MCPReader { } } - private Map> inverseMap(Map intermediaryToMCPMap) { - Map> map = new HashMap<>(); + private Map> inverseMap(Map intermediaryToMCPMap) { + Map> map = new HashMap<>(); - for (Map.Entry token : intermediaryToMCPMap.entrySet()) { + for (Map.Entry token : intermediaryToMCPMap.entrySet()) { map.computeIfAbsent(token.getValue(), s -> new ArrayList<>()).add(token.getKey()); } return map; } - private void appendClass(Map tokens, ClassMapping classMapping) { - MemberToken ofClass = MemberToken.ofClass(classMapping.getFullObfuscatedName()); + private void appendClass(Map, String> tokens, ClassMapping classMapping) { + MemberToken ofClass = MemberToken.ofClass(classMapping.getFullObfuscatedName()); tokens.put(ofClass, classMapping.getFullDeobfuscatedName()); for (FieldMapping fieldMapping : classMapping.getFieldMappings()) { @@ -336,28 +356,102 @@ public class MCPReader { } } - private record MemberToken( - TokenType type, - @Nullable MCPReader.MemberToken owner, - String name, - @Nullable String descriptor - ) { - static MemberToken ofClass(String name) { - return new MemberToken(TokenType.CLASS, null, name, null); + private interface TokenType { + enum Class implements TokenType { } - static MemberToken ofField(MemberToken owner, String name) { - return new MemberToken(TokenType.FIELD, owner, name, null); + enum Method implements TokenType { } - static MemberToken ofMethod(MemberToken owner, String name, String descriptor) { - return new MemberToken(TokenType.METHOD, owner, name, descriptor); + enum Field implements TokenType { } } - private enum TokenType { - CLASS, - METHOD, - FIELD + private static class MemberToken { + @Nullable + private MemberToken owner; + private String name; + @Nullable private String descriptor; + + public MemberToken(@Nullable MemberToken owner, String name, @Nullable String descriptor) { + this.owner = owner; + this.name = name; + this.descriptor = descriptor; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MemberToken that)) return false; + return Objects.equals(owner, that.owner) && Objects.equals(name, that.name) && Objects.equals(descriptor, that.descriptor); + } + + @Override + public int hashCode() { + return Objects.hash(owner, name, descriptor); + } + + static class ClassToken extends MemberToken { + ClassToken(String name) { + super(null, name, null); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ClassToken)) return false; + return super.equals(o); + } + + @Override + public int hashCode() { + return (1 + super.hashCode()) * 31 + 1; + } + } + + static class FieldToken extends MemberToken { + FieldToken(@Nullable MemberToken owner, String name) { + super(owner, name, null); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof FieldToken)) return false; + return super.equals(o); + } + + @Override + public int hashCode() { + return (1 + super.hashCode()) * 31 + 2; + } + } + + static class MethodToken extends MemberToken { + MethodToken(@Nullable MemberToken owner, String name, @Nullable String descriptor) { + super(owner, name, descriptor); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof MethodToken)) return false; + return super.equals(o); + } + + @Override + public int hashCode() { + return (1 + super.hashCode()) * 31 + 3; + } + } + + static MemberToken ofClass(String name) { + return new MemberToken.ClassToken(name); + } + + static MemberToken ofField(MemberToken owner, String name) { + return new MemberToken.FieldToken(owner, name); + } + + static MemberToken ofMethod(MemberToken owner, String name, String descriptor) { + return new MemberToken.MethodToken(owner, name, descriptor); + } } }