* After intermediary-named mappings are obtained,
* and official-intermediary mappings are obtained and swapped using CommandReorderTinyV2, Loom merges them using this command,
* and then reorders it to official-intermediary-named using CommandReorderTinyV2 again.
* This is a convenient way of storing all the mappings in Loom.
+ *
*/
public class CommandMergeTinyV2 extends Command {
public CommandMergeTinyV2() {
@@ -78,7 +80,7 @@ public CommandMergeTinyV2() {
}
/**
- * and are the tiny files to be merged. The result will be written to .
+ * {@code } and {@code } are the tiny files to be merged. The result will be written to {@code }.
*/
@Override
public String getHelpString() {
@@ -100,20 +102,21 @@ public void run(String[] args) throws IOException {
TinyFile tinyFileB = TinyV2Reader.read(inputB);
TinyHeader headerA = tinyFileA.getHeader();
TinyHeader headerB = tinyFileB.getHeader();
+
if (headerA.getNamespaces().size() != 2) {
throw new IllegalArgumentException(inputA + " must have exactly 2 namespaces.");
}
+
if (headerB.getNamespaces().size() != 2) {
throw new IllegalArgumentException(inputB + " must have exactly 2 namespaces.");
}
if (!headerA.getNamespaces().get(0).equals(headerB.getNamespaces().get(0))) {
- throw new IllegalArgumentException(
- String.format("The input tiny files must have the same namespaces as the first column. " +
- "(%s has %s while %s has %s)",
- inputA, headerA.getNamespaces().get(0), inputB, headerB.getNamespaces().get(0))
- );
+ throw new IllegalArgumentException(String.format(
+ "The input tiny files must have the same namespaces as the first column. " + "(%s has %s while %s has %s)",
+ inputA, headerA.getNamespaces().get(0), inputB, headerB.getNamespaces().get(0)));
}
+
System.out.println("Merging " + inputA + " with " + inputB);
TinyFile mergedFile = merge(tinyFileA, tinyFileB);
@@ -121,12 +124,10 @@ public void run(String[] args) throws IOException {
System.out.println("Merged mappings written to " + Paths.get(args[2]));
}
-
private TinyFile merge(TinyFile inputA, TinyFile inputB) {
//TODO: how to merge properties?
TinyHeader mergedHeader = mergeHeaders(inputA.getHeader(), inputB.getHeader());
-
List keyUnion = keyUnion(inputA.getClassEntries(), inputB.getClassEntries());
Map inputAClasses = inputA.mapClassesByFirstNamespace();
@@ -153,29 +154,28 @@ private TinyClass matchEnclosingClassIfNeeded(String key, TinyClass tinyClass, M
}
/**
- * Takes something like net/minecraft/class_123$class_124 that doesn't have a mapping, tries to find net/minecraft/class_123
- * , say the mapping of net/minecraft/class_123 is path/to/someclass and then returns a class of the form
- * path/to/someclass$class124
+ * Takes something like {@code net/minecraft/class_123$class_124} that doesn't have a mapping, tries to find {@code net/minecraft/class_123}.
+ * Say the mapping of {@code net/minecraft/class_123} is {@code path/to/someclass} and then returns a class of the form
+ * {@code path/to/someclass$class124}
*/
@Nonnull
private String matchEnclosingClass(String sharedName, Map inputBClassBySharedNamespace) {
String[] path = sharedName.split(escape("$"));
int parts = path.length;
+
for (int i = parts - 2; i >= 0; i--) {
String currentPath = String.join("$", Arrays.copyOfRange(path, 0, i + 1));
TinyClass match = inputBClassBySharedNamespace.get(currentPath);
if (match != null && !match.getClassNames().get(1).isEmpty()) {
return match.getClassNames().get(1)
- + "$" + String.join("$", Arrays.copyOfRange(path, i + 1, path.length));
-
+ + "$" + String.join("$", Arrays.copyOfRange(path, i + 1, path.length));
}
}
return sharedName;
}
-
private TinyClass mergeClasses(String sharedClassName, @Nonnull TinyClass classA, @Nonnull TinyClass classB) {
List mergedNames = mergeNames(sharedClassName, classA, classB);
List mergedComments = mergeComments(classA.getComments(), classB.getComments());
@@ -184,7 +184,7 @@ private TinyClass mergeClasses(String sharedClassName, @Nonnull TinyClass classA
Map, TinyMethod> methodsA = classA.mapMethodsByFirstNamespaceAndDescriptor();
Map, TinyMethod> methodsB = classB.mapMethodsByFirstNamespaceAndDescriptor();
List mergedMethods = map(methodKeyUnion,
- (Pair k) -> mergeMethods(k.getLeft(), methodsA.get(k), methodsB.get(k)));
+ (Pair k) -> mergeMethods(k.getLeft(), methodsA.get(k), methodsB.get(k)));
List fieldKeyUnion = keyUnion(classA.getFields(), classB.getFields());
Map fieldsA = classA.mapFieldsByFirstNamespace();
@@ -194,8 +194,8 @@ private TinyClass mergeClasses(String sharedClassName, @Nonnull TinyClass classA
return new TinyClass(mergedNames, mergedMethods, mergedFields, mergedComments);
}
- private static final TinyMethod EMPTY_METHOD = new TinyMethod(null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
-
+ private static final TinyMethod EMPTY_METHOD = new TinyMethod(null, Collections.emptyList(), Collections.emptyList(),
+ Collections.emptyList(), Collections.emptyList());
private TinyMethod mergeMethods(String sharedMethodName, @Nullable TinyMethod methodA, @Nullable TinyMethod methodB) {
List mergedNames = mergeNames(sharedMethodName, methodA, methodB);
@@ -204,10 +204,9 @@ private TinyMethod mergeMethods(String sharedMethodName, @Nullable TinyMethod me
List mergedComments = mergeComments(methodA.getComments(), methodB.getComments());
String descriptor = methodA.getMethodDescriptorInFirstNamespace() != null ? methodA.getMethodDescriptorInFirstNamespace()
- : methodB.getMethodDescriptorInFirstNamespace();
+ : methodB.getMethodDescriptorInFirstNamespace();
if (descriptor == null) throw new RuntimeException("no descriptor for key " + sharedMethodName);
-
//TODO: this won't work too well when the first namespace is named or there is more than one named namespace (hack)
List mergedParameters = new ArrayList<>();
addParameters(methodA, mergedParameters, 2);
@@ -233,18 +232,17 @@ private void addLocalVariables(TinyMethod method, List addTo,
List names = new ArrayList<>(localVariable.getLocalVariableNames());
names.add(emptySpacePos, "");
addTo.add(new TinyLocalVariable(localVariable.getLvIndex(), localVariable.getLvStartOffset(),
- localVariable.getLvTableIndex(), names, localVariable.getComments()));
+ localVariable.getLvTableIndex(), names, localVariable.getComments()));
}
}
-
private TinyField mergeFields(String sharedFieldName, @Nullable TinyField fieldA, @Nullable TinyField fieldB) {
List mergedNames = mergeNames(sharedFieldName, fieldA, fieldB);
List mergedComments = mergeComments(fieldA != null ? fieldA.getComments() : Collections.emptyList(),
- fieldB != null ? fieldB.getComments() : Collections.emptyList());
+ fieldB != null ? fieldB.getComments() : Collections.emptyList());
String descriptor = fieldA != null ? fieldA.getFieldDescriptorInFirstNamespace()
- : fieldB != null ? fieldB.getFieldDescriptorInFirstNamespace() : null;
+ : fieldB != null ? fieldB.getFieldDescriptorInFirstNamespace() : null;
if (descriptor == null) throw new RuntimeException("no descriptor for key " + sharedFieldName);
return new TinyField(descriptor, mergedNames, mergedComments);
@@ -269,7 +267,6 @@ private Stream> mapToFirstNamespaceAndDescriptor(TinyClass
return tinyClass.getMethods().stream().map(m -> Pair.of(m.getMapping().get(0), m.getMethodDescriptorInFirstNamespace()));
}
-
private List mergeNames(String key, @Nullable Mapping mappingA, @Nullable Mapping mappingB) {
List merged = new ArrayList<>();
merged.add(key);
@@ -303,5 +300,4 @@ private static String escape(String str) {
private List map(List from, Function mapper) {
return from.stream().map(mapper).collect(Collectors.toList());
}
-
}
diff --git a/src/main/java/net/fabricmc/stitch/commands/tinyv2/CommandProposeV2FieldNames.java b/src/main/java/net/fabricmc/stitch/commands/tinyv2/CommandProposeV2FieldNames.java
index 6e74d77..8b47b3c 100644
--- a/src/main/java/net/fabricmc/stitch/commands/tinyv2/CommandProposeV2FieldNames.java
+++ b/src/main/java/net/fabricmc/stitch/commands/tinyv2/CommandProposeV2FieldNames.java
@@ -24,14 +24,14 @@
import java.util.Map;
import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
import com.google.common.collect.Lists;
import net.fabricmc.mappings.EntryTriple;
import net.fabricmc.stitch.Command;
import net.fabricmc.stitch.util.FieldNameFinder;
-import javax.annotation.Nullable;
-
/**
* Java stores the names of enums in the bytecode, and obfuscation doesn't get rid of it. We can use this for easy mappings.
* This command adds all of the field mappings that FieldNameFinder finds (it overwrites existing mappings for those names).
@@ -43,10 +43,12 @@ public CommandProposeV2FieldNames() {
}
/**
- * is any Minecraft jar, and are mappings of that jar (the same version).
- * with the additional field names will be written to .+
- * Assumes the input mappings are intermediary->yarn mappings!
- * is a boolean ("true" or "false") deciding if existing yarn names should be replaced by the generated names.
+ *
+ *
{@code } is any Minecraft jar, and {@code } are mappings of that jar (the same version).
+ *
{@code } with the additional field names will be written to {@code }.
+ *
Assumes the input mappings are intermediary -> Yarn mappings!
+ *
{@code } is a boolean ({@code true} or {@code false}) deciding if existing yarn names should be replaced by the generated names.
+ *
*/
@Override
public String getHelpString() {
@@ -60,8 +62,8 @@ public boolean isArgumentCountValid(int count) {
private Map generatedNamesOfClass(TinyClass tinyClass) {
return tinyClass.getFields().stream().collect(Collectors.toMap(
- (TinyField field) -> new EntryTriple(tinyClass.getClassNames().get(0), field.getFieldNames().get(0), field.getFieldDescriptorInFirstNamespace())
- , field -> field));
+ (TinyField field) -> new EntryTriple(tinyClass.getClassNames().get(0), field.getFieldNames().get(0),
+ field.getFieldDescriptorInFirstNamespace()), field -> field));
}
@Override
@@ -72,10 +74,10 @@ public void run(String[] args) throws Exception {
Boolean shouldReplace = parseBooleanOrNull(args[3]);
// Validation
- if(!inputJar.exists()) throw new IllegalArgumentException("Cannot find input jar at " + inputJar);
- if(!Files.exists(inputMappings)) throw new IllegalArgumentException("Cannot find input mappings at " + inputMappings);
- if(Files.exists(outputMappings)) System.out.println("Warning: existing file will be replaced by output mappings");
- if(shouldReplace == null) throw new IllegalArgumentException(" must be 'true' or 'false'");
+ if (!inputJar.exists()) throw new IllegalArgumentException("Cannot find input jar at " + inputJar);
+ if (!Files.exists(inputMappings)) throw new IllegalArgumentException("Cannot find input mappings at " + inputMappings);
+ if (Files.exists(outputMappings)) System.out.println("Warning: existing file will be replaced by output mappings");
+ if (shouldReplace == null) throw new IllegalArgumentException(" must be 'true' or 'false'");
// entrytriple from the input jar namespace
Map generatedFieldNames = new FieldNameFinder().findNames(new File(args[0]));
@@ -85,12 +87,13 @@ public void run(String[] args) throws Exception {
Map fieldsMap = new HashMap<>();
tinyFile.getClassEntries().stream().map(this::generatedNamesOfClass).forEach(map -> map.forEach(fieldsMap::put));
Map classMap = tinyFile.mapClassesByFirstNamespace();
-
int replaceCount = 0;
+
for (Map.Entry entry : generatedFieldNames.entrySet()) {
EntryTriple key = entry.getKey();
String newName = entry.getValue();
TinyField field = fieldsMap.get(key);
+
// If the field name exists, replace the name with the auto-generated name, as long as is true.
if (field != null) {
if (shouldReplace) {
@@ -99,13 +102,13 @@ public void run(String[] args) throws Exception {
}
} else {
TinyClass tinyClass = classMap.get(key.getOwner());
+
// If field name does not exist, but its class does exist, create a new mapping with the supplied generated name.
if (tinyClass != null) {
tinyClass.getFields().add(new TinyField(key.getDesc(), Lists.newArrayList(key.getName(), newName), Lists.newArrayList()));
replaceCount++;
}
}
-
}
System.err.println("Replaced " + replaceCount + " names in the mappings.");
@@ -118,8 +121,13 @@ public void run(String[] args) throws Exception {
@Nullable
private Boolean parseBooleanOrNull(String booleanLiteral) {
String lowerCase = booleanLiteral.toLowerCase();
- if (lowerCase.equals("true")) return Boolean.TRUE;
- else if (lowerCase.equals("false")) return Boolean.FALSE;
- else return null;
+
+ if (lowerCase.equals("true")) {
+ return Boolean.TRUE;
+ } else if (lowerCase.equals("false")) {
+ return Boolean.FALSE;
+ }
+
+ return null;
}
}
diff --git a/src/main/java/net/fabricmc/stitch/commands/tinyv2/CommandReorderTinyV2.java b/src/main/java/net/fabricmc/stitch/commands/tinyv2/CommandReorderTinyV2.java
index bcf1e36..1befa35 100644
--- a/src/main/java/net/fabricmc/stitch/commands/tinyv2/CommandReorderTinyV2.java
+++ b/src/main/java/net/fabricmc/stitch/commands/tinyv2/CommandReorderTinyV2.java
@@ -34,23 +34,29 @@
import net.fabricmc.stitch.Command;
/**
- * - Reorders the columns in the tiny file
- * - Remaps the descriptors to use the newly first column.
- *
- * For example:
- *
- * This:
- * intermediary named official
- * c net/minecraft/class_123 net/minecraft/somePackage/someClass a
- * m (Lnet/minecraft/class_124;)V method_1234 someMethod a
- *
+ *
+ *
Reorders the columns in the tiny file
+ *
Remaps the descriptors to use the newly first column
+ *
+ *
+ *
For example, this:
+ *
+ * intermediary named official
+ * c net/minecraft/class_123 net/minecraft/somePackage/someClass a
+ * m (Lnet/minecraft/class_124;)V method_1234 someMethod a
+ *
* Reordered to official intermediary named:
- * official intermediary named
- * c a net/minecraft/class_123 net/minecraft/somePackage/someClass
- * m (La;)V a method_1234 someMethod
+ *
+ * official intermediary named
+ * c a net/minecraft/class_123 net/minecraft/somePackage/someClass
+ * m (La;)V a method_1234 someMethod
+ *
+ *
+ *
*
* This is used to reorder the the official-intermediary mappings to be intermediary-official, so they can be merged with
* intermediary-named in CommandMergeTinyV2, and then reorder the outputted intermediary-official-named to official-intermediary-named.
+ *
*/
public class CommandReorderTinyV2 extends Command {
public CommandReorderTinyV2() {
@@ -58,8 +64,8 @@ public CommandReorderTinyV2() {
}
/**
- * Reorders the columns in according to [new name order...] and puts the result in .
- * new name order is for example "official intermediary named"
+ * Reorders the columns in {@code } according to {@code [new name order...]} and puts the result in {@code }.
+ * {@code new name order} is, for example, {@code official intermediary named}.
*/
@Override
public String getHelpString() {
@@ -81,8 +87,8 @@ public void run(String[] args) throws Exception {
validateNamespaces(newOrder, tinyFile);
Map mappingCopy = tinyFile.getClassEntries().stream()
- .collect(Collectors.toMap(c -> c.getClassNames().get(0),
- c -> new TinyClass(new ArrayList<>(c.getClassNames()), c.getMethods(), c.getFields(), c.getComments())));
+ .collect(Collectors.toMap(c -> c.getClassNames().get(0),
+ c -> new TinyClass(new ArrayList<>(c.getClassNames()), c.getMethods(), c.getFields(), c.getComments())));
int newFirstNamespaceOldIndex = tinyFile.getHeader().getNamespaces().indexOf(newOrder.get(0));
reorder(tinyFile, newOrder);
@@ -96,13 +102,14 @@ private void validateNamespaces(List newOrder, TinyFile tinyFile) {
HashSet providedNamespacesOrderless = new HashSet<>(newOrder);
if (!fileNamespacesOrderless.equals(providedNamespacesOrderless)) {
- throw new IllegalArgumentException("The tiny file has different namespaces than those specified." +
- " specified: " + providedNamespacesOrderless.toString() + ", file: " + fileNamespacesOrderless.toString());
+ throw new IllegalArgumentException("The tiny file has different namespaces than those specified."
+ + " specified: " + providedNamespacesOrderless.toString() + ", file: " + fileNamespacesOrderless.toString());
}
}
private void reorder(TinyFile tinyFile, List newOrder) {
Map indexMapping = new HashMap<>();
+
for (int i = 0; i < newOrder.size(); i++) {
indexMapping.put(tinyFile.getHeader().getNamespaces().indexOf(newOrder.get(i)), i);
}
@@ -112,7 +119,9 @@ private void reorder(TinyFile tinyFile, List newOrder) {
for (int i = names.size(); i < newOrder.size(); i++) {
names.add("");
}
+
List namesCopy = new ArrayList<>(names);
+
for (int i = 0; i < namesCopy.size(); i++) {
names.set(indexMapping.get(i), namesCopy.get(i));
}
@@ -124,29 +133,34 @@ private void remapDescriptors(TinyFile tinyFile, Map mappings
for (TinyMethod method : tinyClass.getMethods()) {
remapMethodDescriptor(method, mappings, targetNamespace);
}
+
for (TinyField field : tinyClass.getFields()) {
remapFieldDescriptor(field, mappings, targetNamespace);
}
}
}
-
/**
- * In this case the visitor is not a nice man and reorganizes the house as he sees fit
+ * In this case the visitor is not a nice man and reorganizes the house as he sees fit.
*/
private void visitNames(TinyFile tinyFile, Consumer> namesVisitor) {
namesVisitor.accept(tinyFile.getHeader().getNamespaces());
+
for (TinyClass tinyClass : tinyFile.getClassEntries()) {
namesVisitor.accept(tinyClass.getClassNames());
+
for (TinyMethod method : tinyClass.getMethods()) {
namesVisitor.accept(method.getMethodNames());
+
for (TinyMethodParameter parameter : method.getParameters()) {
namesVisitor.accept(parameter.getParameterNames());
}
+
for (TinyLocalVariable localVariable : method.getLocalVariables()) {
namesVisitor.accept(localVariable.getLocalVariableNames());
}
}
+
for (TinyField field : tinyClass.getFields()) {
namesVisitor.accept(field.getFieldNames());
}
@@ -164,24 +178,22 @@ private void remapFieldDescriptor(TinyField field, Map mappin
private void remapMethodDescriptor(TinyMethod method, Map mappings, int targetNamespace) {
String descriptor = method.getMethodDescriptorInFirstNamespace();
String[] paramsAndReturnType = descriptor.split(Pattern.quote(")"));
- if (paramsAndReturnType.length != 2) {
- throw new IllegalArgumentException(
- "method descriptor '" + descriptor + "' is of an unknown format.");
- }
+
+ if (paramsAndReturnType.length != 2) {
+ throw new IllegalArgumentException("method descriptor '" + descriptor + "' is of an unknown format.");
+ }
+
List params = parseParameterDescriptors(paramsAndReturnType[0].substring(1));
String returnType = paramsAndReturnType[1];
-
List paramsMapped = params.stream().map(p -> remapType(p, mappings, targetNamespace)).collect(Collectors.toList());
String returnTypeMapped = returnType.equals("V") ? "V" : remapType(returnType, mappings, targetNamespace);
-
String newDescriptor = "(" + String.join("", paramsMapped) + ")" + returnTypeMapped;
- method.setMethodDescriptorInFirstNamespace(newDescriptor);
+ method.setMethodDescriptorInFirstNamespace(newDescriptor);
}
private static final Collection primitiveTypeNames = Arrays.asList("B", "C", "D", "F", "I", "J", "S", "Z");
-
private List parseParameterDescriptors(String concatenatedParameterDescriptors) {
List parameterDescriptors = new ArrayList<>();
boolean inClassName = false;
@@ -190,12 +202,14 @@ private List parseParameterDescriptors(String concatenatedParameterDescr
for (int i = 0; i < concatenatedParameterDescriptors.length(); i++) {
char c = concatenatedParameterDescriptors.charAt(i);
+
if (inClassName) {
if (c == ';') {
- if (currentClassName.length() == 0) {
- throw new IllegalArgumentException(
- "Empty class name in parameter list " + concatenatedParameterDescriptors + " at position " + i);
- }
+ if (currentClassName.length() == 0) {
+ throw new IllegalArgumentException("Empty class name in parameter list " + concatenatedParameterDescriptors
+ + " at position " + i);
+ }
+
parameterDescriptors.add(Strings.repeat("[", inArrayNestingLevel) + "L" + currentClassName.toString() + ";");
inArrayNestingLevel = 0;
currentClassName = new StringBuilder();
@@ -212,19 +226,15 @@ private List parseParameterDescriptors(String concatenatedParameterDescr
} else if (c == 'L') {
inClassName = true;
} else {
- throw new IllegalArgumentException(
- "Unexpected special character " + c + " in parameter descriptor list "
- + concatenatedParameterDescriptors);
+ throw new IllegalArgumentException("Unexpected special character " + c + " in parameter descriptor list "
+ + concatenatedParameterDescriptors);
}
}
-
}
return parameterDescriptors;
-
}
-
/**
* Remaps type from namespace X, to the namespace of targetNamespaceIndex in mappings, where mappings
* is a mapping from names in namespace X to the names in all other namespaces.
@@ -232,9 +242,11 @@ private List parseParameterDescriptors(String concatenatedParameterDescr
private String remapType(String type, Map mappings, int targetNamespaceIndex) {
if (type.isEmpty()) throw new IllegalArgumentException("types cannot be empty strings");
if (primitiveTypeNames.contains(type)) return type;
- if (type.charAt(0) == '[') {
- return "[" + remapType(type.substring(1), mappings, targetNamespaceIndex);
- }
+
+ if (type.charAt(0) == '[') {
+ return "[" + remapType(type.substring(1), mappings, targetNamespaceIndex);
+ }
+
if (type.charAt(0) == 'L' && type.charAt(type.length() - 1) == ';') {
String className = type.substring(1, type.length() - 1);
TinyClass mapping = mappings.get(className);
@@ -243,8 +255,5 @@ private String remapType(String type, Map mappings, int targe
}
throw new IllegalArgumentException("type descriptor '" + type + "' is of an unknown format.");
-
}
-
-
}
diff --git a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyClass.java b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyClass.java
index 2b7c9f6..88e22ca 100644
--- a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyClass.java
+++ b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyClass.java
@@ -28,7 +28,7 @@ public class TinyClass implements Comparable, Mapping {
@Override
public String toString() {
return "TinyClass(names = [" + String.join(", ", classNames) + "], " + methods.size() + " methods, "
- + fields.size() + " fields, " + comments.size() + " comments)";
+ + fields.size() + " fields, " + comments.size() + " comments)";
}
private final List classNames;
@@ -50,7 +50,6 @@ public TinyClass(List classNames) {
this.comments = new ArrayList<>();
}
-
/**
* Descriptors are also taken into account because methods can overload.
* The key format is firstMethodName + descriptor
@@ -63,7 +62,6 @@ public Map mapFieldsByFirstNamespace() {
return fields.stream().collect(Collectors.toMap(f -> f.getFieldNames().get(0), f -> f));
}
-
public List getClassNames() {
return classNames;
}
diff --git a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyField.java b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyField.java
index 543faa2..ef2c14d 100644
--- a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyField.java
+++ b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyField.java
@@ -20,7 +20,6 @@
import java.util.List;
public class TinyField implements Comparable, Mapping {
-
/**
* For example when we have official -> named mappings the descriptor will be in official, but in named -> official
* the descriptor will be in named.
diff --git a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyHeader.java b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyHeader.java
index a6be00d..4585a18 100644
--- a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyHeader.java
+++ b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyHeader.java
@@ -20,11 +20,10 @@
import java.util.Map;
public class TinyHeader {
-
private final List namespaces;
private final int majorVersion;
private final int minorVersion;
- private final Map properties;
+ private final Map properties;
public TinyHeader(List namespaces, int majorVersion, int minorVersion, Map properties) {
this.namespaces = namespaces;
diff --git a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyLocalVariable.java b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyLocalVariable.java
index 56e0495..221b1d4 100644
--- a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyLocalVariable.java
+++ b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyLocalVariable.java
@@ -20,12 +20,9 @@
import java.util.List;
public class TinyLocalVariable implements Comparable, Mapping {
-
private final int lvIndex;
private final int lvStartOffset;
- /**
- * Will be -1 when there is no lvt index
- */
+ /** Will be -1 when there is no lvt index. */
private final int lvTableIndex;
private final List localVariableNames;
private final Collection comments;
@@ -38,7 +35,6 @@ public TinyLocalVariable(int lvIndex, int lvStartOffset, int lvTableIndex, List<
this.comments = comments;
}
-
public int getLvIndex() {
return lvIndex;
}
diff --git a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyMethod.java b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyMethod.java
index 14c2796..608da98 100644
--- a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyMethod.java
+++ b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyMethod.java
@@ -22,7 +22,6 @@
import java.util.stream.Collectors;
public class TinyMethod implements Comparable, Mapping {
-
@Override
public String toString() {
return "TinyMethod(names = [" + String.join(", ", methodNames) + "], desc = " + methodDescriptorInFirstNamespace
@@ -79,7 +78,7 @@ public Collection getComments() {
@Override
public int compareTo(TinyMethod o) {
return (methodNames.get(0) + methodDescriptorInFirstNamespace)
- .compareTo(o.methodNames.get(0) + o.methodDescriptorInFirstNamespace);
+ .compareTo(o.methodNames.get(0) + o.methodDescriptorInFirstNamespace);
}
public void setMethodDescriptorInFirstNamespace(String methodDescriptorInFirstNamespace) {
diff --git a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyV2Reader.java b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyV2Reader.java
index 1d935d4..a498958 100644
--- a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyV2Reader.java
+++ b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyV2Reader.java
@@ -44,7 +44,7 @@ private enum CommentType {
private TinyHeader header;
private int namespaceAmount;
- // private String
+ // private String
private Set classes = new HashSet<>();
private TinyClass currentClass;
@@ -61,8 +61,8 @@ private List getNames(MappingGetter getter) {
@Override
public void start(TinyMetadata metadata) {
- header = new TinyHeader(new ArrayList<>(metadata.getNamespaces()), metadata.getMajorVersion(), metadata.getMinorVersion(),
- metadata.getProperties());
+ header = new TinyHeader(new ArrayList<>(metadata.getNamespaces()), metadata.getMajorVersion(),
+ metadata.getMinorVersion(), metadata.getProperties());
namespaceAmount = header.getNamespaces().size();
}
@@ -82,35 +82,32 @@ public void pushField(MappingGetter name, String descriptor) {
@Override
public void pushMethod(MappingGetter name, String descriptor) {
- currentMethod = new TinyMethod(
- descriptor, getNames(name), new HashSet<>(), new HashSet<>(), new ArrayList<>()
- );
+ currentMethod = new TinyMethod(descriptor, getNames(name), new HashSet<>(), new HashSet<>(), new ArrayList<>());
currentClass.getMethods().add(currentMethod);
currentCommentType = CommentType.METHOD;
}
@Override
public void pushParameter(MappingGetter name, int localVariableIndex) {
- currentParameter = new TinyMethodParameter(
- localVariableIndex, getNames(name), new ArrayList<>()
- );
+ currentParameter = new TinyMethodParameter(localVariableIndex, getNames(name), new ArrayList<>());
currentMethod.getParameters().add(currentParameter);
currentCommentType = CommentType.PARAMETER;
}
@Override
public void pushLocalVariable(MappingGetter name, int localVariableIndex, int localVariableStartOffset, int localVariableTableIndex) {
- currentLocalVariable = new TinyLocalVariable(
- localVariableIndex, localVariableStartOffset, localVariableTableIndex, getNames(name), new ArrayList<>()
- );
+ currentLocalVariable = new TinyLocalVariable(localVariableIndex, localVariableStartOffset,
+ localVariableTableIndex, getNames(name), new ArrayList<>());
currentMethod.getLocalVariables().add(currentLocalVariable);
currentCommentType = CommentType.LOCAL_VARIABLE;
}
@Override
public void pushComment(String comment) {
- if (inComment)
+ if (inComment) {
throw new RuntimeException("commenting on comment");
+ }
+
switch (currentCommentType) {
case CLASS:
currentClass.getComments().add(comment);
@@ -130,6 +127,7 @@ public void pushComment(String comment) {
default:
throw new RuntimeException("unexpected comment without parent");
}
+
inComment = true;
}
@@ -143,19 +141,19 @@ public void pop(int count) {
CommentType last = currentCommentType;
switch (last) {
- case CLASS:
- currentCommentType = null;
- break;
- case FIELD:
- case METHOD:
- currentCommentType = CommentType.CLASS;
- break;
- case PARAMETER:
- case LOCAL_VARIABLE:
- currentCommentType = CommentType.METHOD;
- break;
- default:
- throw new IllegalStateException("visit stack is empty!");
+ case CLASS:
+ currentCommentType = null;
+ break;
+ case FIELD:
+ case METHOD:
+ currentCommentType = CommentType.CLASS;
+ break;
+ case PARAMETER:
+ case LOCAL_VARIABLE:
+ currentCommentType = CommentType.METHOD;
+ break;
+ default:
+ throw new IllegalStateException("visit stack is empty!");
}
}
}
@@ -167,6 +165,7 @@ private TinyFile getAST() {
public static TinyFile read(Path readFrom) throws IOException {
Visitor visitor = new Visitor();
+
try (BufferedReader reader = Files.newBufferedReader(readFrom)) {
TinyV2Factory.visit(reader, visitor);
}
diff --git a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyV2Writer.java b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyV2Writer.java
index c27de9a..b365a38 100644
--- a/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyV2Writer.java
+++ b/src/main/java/net/fabricmc/stitch/commands/tinyv2/TinyV2Writer.java
@@ -57,8 +57,6 @@ private Indents() {
public static final int FIELD_COMMENT = 2;
public static final int PARAMETER_COMMENT = 3;
public static final int LOCAL_VARIABLE_COMMENT = 3;
-
-
}
private TinyV2Writer() {
@@ -76,10 +74,9 @@ private void instanceWrite(TinyFile tinyFile, Path writeTo) throws IOException {
}
}
-
private void writeHeader(TinyHeader header) {
writeLine(Indents.HEADER, header.getNamespaces(), Prefixes.HEADER,
- Integer.toString(header.getMajorVersion()), Integer.toString(header.getMinorVersion()));
+ Integer.toString(header.getMajorVersion()), Integer.toString(header.getMinorVersion()));
header.getProperties().forEach((key, value) -> writeLine(Indents.PROPERTY, value));
}
@@ -90,7 +87,6 @@ private void writeClass(TinyClass tinyClass) {
tinyClass.getMethods().stream().sorted().forEach(this::writeMethod);
tinyClass.getFields().stream().sorted().forEach(this::writeField);
-
}
private void writeMethod(TinyMethod method) {
@@ -100,22 +96,20 @@ private void writeMethod(TinyMethod method) {
method.getParameters().stream().sorted().forEach(this::writeMethodParameter);
method.getLocalVariables().stream().sorted().forEach(this::writeLocalVariable);
-
}
private void writeMethodParameter(TinyMethodParameter parameter) {
writeLine(Indents.PARAMETER, parameter.getParameterNames(), Prefixes.PARAMETER, Integer.toString(parameter.getLvIndex()));
+
for (String comment : parameter.getComments()) {
writeComment(Indents.PARAMETER_COMMENT, comment);
}
-
}
private void writeLocalVariable(TinyLocalVariable localVariable) {
writeLine(Indents.LOCAL_VARIABLE, localVariable.getLocalVariableNames(), Prefixes.VARIABLE,
- Integer.toString(localVariable.getLvIndex()), Integer.toString(localVariable.getLvStartOffset()),
- Integer.toString(localVariable.getLvTableIndex())
- );
+ Integer.toString(localVariable.getLvIndex()), Integer.toString(localVariable.getLvStartOffset()),
+ Integer.toString(localVariable.getLvTableIndex()));
for (String comment : localVariable.getComments()) {
writeComment(Indents.LOCAL_VARIABLE_COMMENT, comment);
@@ -127,29 +121,30 @@ private void writeField(TinyField field) {
for (String comment : field.getComments()) writeComment(Indents.FIELD_COMMENT, comment);
}
-
private void writeComment(int indentLevel, String comment) {
writeLine(indentLevel, Prefixes.COMMENT, escapeComment(comment));
}
private static String escapeComment(String old) {
StringBuilder sb = new StringBuilder(old.length());
+
for (int i = 0; i < old.length(); i++) {
char c = old.charAt(i);
int t = TO_ESCAPE.indexOf(c);
+
if (t == -1) {
sb.append(c);
} else {
sb.append('\\').append(ESCAPED.charAt(t));
}
}
+
return sb.toString();
}
private static final String TO_ESCAPE = "\\\n\r\0\t";
private static final String ESCAPED = "\\nr0t";
-
private void write(int indentLevel, String... tabSeparatedStrings) {
try {
for (int i = 0; i < indentLevel; i++) writer.write('\t');
diff --git a/src/main/java/net/fabricmc/stitch/enigma/StitchEnigmaPlugin.java b/src/main/java/net/fabricmc/stitch/enigma/StitchEnigmaPlugin.java
index 8a820ec..368b082 100644
--- a/src/main/java/net/fabricmc/stitch/enigma/StitchEnigmaPlugin.java
+++ b/src/main/java/net/fabricmc/stitch/enigma/StitchEnigmaPlugin.java
@@ -21,11 +21,9 @@
import cuchaz.enigma.api.service.ObfuscationTestService;
public class StitchEnigmaPlugin implements EnigmaPlugin {
-
@Override
public void init(EnigmaPluginContext ctx) {
StitchNameProposalService.register(ctx);
ctx.registerService("stitch:intermediary_obfuscation_test", ObfuscationTestService.TYPE, StitchIntermediaryObfuscationTestService::new);
}
-
}
diff --git a/src/main/java/net/fabricmc/stitch/enigma/StitchIntermediaryObfuscationTestService.java b/src/main/java/net/fabricmc/stitch/enigma/StitchIntermediaryObfuscationTestService.java
index 4580a47..31a8b66 100644
--- a/src/main/java/net/fabricmc/stitch/enigma/StitchIntermediaryObfuscationTestService.java
+++ b/src/main/java/net/fabricmc/stitch/enigma/StitchIntermediaryObfuscationTestService.java
@@ -43,6 +43,7 @@ public boolean testDeobfuscated(Entry> entry) {
// all obfuscated components are, at their outermost, class_
String lastComponent = components[components.length - 1];
+
if (lastComponent.startsWith(this.classPrefix) || lastComponent.startsWith(this.classPackagePrefix)) {
return false;
}
diff --git a/src/main/java/net/fabricmc/stitch/enigma/StitchNameProposalService.java b/src/main/java/net/fabricmc/stitch/enigma/StitchNameProposalService.java
index 52fda16..51e643b 100644
--- a/src/main/java/net/fabricmc/stitch/enigma/StitchNameProposalService.java
+++ b/src/main/java/net/fabricmc/stitch/enigma/StitchNameProposalService.java
@@ -16,23 +16,24 @@
package net.fabricmc.stitch.enigma;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
import cuchaz.enigma.analysis.index.JarIndex;
import cuchaz.enigma.api.EnigmaPluginContext;
import cuchaz.enigma.api.service.JarIndexerService;
import cuchaz.enigma.api.service.NameProposalService;
import cuchaz.enigma.classprovider.ClassProvider;
import cuchaz.enigma.translation.representation.entry.FieldEntry;
+import org.objectweb.asm.tree.MethodNode;
+
import net.fabricmc.mappings.EntryTriple;
import net.fabricmc.stitch.util.FieldNameFinder;
import net.fabricmc.stitch.util.NameFinderVisitor;
import net.fabricmc.stitch.util.StitchUtil;
-import org.objectweb.asm.tree.MethodNode;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
public class StitchNameProposalService {
private Map fieldNames;
@@ -41,7 +42,6 @@ private StitchNameProposalService(EnigmaPluginContext ctx) {
ctx.registerService("stitch:jar_indexer", JarIndexerService.TYPE, ctx1 -> new JarIndexerService() {
@Override
public void acceptJar(Set classNames, ClassProvider classProvider, JarIndex jarIndex) {
-
Map> enumFields = new HashMap<>();
Map> methods = new HashMap<>();
@@ -58,11 +58,12 @@ public void acceptJar(Set classNames, ClassProvider classProvider, JarIn
});
ctx.registerService("stitch:name_proposal", NameProposalService.TYPE, ctx12 -> (obfEntry, remapper) -> {
- if(obfEntry instanceof FieldEntry){
+ if (obfEntry instanceof FieldEntry) {
FieldEntry fieldEntry = (FieldEntry) obfEntry;
EntryTriple key = new EntryTriple(fieldEntry.getContainingClass().getFullName(), fieldEntry.getName(), fieldEntry.getDesc().toString());
return Optional.ofNullable(fieldNames.get(key));
}
+
return Optional.empty();
});
}
diff --git a/src/main/java/net/fabricmc/stitch/merge/ClassMerger.java b/src/main/java/net/fabricmc/stitch/merge/ClassMerger.java
index 738a195..a739845 100644
--- a/src/main/java/net/fabricmc/stitch/merge/ClassMerger.java
+++ b/src/main/java/net/fabricmc/stitch/merge/ClassMerger.java
@@ -16,210 +16,226 @@
package net.fabricmc.stitch.merge;
-import net.fabricmc.stitch.util.StitchUtil;
-import org.objectweb.asm.*;
-import org.objectweb.asm.tree.*;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.FieldNode;
+import org.objectweb.asm.tree.InnerClassNode;
+import org.objectweb.asm.tree.MethodNode;
-import java.util.*;
+import net.fabricmc.stitch.util.StitchUtil;
public class ClassMerger {
- private static final String SIDE_DESCRIPTOR = "Lnet/fabricmc/api/EnvType;";
- private static final String ITF_DESCRIPTOR = "Lnet/fabricmc/api/EnvironmentInterface;";
- private static final String ITF_LIST_DESCRIPTOR = "Lnet/fabricmc/api/EnvironmentInterfaces;";
- private static final String SIDED_DESCRIPTOR = "Lnet/fabricmc/api/Environment;";
-
- private abstract class Merger {
- private final Map entriesClient, entriesServer;
- private final List entryNames;
-
- public Merger(List entriesClient, List entriesServer) {
- this.entriesClient = new LinkedHashMap<>();
- this.entriesServer = new LinkedHashMap<>();
-
- List listClient = toMap(entriesClient, this.entriesClient);
- List listServer = toMap(entriesServer, this.entriesServer);
-
- this.entryNames = StitchUtil.mergePreserveOrder(listClient, listServer);
- }
-
- public abstract String getName(T entry);
- public abstract void applySide(T entry, String side);
-
- private final List toMap(List entries, Map map) {
- List list = new ArrayList<>(entries.size());
- for (T entry : entries) {
- String name = getName(entry);
- map.put(name, entry);
- list.add(name);
- }
- return list;
- }
-
- public void merge(List list) {
- for (String s : entryNames) {
- T entryClient = entriesClient.get(s);
- T entryServer = entriesServer.get(s);
-
- if (entryClient != null && entryServer != null) {
- list.add(entryClient);
- } else if (entryClient != null) {
- applySide(entryClient, "CLIENT");
- list.add(entryClient);
- } else {
- applySide(entryServer, "SERVER");
- list.add(entryServer);
- }
- }
- }
- }
-
- private static void visitSideAnnotation(AnnotationVisitor av, String side) {
- av.visitEnum("value", SIDE_DESCRIPTOR, side.toUpperCase(Locale.ROOT));
- av.visitEnd();
- }
-
- private static void visitItfAnnotation(AnnotationVisitor av, String side, List itfDescriptors) {
- for (String itf : itfDescriptors) {
- AnnotationVisitor avItf = av.visitAnnotation(null, ITF_DESCRIPTOR);
- avItf.visitEnum("value", SIDE_DESCRIPTOR, side.toUpperCase(Locale.ROOT));
- avItf.visit("itf", Type.getType("L" + itf + ";"));
- avItf.visitEnd();
- }
- }
-
- public static class SidedClassVisitor extends ClassVisitor {
- private final String side;
-
- public SidedClassVisitor(int api, ClassVisitor cv, String side) {
- super(api, cv);
- this.side = side;
- }
-
- @Override
- public void visitEnd() {
- AnnotationVisitor av = cv.visitAnnotation(SIDED_DESCRIPTOR, true);
- visitSideAnnotation(av, side);
- super.visitEnd();
- }
- }
-
- public ClassMerger() {
-
- }
-
- public byte[] merge(byte[] classClient, byte[] classServer) {
- ClassReader readerC = new ClassReader(classClient);
- ClassReader readerS = new ClassReader(classServer);
- ClassWriter writer = new ClassWriter(0);
-
- ClassNode nodeC = new ClassNode(StitchUtil.ASM_VERSION);
- readerC.accept(nodeC, 0);
-
- ClassNode nodeS = new ClassNode(StitchUtil.ASM_VERSION);
- readerS.accept(nodeS, 0);
-
- ClassNode nodeOut = new ClassNode(StitchUtil.ASM_VERSION);
- nodeOut.version = nodeC.version;
- nodeOut.access = nodeC.access;
- nodeOut.name = nodeC.name;
- nodeOut.signature = nodeC.signature;
- nodeOut.superName = nodeC.superName;
- nodeOut.sourceFile = nodeC.sourceFile;
- nodeOut.sourceDebug = nodeC.sourceDebug;
- nodeOut.outerClass = nodeC.outerClass;
- nodeOut.outerMethod = nodeC.outerMethod;
- nodeOut.outerMethodDesc = nodeC.outerMethodDesc;
- nodeOut.module = nodeC.module;
- nodeOut.nestHostClass = nodeC.nestHostClass;
- nodeOut.nestMembers = nodeC.nestMembers;
- nodeOut.attrs = nodeC.attrs;
-
- if (nodeC.invisibleAnnotations != null) {
- nodeOut.invisibleAnnotations = new ArrayList<>();
- nodeOut.invisibleAnnotations.addAll(nodeC.invisibleAnnotations);
- }
- if (nodeC.invisibleTypeAnnotations != null) {
- nodeOut.invisibleTypeAnnotations = new ArrayList<>();
- nodeOut.invisibleTypeAnnotations.addAll(nodeC.invisibleTypeAnnotations);
- }
- if (nodeC.visibleAnnotations != null) {
- nodeOut.visibleAnnotations = new ArrayList<>();
- nodeOut.visibleAnnotations.addAll(nodeC.visibleAnnotations);
- }
- if (nodeC.visibleTypeAnnotations != null) {
- nodeOut.visibleTypeAnnotations = new ArrayList<>();
- nodeOut.visibleTypeAnnotations.addAll(nodeC.visibleTypeAnnotations);
- }
-
- List itfs = StitchUtil.mergePreserveOrder(nodeC.interfaces, nodeS.interfaces);
- nodeOut.interfaces = new ArrayList<>();
-
- List clientItfs = new ArrayList<>();
- List serverItfs = new ArrayList<>();
-
- for (String s : itfs) {
- boolean nc = nodeC.interfaces.contains(s);
- boolean ns = nodeS.interfaces.contains(s);
- nodeOut.interfaces.add(s);
- if (nc && !ns) {
- clientItfs.add(s);
- } else if (ns && !nc) {
- serverItfs.add(s);
- }
- }
-
- if (!clientItfs.isEmpty() || !serverItfs.isEmpty()) {
- AnnotationVisitor envInterfaces = nodeOut.visitAnnotation(ITF_LIST_DESCRIPTOR, false);
- AnnotationVisitor eiArray = envInterfaces.visitArray("value");
-
- if (!clientItfs.isEmpty()) {
- visitItfAnnotation(eiArray, "CLIENT", clientItfs);
- }
- if (!serverItfs.isEmpty()) {
- visitItfAnnotation(eiArray, "SERVER", serverItfs);
- }
- eiArray.visitEnd();
- envInterfaces.visitEnd();
- }
-
- new Merger(nodeC.innerClasses, nodeS.innerClasses) {
- @Override
- public String getName(InnerClassNode entry) {
- return entry.name;
- }
-
- @Override
- public void applySide(InnerClassNode entry, String side) {
- }
- }.merge(nodeOut.innerClasses);
-
- new Merger(nodeC.fields, nodeS.fields) {
- @Override
- public String getName(FieldNode entry) {
- return entry.name + ";;" + entry.desc;
- }
-
- @Override
- public void applySide(FieldNode entry, String side) {
- AnnotationVisitor av = entry.visitAnnotation(SIDED_DESCRIPTOR, false);
- visitSideAnnotation(av, side);
- }
- }.merge(nodeOut.fields);
-
- new Merger(nodeC.methods, nodeS.methods) {
- @Override
- public String getName(MethodNode entry) {
- return entry.name + entry.desc;
- }
-
- @Override
- public void applySide(MethodNode entry, String side) {
- AnnotationVisitor av = entry.visitAnnotation(SIDED_DESCRIPTOR, false);
- visitSideAnnotation(av, side);
- }
- }.merge(nodeOut.methods);
-
- nodeOut.accept(writer);
- return writer.toByteArray();
- }
+ private static final String SIDE_DESCRIPTOR = "Lnet/fabricmc/api/EnvType;";
+ private static final String ITF_DESCRIPTOR = "Lnet/fabricmc/api/EnvironmentInterface;";
+ private static final String ITF_LIST_DESCRIPTOR = "Lnet/fabricmc/api/EnvironmentInterfaces;";
+ private static final String SIDED_DESCRIPTOR = "Lnet/fabricmc/api/Environment;";
+
+ private abstract class Merger {
+ private final Map entriesClient, entriesServer;
+ private final List entryNames;
+
+ Merger(List entriesClient, List entriesServer) {
+ this.entriesClient = new LinkedHashMap<>();
+ this.entriesServer = new LinkedHashMap<>();
+
+ List listClient = toMap(entriesClient, this.entriesClient);
+ List listServer = toMap(entriesServer, this.entriesServer);
+
+ this.entryNames = StitchUtil.mergePreserveOrder(listClient, listServer);
+ }
+
+ public abstract String getName(T entry);
+ public abstract void applySide(T entry, String side);
+
+ private List toMap(List entries, Map map) {
+ List list = new ArrayList<>(entries.size());
+
+ for (T entry : entries) {
+ String name = getName(entry);
+ map.put(name, entry);
+ list.add(name);
+ }
+
+ return list;
+ }
+
+ public void merge(List list) {
+ for (String s : entryNames) {
+ T entryClient = entriesClient.get(s);
+ T entryServer = entriesServer.get(s);
+
+ if (entryClient != null && entryServer != null) {
+ list.add(entryClient);
+ } else if (entryClient != null) {
+ applySide(entryClient, "CLIENT");
+ list.add(entryClient);
+ } else {
+ applySide(entryServer, "SERVER");
+ list.add(entryServer);
+ }
+ }
+ }
+ }
+
+ private static void visitSideAnnotation(AnnotationVisitor av, String side) {
+ av.visitEnum("value", SIDE_DESCRIPTOR, side.toUpperCase(Locale.ROOT));
+ av.visitEnd();
+ }
+
+ private static void visitItfAnnotation(AnnotationVisitor av, String side, List itfDescriptors) {
+ for (String itf : itfDescriptors) {
+ AnnotationVisitor avItf = av.visitAnnotation(null, ITF_DESCRIPTOR);
+ avItf.visitEnum("value", SIDE_DESCRIPTOR, side.toUpperCase(Locale.ROOT));
+ avItf.visit("itf", Type.getType("L" + itf + ";"));
+ avItf.visitEnd();
+ }
+ }
+
+ public static class SidedClassVisitor extends ClassVisitor {
+ private final String side;
+
+ public SidedClassVisitor(int api, ClassVisitor cv, String side) {
+ super(api, cv);
+ this.side = side;
+ }
+
+ @Override
+ public void visitEnd() {
+ AnnotationVisitor av = cv.visitAnnotation(SIDED_DESCRIPTOR, true);
+ visitSideAnnotation(av, side);
+ super.visitEnd();
+ }
+ }
+
+ public byte[] merge(byte[] classClient, byte[] classServer) {
+ ClassReader readerC = new ClassReader(classClient);
+ ClassReader readerS = new ClassReader(classServer);
+ ClassWriter writer = new ClassWriter(0);
+
+ ClassNode nodeC = new ClassNode(StitchUtil.ASM_VERSION);
+ readerC.accept(nodeC, 0);
+
+ ClassNode nodeS = new ClassNode(StitchUtil.ASM_VERSION);
+ readerS.accept(nodeS, 0);
+
+ ClassNode nodeOut = new ClassNode(StitchUtil.ASM_VERSION);
+ nodeOut.version = nodeC.version;
+ nodeOut.access = nodeC.access;
+ nodeOut.name = nodeC.name;
+ nodeOut.signature = nodeC.signature;
+ nodeOut.superName = nodeC.superName;
+ nodeOut.sourceFile = nodeC.sourceFile;
+ nodeOut.sourceDebug = nodeC.sourceDebug;
+ nodeOut.outerClass = nodeC.outerClass;
+ nodeOut.outerMethod = nodeC.outerMethod;
+ nodeOut.outerMethodDesc = nodeC.outerMethodDesc;
+ nodeOut.module = nodeC.module;
+ nodeOut.nestHostClass = nodeC.nestHostClass;
+ nodeOut.nestMembers = nodeC.nestMembers;
+ nodeOut.attrs = nodeC.attrs;
+
+ if (nodeC.invisibleAnnotations != null) {
+ nodeOut.invisibleAnnotations = new ArrayList<>();
+ nodeOut.invisibleAnnotations.addAll(nodeC.invisibleAnnotations);
+ }
+
+ if (nodeC.invisibleTypeAnnotations != null) {
+ nodeOut.invisibleTypeAnnotations = new ArrayList<>();
+ nodeOut.invisibleTypeAnnotations.addAll(nodeC.invisibleTypeAnnotations);
+ }
+
+ if (nodeC.visibleAnnotations != null) {
+ nodeOut.visibleAnnotations = new ArrayList<>();
+ nodeOut.visibleAnnotations.addAll(nodeC.visibleAnnotations);
+ }
+
+ if (nodeC.visibleTypeAnnotations != null) {
+ nodeOut.visibleTypeAnnotations = new ArrayList<>();
+ nodeOut.visibleTypeAnnotations.addAll(nodeC.visibleTypeAnnotations);
+ }
+
+ List itfs = StitchUtil.mergePreserveOrder(nodeC.interfaces, nodeS.interfaces);
+ nodeOut.interfaces = new ArrayList<>();
+
+ List clientItfs = new ArrayList<>();
+ List serverItfs = new ArrayList<>();
+
+ for (String s : itfs) {
+ boolean nc = nodeC.interfaces.contains(s);
+ boolean ns = nodeS.interfaces.contains(s);
+ nodeOut.interfaces.add(s);
+
+ if (nc && !ns) {
+ clientItfs.add(s);
+ } else if (ns && !nc) {
+ serverItfs.add(s);
+ }
+ }
+
+ if (!clientItfs.isEmpty() || !serverItfs.isEmpty()) {
+ AnnotationVisitor envInterfaces = nodeOut.visitAnnotation(ITF_LIST_DESCRIPTOR, false);
+ AnnotationVisitor eiArray = envInterfaces.visitArray("value");
+
+ if (!clientItfs.isEmpty()) {
+ visitItfAnnotation(eiArray, "CLIENT", clientItfs);
+ }
+
+ if (!serverItfs.isEmpty()) {
+ visitItfAnnotation(eiArray, "SERVER", serverItfs);
+ }
+
+ eiArray.visitEnd();
+ envInterfaces.visitEnd();
+ }
+
+ new Merger(nodeC.innerClasses, nodeS.innerClasses) {
+ @Override
+ public String getName(InnerClassNode entry) {
+ return entry.name;
+ }
+
+ @Override
+ public void applySide(InnerClassNode entry, String side) {
+ }
+ }.merge(nodeOut.innerClasses);
+
+ new Merger(nodeC.fields, nodeS.fields) {
+ @Override
+ public String getName(FieldNode entry) {
+ return entry.name + ";;" + entry.desc;
+ }
+
+ @Override
+ public void applySide(FieldNode entry, String side) {
+ AnnotationVisitor av = entry.visitAnnotation(SIDED_DESCRIPTOR, false);
+ visitSideAnnotation(av, side);
+ }
+ }.merge(nodeOut.fields);
+
+ new Merger(nodeC.methods, nodeS.methods) {
+ @Override
+ public String getName(MethodNode entry) {
+ return entry.name + entry.desc;
+ }
+
+ @Override
+ public void applySide(MethodNode entry, String side) {
+ AnnotationVisitor av = entry.visitAnnotation(SIDED_DESCRIPTOR, false);
+ visitSideAnnotation(av, side);
+ }
+ }.merge(nodeOut.methods);
+
+ nodeOut.accept(writer);
+ return writer.toByteArray();
+ }
}
diff --git a/src/main/java/net/fabricmc/stitch/merge/JarMerger.java b/src/main/java/net/fabricmc/stitch/merge/JarMerger.java
index addf53b..94fc34c 100644
--- a/src/main/java/net/fabricmc/stitch/merge/JarMerger.java
+++ b/src/main/java/net/fabricmc/stitch/merge/JarMerger.java
@@ -16,211 +16,226 @@
package net.fabricmc.stitch.merge;
-import net.fabricmc.stitch.util.SnowmanClassVisitor;
-import net.fabricmc.stitch.util.StitchUtil;
-import net.fabricmc.stitch.util.SyntheticParameterClassVisitor;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ClassWriter;
-
-import java.io.*;
+import java.io.File;
+import java.io.IOException;
import java.nio.charset.Charset;
-import java.nio.file.*;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
-import java.util.*;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import net.fabricmc.stitch.util.SnowmanClassVisitor;
+import net.fabricmc.stitch.util.StitchUtil;
+import net.fabricmc.stitch.util.SyntheticParameterClassVisitor;
+
public class JarMerger implements AutoCloseable {
- public class Entry {
- public final Path path;
- public final BasicFileAttributes metadata;
- public final byte[] data;
-
- public Entry(Path path, BasicFileAttributes metadata, byte[] data) {
- this.path = path;
- this.metadata = metadata;
- this.data = data;
- }
- }
-
- private static final ClassMerger CLASS_MERGER = new ClassMerger();
- private final StitchUtil.FileSystemDelegate inputClientFs, inputServerFs, outputFs;
- private final Path inputClient, inputServer;
- private final Map entriesClient, entriesServer;
- private final Set entriesAll;
- private boolean removeSnowmen = false;
- private boolean offsetSyntheticsParams = false;
-
- public JarMerger(File inputClient, File inputServer, File output) throws IOException {
- if (output.exists()) {
- if (!output.delete()) {
- throw new IOException("Could not delete " + output.getName());
- }
- }
-
- this.inputClient = (inputClientFs = StitchUtil.getJarFileSystem(inputClient, false)).get().getPath("/");
- this.inputServer = (inputServerFs = StitchUtil.getJarFileSystem(inputServer, false)).get().getPath("/");
- this.outputFs = StitchUtil.getJarFileSystem(output, true);
-
- this.entriesClient = new HashMap<>();
- this.entriesServer = new HashMap<>();
- this.entriesAll = new TreeSet<>();
- }
-
- public void enableSnowmanRemoval() {
- removeSnowmen = true;
- }
-
- public void enableSyntheticParamsOffset() {
- offsetSyntheticsParams = true;
- }
-
- @Override
- public void close() throws IOException {
- inputClientFs.close();
- inputServerFs.close();
- outputFs.close();
- }
-
- private void readToMap(Map map, Path input, boolean isServer) {
- try {
- Files.walkFileTree(input, new SimpleFileVisitor() {
- @Override
- public FileVisitResult visitFile(Path path, BasicFileAttributes attr) throws IOException {
- if (attr.isDirectory()) {
- return FileVisitResult.CONTINUE;
- }
-
- if (!path.getFileName().toString().endsWith(".class")) {
- if (path.toString().equals("/META-INF/MANIFEST.MF")) {
- map.put("META-INF/MANIFEST.MF", new Entry(path, attr,
- "Manifest-Version: 1.0\nMain-Class: net.minecraft.client.Main\n".getBytes(Charset.forName("UTF-8"))));
- } else {
- if (path.toString().startsWith("/META-INF/")) {
- if (path.toString().endsWith(".SF") || path.toString().endsWith(".RSA")) {
- return FileVisitResult.CONTINUE;
- }
- }
-
- map.put(path.toString().substring(1), new Entry(path, attr, null));
- }
-
- return FileVisitResult.CONTINUE;
- }
-
- byte[] output = Files.readAllBytes(path);
- map.put(path.toString().substring(1), new Entry(path, attr, output));
- return FileVisitResult.CONTINUE;
- }
- });
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- private void add(Entry entry) throws IOException {
- Path outPath = outputFs.get().getPath(entry.path.toString());
- if (outPath.getParent() != null) {
- Files.createDirectories(outPath.getParent());
- }
-
- if (entry.data != null) {
- Files.write(outPath, entry.data, StandardOpenOption.CREATE_NEW);
- } else {
- Files.copy(entry.path, outPath);
- }
-
- Files.getFileAttributeView(outPath, BasicFileAttributeView.class)
- .setTimes(
- entry.metadata.creationTime(),
- entry.metadata.lastAccessTime(),
- entry.metadata.lastModifiedTime()
- );
- }
-
- public void merge() throws IOException {
- ExecutorService service = Executors.newFixedThreadPool(2);
- service.submit(() -> readToMap(entriesClient, inputClient, false));
- service.submit(() -> readToMap(entriesServer, inputServer, true));
- service.shutdown();
- try {
- service.awaitTermination(1, TimeUnit.HOURS);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- entriesAll.addAll(entriesClient.keySet());
- entriesAll.addAll(entriesServer.keySet());
-
- List entries = entriesAll.parallelStream().map((entry) -> {
- boolean isClass = entry.endsWith(".class");
- boolean isMinecraft = entriesClient.containsKey(entry) || entry.startsWith("net/minecraft") || !entry.contains("/");
- Entry result;
- String side = null;
-
- Entry entry1 = entriesClient.get(entry);
- Entry entry2 = entriesServer.get(entry);
-
- if (entry1 != null && entry2 != null) {
- if (Arrays.equals(entry1.data, entry2.data)) {
- result = entry1;
- } else {
- if (isClass) {
- result = new Entry(entry1.path, entry1.metadata, CLASS_MERGER.merge(entry1.data, entry2.data));
- } else {
- // FIXME: More heuristics?
- result = entry1;
- }
- }
- } else if ((result = entry1) != null) {
- side = "CLIENT";
- } else if ((result = entry2) != null) {
- side = "SERVER";
- }
-
- if (isClass && !isMinecraft && "SERVER".equals(side)) {
- // Server bundles libraries, client doesn't - skip them
- return null;
- }
-
- if (result != null) {
- if (isMinecraft && isClass) {
- byte[] data = result.data;
- ClassReader reader = new ClassReader(data);
- ClassWriter writer = new ClassWriter(0);
- ClassVisitor visitor = writer;
-
- if (side != null) {
- visitor = new ClassMerger.SidedClassVisitor(StitchUtil.ASM_VERSION, visitor, side);
- }
-
- if (removeSnowmen) {
- visitor = new SnowmanClassVisitor(StitchUtil.ASM_VERSION, visitor);
- }
-
- if (offsetSyntheticsParams) {
- visitor = new SyntheticParameterClassVisitor(StitchUtil.ASM_VERSION, visitor);
- }
-
- if (visitor != writer) {
- reader.accept(visitor, 0);
- data = writer.toByteArray();
- result = new Entry(result.path, result.metadata, data);
- }
- }
-
- return result;
- } else {
- return null;
- }
- }).filter(Objects::nonNull).collect(Collectors.toList());
-
- for (Entry e : entries) {
- add(e);
- }
- }
+ public class Entry {
+ public final Path path;
+ public final BasicFileAttributes metadata;
+ public final byte[] data;
+
+ public Entry(Path path, BasicFileAttributes metadata, byte[] data) {
+ this.path = path;
+ this.metadata = metadata;
+ this.data = data;
+ }
+ }
+
+ private static final ClassMerger CLASS_MERGER = new ClassMerger();
+ private final StitchUtil.FileSystemDelegate inputClientFs, inputServerFs, outputFs;
+ private final Path inputClient, inputServer;
+ private final Map entriesClient, entriesServer;
+ private final Set entriesAll;
+ private boolean removeSnowmen = false;
+ private boolean offsetSyntheticsParams = false;
+
+ public JarMerger(File inputClient, File inputServer, File output) throws IOException {
+ if (output.exists()) {
+ if (!output.delete()) {
+ throw new IOException("Could not delete " + output.getName());
+ }
+ }
+
+ this.inputClient = (inputClientFs = StitchUtil.getJarFileSystem(inputClient, false)).get().getPath("/");
+ this.inputServer = (inputServerFs = StitchUtil.getJarFileSystem(inputServer, false)).get().getPath("/");
+ this.outputFs = StitchUtil.getJarFileSystem(output, true);
+
+ this.entriesClient = new HashMap<>();
+ this.entriesServer = new HashMap<>();
+ this.entriesAll = new TreeSet<>();
+ }
+
+ public void enableSnowmanRemoval() {
+ removeSnowmen = true;
+ }
+
+ public void enableSyntheticParamsOffset() {
+ offsetSyntheticsParams = true;
+ }
+
+ @Override
+ public void close() throws IOException {
+ inputClientFs.close();
+ inputServerFs.close();
+ outputFs.close();
+ }
+
+ private void readToMap(Map map, Path input, boolean isServer) {
+ try {
+ Files.walkFileTree(input, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path path, BasicFileAttributes attr) throws IOException {
+ if (attr.isDirectory()) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ if (!path.getFileName().toString().endsWith(".class")) {
+ if (path.toString().equals("/META-INF/MANIFEST.MF")) {
+ map.put("META-INF/MANIFEST.MF", new Entry(path, attr,
+ "Manifest-Version: 1.0\nMain-Class: net.minecraft.client.Main\n".getBytes(Charset.forName("UTF-8"))));
+ } else {
+ if (path.toString().startsWith("/META-INF/")) {
+ if (path.toString().endsWith(".SF") || path.toString().endsWith(".RSA")) {
+ return FileVisitResult.CONTINUE;
+ }
+ }
+
+ map.put(path.toString().substring(1), new Entry(path, attr, null));
+ }
+
+ return FileVisitResult.CONTINUE;
+ }
+
+ byte[] output = Files.readAllBytes(path);
+ map.put(path.toString().substring(1), new Entry(path, attr, output));
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void add(Entry entry) throws IOException {
+ Path outPath = outputFs.get().getPath(entry.path.toString());
+
+ if (outPath.getParent() != null) {
+ Files.createDirectories(outPath.getParent());
+ }
+
+ if (entry.data != null) {
+ Files.write(outPath, entry.data, StandardOpenOption.CREATE_NEW);
+ } else {
+ Files.copy(entry.path, outPath);
+ }
+
+ Files.getFileAttributeView(outPath, BasicFileAttributeView.class)
+ .setTimes(
+ entry.metadata.creationTime(),
+ entry.metadata.lastAccessTime(),
+ entry.metadata.lastModifiedTime());
+ }
+
+ public void merge() throws IOException {
+ ExecutorService service = Executors.newFixedThreadPool(2);
+ service.submit(() -> readToMap(entriesClient, inputClient, false));
+ service.submit(() -> readToMap(entriesServer, inputServer, true));
+ service.shutdown();
+
+ try {
+ service.awaitTermination(1, TimeUnit.HOURS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ entriesAll.addAll(entriesClient.keySet());
+ entriesAll.addAll(entriesServer.keySet());
+
+ List entries = entriesAll.parallelStream().map((entry) -> {
+ boolean isClass = entry.endsWith(".class");
+ boolean isMinecraft = entriesClient.containsKey(entry)
+ || entry.startsWith("net/minecraft")
+ || !entry.contains("/");
+ Entry result;
+ String side = null;
+
+ Entry entry1 = entriesClient.get(entry);
+ Entry entry2 = entriesServer.get(entry);
+
+ if (entry1 != null && entry2 != null) {
+ if (Arrays.equals(entry1.data, entry2.data)) {
+ result = entry1;
+ } else {
+ if (isClass) {
+ result = new Entry(entry1.path, entry1.metadata, CLASS_MERGER.merge(entry1.data, entry2.data));
+ } else {
+ // FIXME: More heuristics?
+ result = entry1;
+ }
+ }
+ } else if ((result = entry1) != null) {
+ side = "CLIENT";
+ } else if ((result = entry2) != null) {
+ side = "SERVER";
+ }
+
+ if (isClass && !isMinecraft && "SERVER".equals(side)) {
+ // Server bundles libraries, client doesn't - skip them
+ return null;
+ }
+
+ if (result != null) {
+ if (isMinecraft && isClass) {
+ byte[] data = result.data;
+ ClassReader reader = new ClassReader(data);
+ ClassWriter writer = new ClassWriter(0);
+ ClassVisitor visitor = writer;
+
+ if (side != null) {
+ visitor = new ClassMerger.SidedClassVisitor(StitchUtil.ASM_VERSION, visitor, side);
+ }
+
+ if (removeSnowmen) {
+ visitor = new SnowmanClassVisitor(StitchUtil.ASM_VERSION, visitor);
+ }
+
+ if (offsetSyntheticsParams) {
+ visitor = new SyntheticParameterClassVisitor(StitchUtil.ASM_VERSION, visitor);
+ }
+
+ if (visitor != writer) {
+ reader.accept(visitor, 0);
+ data = writer.toByteArray();
+ result = new Entry(result.path, result.metadata, data);
+ }
+ }
+
+ return result;
+ } else {
+ return null;
+ }
+ }).filter(Objects::nonNull).collect(Collectors.toList());
+
+ for (Entry e : entries) {
+ add(e);
+ }
+ }
}
diff --git a/src/main/java/net/fabricmc/stitch/representation/AbstractJarEntry.java b/src/main/java/net/fabricmc/stitch/representation/AbstractJarEntry.java
index 9ff051a..8e6249a 100644
--- a/src/main/java/net/fabricmc/stitch/representation/AbstractJarEntry.java
+++ b/src/main/java/net/fabricmc/stitch/representation/AbstractJarEntry.java
@@ -17,41 +17,41 @@
package net.fabricmc.stitch.representation;
public abstract class AbstractJarEntry {
- protected String name;
- protected int access;
-
- public AbstractJarEntry(String name) {
- this.name = name;
- }
-
- public int getAccess() {
- return access;
- }
-
- protected void setAccess(int value) {
- this.access = value;
- }
-
- public String getName() {
- return name;
- }
-
- protected String getKey() {
- return name;
- }
-
- @Override
- public boolean equals(Object other) {
- return other != null && other.getClass() == getClass() && ((AbstractJarEntry) other).getKey().equals(getKey());
- }
-
- @Override
- public int hashCode() {
- return getKey().hashCode();
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "(" + getKey() + ")";
- }
+ protected String name;
+ protected int access;
+
+ public AbstractJarEntry(String name) {
+ this.name = name;
+ }
+
+ public int getAccess() {
+ return access;
+ }
+
+ protected void setAccess(int value) {
+ this.access = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ protected String getKey() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other != null && other.getClass() == getClass() && ((AbstractJarEntry) other).getKey().equals(getKey());
+ }
+
+ @Override
+ public int hashCode() {
+ return getKey().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "(" + getKey() + ")";
+ }
}
diff --git a/src/main/java/net/fabricmc/stitch/representation/Access.java b/src/main/java/net/fabricmc/stitch/representation/Access.java
index 3b3088b..095303d 100644
--- a/src/main/java/net/fabricmc/stitch/representation/Access.java
+++ b/src/main/java/net/fabricmc/stitch/representation/Access.java
@@ -19,27 +19,26 @@
import org.objectweb.asm.Opcodes;
public final class Access {
- private Access() {
+ public static boolean isStatic(int access) {
+ return (access & Opcodes.ACC_STATIC) != 0;
+ }
- }
+ public static boolean isPrivate(int access) {
+ return (access & Opcodes.ACC_PRIVATE) != 0;
+ }
- public static boolean isStatic(int access) {
- return (access & Opcodes.ACC_STATIC) != 0;
- }
+ public static boolean isPrivateOrStatic(int access) {
+ return (access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC)) != 0;
+ }
- public static boolean isPrivate(int access) {
- return (access & Opcodes.ACC_PRIVATE) != 0;
- }
+ public static boolean isInterface(int access) {
+ return (access & Opcodes.ACC_INTERFACE) != 0;
+ }
- public static boolean isPrivateOrStatic(int access) {
- return (access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC)) != 0;
- }
+ public static boolean isNative(int access) {
+ return (access & (Opcodes.ACC_NATIVE)) != 0;
+ }
- public static boolean isInterface(int access) {
- return (access & Opcodes.ACC_INTERFACE) != 0;
- }
-
- public static boolean isNative(int access) {
- return (access & (Opcodes.ACC_NATIVE)) != 0;
- }
+ private Access() {
+ }
}
diff --git a/src/main/java/net/fabricmc/stitch/representation/ClassPropagationTree.java b/src/main/java/net/fabricmc/stitch/representation/ClassPropagationTree.java
index d46d13f..c9d0d68 100644
--- a/src/main/java/net/fabricmc/stitch/representation/ClassPropagationTree.java
+++ b/src/main/java/net/fabricmc/stitch/representation/ClassPropagationTree.java
@@ -16,9 +16,12 @@
package net.fabricmc.stitch.representation;
-import net.fabricmc.stitch.util.StitchUtil;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Set;
-import java.util.*;
+import net.fabricmc.stitch.util.StitchUtil;
/**
* TODO: This doesn't try to follow the JVM's logic at all.
@@ -26,45 +29,48 @@
* where it could get away with naming them differently.
*/
public class ClassPropagationTree {
- private final ClassStorage jar;
- private final Set relevantClasses;
- private final Set topmostClasses;
+ private final ClassStorage jar;
+ private final Set relevantClasses;
+ private final Set topmostClasses;
+
+ public ClassPropagationTree(ClassStorage jar, JarClassEntry baseClass) {
+ this.jar = jar;
+ relevantClasses = StitchUtil.newIdentityHashSet();
+ topmostClasses = StitchUtil.newIdentityHashSet();
+
+ LinkedList queue = new LinkedList<>();
+ queue.add(baseClass);
+
+ while (!queue.isEmpty()) {
+ JarClassEntry entry = queue.remove();
- public ClassPropagationTree(ClassStorage jar, JarClassEntry baseClass) {
- this.jar = jar;
- relevantClasses = StitchUtil.newIdentityHashSet();
- topmostClasses = StitchUtil.newIdentityHashSet();
+ if (entry == null || relevantClasses.contains(entry)) {
+ continue;
+ }
- LinkedList queue = new LinkedList<>();
- queue.add(baseClass);
+ relevantClasses.add(entry);
+ queue.addAll(entry.getSubclasses(jar));
+ queue.addAll(entry.getImplementers(jar));
+ int qSize = queue.size();
- while (!queue.isEmpty()) {
- JarClassEntry entry = queue.remove();
- if (entry == null || relevantClasses.contains(entry)) {
- continue;
- }
- relevantClasses.add(entry);
+ if (qSize == queue.size()) {
+ topmostClasses.add(entry);
+ }
- int qSize = queue.size();
- queue.addAll(entry.getSubclasses(jar));
- queue.addAll(entry.getImplementers(jar));
- if (qSize == queue.size()) {
- topmostClasses.add(entry);
- }
+ queue.addAll(entry.getInterfaces(jar));
+ JarClassEntry superClass = entry.getSuperClass(jar);
- queue.addAll(entry.getInterfaces(jar));
- JarClassEntry superClass = entry.getSuperClass(jar);
- if (superClass != null) {
- queue.add(superClass);
- }
- }
- }
+ if (superClass != null) {
+ queue.add(superClass);
+ }
+ }
+ }
- public Collection getClasses() {
- return Collections.unmodifiableSet(relevantClasses);
- }
+ public Collection getClasses() {
+ return Collections.unmodifiableSet(relevantClasses);
+ }
- public Collection getTopmostClasses() {
- return Collections.unmodifiableSet(topmostClasses);
- }
+ public Collection getTopmostClasses() {
+ return Collections.unmodifiableSet(topmostClasses);
+ }
}
diff --git a/src/main/java/net/fabricmc/stitch/representation/ClassStorage.java b/src/main/java/net/fabricmc/stitch/representation/ClassStorage.java
index a06f72e..112ccc5 100644
--- a/src/main/java/net/fabricmc/stitch/representation/ClassStorage.java
+++ b/src/main/java/net/fabricmc/stitch/representation/ClassStorage.java
@@ -17,5 +17,5 @@
package net.fabricmc.stitch.representation;
public interface ClassStorage {
- JarClassEntry getClass(String name, boolean create);
+ JarClassEntry getClass(String name, boolean create);
}
diff --git a/src/main/java/net/fabricmc/stitch/representation/JarClassEntry.java b/src/main/java/net/fabricmc/stitch/representation/JarClassEntry.java
index 90a0074..c2523f0 100644
--- a/src/main/java/net/fabricmc/stitch/representation/JarClassEntry.java
+++ b/src/main/java/net/fabricmc/stitch/representation/JarClassEntry.java
@@ -16,197 +16,209 @@
package net.fabricmc.stitch.representation;
-import net.fabricmc.stitch.util.Pair;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
import org.objectweb.asm.commons.Remapper;
-import java.util.*;
-import java.util.stream.Collectors;
+import net.fabricmc.stitch.util.Pair;
public class JarClassEntry extends AbstractJarEntry {
- String fullyQualifiedName;
- final Map innerClasses;
- final Map fields;
- final Map methods;
- final Map>> relatedMethods;
-
- String signature;
- String superclass;
- List interfaces;
- List subclasses;
- List implementers;
-
- protected JarClassEntry(String name, String fullyQualifiedName) {
- super(name);
-
- this.fullyQualifiedName = fullyQualifiedName;
- this.innerClasses = new TreeMap<>(Comparator.naturalOrder());
- this.fields = new TreeMap<>(Comparator.naturalOrder());
- this.methods = new TreeMap<>(Comparator.naturalOrder());
- this.relatedMethods = new HashMap<>();
-
- this.subclasses = new ArrayList<>();
- this.implementers = new ArrayList<>();
- }
-
- protected void populate(int access, String signature, String superclass, String[] interfaces) {
- this.setAccess(access);
- this.signature = signature;
- this.superclass = superclass;
- this.interfaces = Arrays.asList(interfaces);
- }
-
- protected void populateParents(ClassStorage storage) {
- JarClassEntry superEntry = getSuperClass(storage);
- if (superEntry != null) {
- superEntry.subclasses.add(fullyQualifiedName);
- }
-
- for (JarClassEntry itf : getInterfaces(storage)) {
- if (itf != null) {
- itf.implementers.add(fullyQualifiedName);
- }
- }
- }
-
- // unstable
- public Collection> getRelatedMethods(JarMethodEntry m) {
- //noinspection unchecked
- return relatedMethods.getOrDefault(m.getKey(), Collections.EMPTY_SET);
- }
-
- public String getFullyQualifiedName() {
- return fullyQualifiedName;
- }
-
- public String getSignature() {
- return signature;
- }
-
- public String getSuperClassName() {
- return superclass;
- }
-
- public JarClassEntry getSuperClass(ClassStorage storage) {
- return storage.getClass(superclass, false);
- }
-
- public List getInterfaceNames() {
- return Collections.unmodifiableList(interfaces);
- }
-
- public List getInterfaces(ClassStorage storage) {
- return toClassEntryList(storage, interfaces);
- }
-
- public List getSubclassNames() {
- return Collections.unmodifiableList(subclasses);
- }
-
- public List getSubclasses(ClassStorage storage) {
- return toClassEntryList(storage, subclasses);
- }
-
- public List getImplementerNames() {
- return Collections.unmodifiableList(implementers);
- }
-
- public List getImplementers(ClassStorage storage) {
- return toClassEntryList(storage, implementers);
- }
-
- private List toClassEntryList(ClassStorage storage, List stringList) {
- if (stringList == null) {
- return Collections.emptyList();
- }
-
- return stringList.stream()
- .map((s) -> storage.getClass(s, false))
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- }
-
- public JarClassEntry getInnerClass(String name) {
- return innerClasses.get(name);
- }
-
- public JarFieldEntry getField(String name) {
- return fields.get(name);
- }
-
- public JarMethodEntry getMethod(String name) {
- return methods.get(name);
- }
-
- public Collection getInnerClasses() {
- return innerClasses.values();
- }
-
- public Collection getFields() {
- return fields.values();
- }
-
- public Collection getMethods() {
- return methods.values();
- }
-
- public boolean isInterface() {
- return Access.isInterface(getAccess());
- }
-
- public boolean isAnonymous() {
- return getName().matches("[0-9]+");
- }
-
- @Override
- public String getKey() {
- return getFullyQualifiedName();
- }
-
- public void remap(Remapper remapper) {
- String oldName = fullyQualifiedName;
- fullyQualifiedName = remapper.map(fullyQualifiedName);
- String[] s = fullyQualifiedName.split("\\$");
- name = s[s.length - 1];
-
- if (superclass != null) {
- superclass = remapper.map(superclass);
- }
-
- interfaces = interfaces.stream().map(remapper::map).collect(Collectors.toList());
- subclasses = subclasses.stream().map(remapper::map).collect(Collectors.toList());
- implementers = implementers.stream().map(remapper::map).collect(Collectors.toList());
-
- Map innerClassOld = new HashMap<>(innerClasses);
- Map fieldsOld = new HashMap<>(fields);
- Map methodsOld = new HashMap<>(methods);
- Map methodKeyRemaps = new HashMap<>();
-
- innerClasses.clear();
- fields.clear();
- methods.clear();
-
- for (Map.Entry entry : innerClassOld.entrySet()) {
- entry.getValue().remap(remapper);
- innerClasses.put(entry.getValue().name, entry.getValue());
- }
-
- for (Map.Entry entry : fieldsOld.entrySet()) {
- entry.getValue().remap(this, oldName, remapper);
- fields.put(entry.getValue().getKey(), entry.getValue());
- }
-
- for (Map.Entry entry : methodsOld.entrySet()) {
- entry.getValue().remap(this, oldName, remapper);
- methods.put(entry.getValue().getKey(), entry.getValue());
- methodKeyRemaps.put(entry.getKey(), entry.getValue().getKey());
- }
-
- // TODO: remap relatedMethods strings???
- Map>> relatedMethodsOld = new HashMap<>(relatedMethods);
- relatedMethods.clear();
-
- for (Map.Entry>> entry : relatedMethodsOld.entrySet()) {
- relatedMethods.put(methodKeyRemaps.getOrDefault(entry.getKey(), entry.getKey()), entry.getValue());
- }
- }
+ String fullyQualifiedName;
+ final Map innerClasses;
+ final Map fields;
+ final Map methods;
+ final Map>> relatedMethods;
+
+ String signature;
+ String superclass;
+ List interfaces;
+ List subclasses;
+ List implementers;
+
+ protected JarClassEntry(String name, String fullyQualifiedName) {
+ super(name);
+
+ this.fullyQualifiedName = fullyQualifiedName;
+ this.innerClasses = new TreeMap<>(Comparator.naturalOrder());
+ this.fields = new TreeMap<>(Comparator.naturalOrder());
+ this.methods = new TreeMap<>(Comparator.naturalOrder());
+ this.relatedMethods = new HashMap<>();
+
+ this.subclasses = new ArrayList<>();
+ this.implementers = new ArrayList<>();
+ }
+
+ protected void populate(int access, String signature, String superclass, String[] interfaces) {
+ this.setAccess(access);
+ this.signature = signature;
+ this.superclass = superclass;
+ this.interfaces = Arrays.asList(interfaces);
+ }
+
+ protected void populateParents(ClassStorage storage) {
+ JarClassEntry superEntry = getSuperClass(storage);
+
+ if (superEntry != null) {
+ superEntry.subclasses.add(fullyQualifiedName);
+ }
+
+ for (JarClassEntry itf : getInterfaces(storage)) {
+ if (itf != null) {
+ itf.implementers.add(fullyQualifiedName);
+ }
+ }
+ }
+
+ // unstable
+ public Collection> getRelatedMethods(JarMethodEntry m) {
+ //noinspection unchecked
+ return relatedMethods.getOrDefault(m.getKey(), Collections.EMPTY_SET);
+ }
+
+ public String getFullyQualifiedName() {
+ return fullyQualifiedName;
+ }
+
+ public String getSignature() {
+ return signature;
+ }
+
+ public String getSuperClassName() {
+ return superclass;
+ }
+
+ public JarClassEntry getSuperClass(ClassStorage storage) {
+ return storage.getClass(superclass, false);
+ }
+
+ public List getInterfaceNames() {
+ return Collections.unmodifiableList(interfaces);
+ }
+
+ public List getInterfaces(ClassStorage storage) {
+ return toClassEntryList(storage, interfaces);
+ }
+
+ public List getSubclassNames() {
+ return Collections.unmodifiableList(subclasses);
+ }
+
+ public List getSubclasses(ClassStorage storage) {
+ return toClassEntryList(storage, subclasses);
+ }
+
+ public List getImplementerNames() {
+ return Collections.unmodifiableList(implementers);
+ }
+
+ public List getImplementers(ClassStorage storage) {
+ return toClassEntryList(storage, implementers);
+ }
+
+ private List toClassEntryList(ClassStorage storage, List stringList) {
+ if (stringList == null) {
+ return Collections.emptyList();
+ }
+
+ return stringList.stream()
+ .map((s) -> storage.getClass(s, false))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ public JarClassEntry getInnerClass(String name) {
+ return innerClasses.get(name);
+ }
+
+ public JarFieldEntry getField(String name) {
+ return fields.get(name);
+ }
+
+ public JarMethodEntry getMethod(String name) {
+ return methods.get(name);
+ }
+
+ public Collection getInnerClasses() {
+ return innerClasses.values();
+ }
+
+ public Collection getFields() {
+ return fields.values();
+ }
+
+ public Collection getMethods() {
+ return methods.values();
+ }
+
+ public boolean isInterface() {
+ return Access.isInterface(getAccess());
+ }
+
+ public boolean isAnonymous() {
+ return getName().matches("[0-9]+");
+ }
+
+ @Override
+ public String getKey() {
+ return getFullyQualifiedName();
+ }
+
+ public void remap(Remapper remapper) {
+ String oldName = fullyQualifiedName;
+ fullyQualifiedName = remapper.map(fullyQualifiedName);
+ String[] s = fullyQualifiedName.split("\\$");
+ name = s[s.length - 1];
+
+ if (superclass != null) {
+ superclass = remapper.map(superclass);
+ }
+
+ interfaces = interfaces.stream().map(remapper::map).collect(Collectors.toList());
+ subclasses = subclasses.stream().map(remapper::map).collect(Collectors.toList());
+ implementers = implementers.stream().map(remapper::map).collect(Collectors.toList());
+
+ Map innerClassOld = new HashMap<>(innerClasses);
+ Map fieldsOld = new HashMap<>(fields);
+ Map methodsOld = new HashMap<>(methods);
+ Map methodKeyRemaps = new HashMap<>();
+
+ innerClasses.clear();
+ fields.clear();
+ methods.clear();
+
+ for (Map.Entry entry : innerClassOld.entrySet()) {
+ entry.getValue().remap(remapper);
+ innerClasses.put(entry.getValue().name, entry.getValue());
+ }
+
+ for (Map.Entry entry : fieldsOld.entrySet()) {
+ entry.getValue().remap(this, oldName, remapper);
+ fields.put(entry.getValue().getKey(), entry.getValue());
+ }
+
+ for (Map.Entry entry : methodsOld.entrySet()) {
+ entry.getValue().remap(this, oldName, remapper);
+ methods.put(entry.getValue().getKey(), entry.getValue());
+ methodKeyRemaps.put(entry.getKey(), entry.getValue().getKey());
+ }
+
+ // TODO: remap relatedMethods strings???
+ Map>> relatedMethodsOld = new HashMap<>(relatedMethods);
+ relatedMethods.clear();
+
+ for (Map.Entry>> entry : relatedMethodsOld.entrySet()) {
+ relatedMethods.put(methodKeyRemaps.getOrDefault(entry.getKey(), entry.getKey()), entry.getValue());
+ }
+ }
}
diff --git a/src/main/java/net/fabricmc/stitch/representation/JarFieldEntry.java b/src/main/java/net/fabricmc/stitch/representation/JarFieldEntry.java
index 9577cd8..52fd2c7 100644
--- a/src/main/java/net/fabricmc/stitch/representation/JarFieldEntry.java
+++ b/src/main/java/net/fabricmc/stitch/representation/JarFieldEntry.java
@@ -19,33 +19,33 @@
import org.objectweb.asm.commons.Remapper;
public class JarFieldEntry extends AbstractJarEntry {
- protected String desc;
- protected String signature;
-
- JarFieldEntry(int access, String name, String desc, String signature) {
- super(name);
- this.setAccess(access);
- this.desc = desc;
- this.signature = signature;
- }
-
- public String getDescriptor() {
- return desc;
- }
-
- public String getSignature() {
- return signature;
- }
-
- @Override
- protected String getKey() {
- return super.getKey() + desc;
- }
-
- public void remap(JarClassEntry classEntry, String oldOwner, Remapper remapper) {
- String pastDesc = desc;
-
- name = remapper.mapFieldName(oldOwner, name, pastDesc);
- desc = remapper.mapDesc(pastDesc);
- }
+ protected String desc;
+ protected String signature;
+
+ JarFieldEntry(int access, String name, String desc, String signature) {
+ super(name);
+ this.setAccess(access);
+ this.desc = desc;
+ this.signature = signature;
+ }
+
+ public String getDescriptor() {
+ return desc;
+ }
+
+ public String getSignature() {
+ return signature;
+ }
+
+ @Override
+ protected String getKey() {
+ return super.getKey() + desc;
+ }
+
+ public void remap(JarClassEntry classEntry, String oldOwner, Remapper remapper) {
+ String pastDesc = desc;
+
+ name = remapper.mapFieldName(oldOwner, name, pastDesc);
+ desc = remapper.mapDesc(pastDesc);
+ }
}
diff --git a/src/main/java/net/fabricmc/stitch/representation/JarMethodEntry.java b/src/main/java/net/fabricmc/stitch/representation/JarMethodEntry.java
index ce45fae..123ff8d 100644
--- a/src/main/java/net/fabricmc/stitch/representation/JarMethodEntry.java
+++ b/src/main/java/net/fabricmc/stitch/representation/JarMethodEntry.java
@@ -16,111 +16,120 @@
package net.fabricmc.stitch.representation;
-import net.fabricmc.stitch.util.StitchUtil;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
import org.objectweb.asm.commons.Remapper;
-import java.util.*;
+import net.fabricmc.stitch.util.StitchUtil;
public class JarMethodEntry extends AbstractJarEntry {
- protected String desc;
- protected String signature;
-
- protected JarMethodEntry(int access, String name, String desc, String signature) {
- super(name);
- this.setAccess(access);
- this.desc = desc;
- this.signature = signature;
- }
-
- public String getDescriptor() {
- return desc;
- }
-
- public String getSignature() {
- return signature;
- }
-
- @Override
- protected String getKey() {
- return super.getKey() + desc;
- }
-
- public boolean isSource(ClassStorage storage, JarClassEntry c) {
- if (Access.isPrivateOrStatic(getAccess())) {
- return true;
- }
-
- Set entries = StitchUtil.newIdentityHashSet();
- entries.add(c);
- getMatchingSources(entries, storage, c);
- return entries.size() == 1;
- }
-
- public List getMatchingEntries(ClassStorage storage, JarClassEntry c) {
- if (Access.isPrivateOrStatic(getAccess())) {
- return Collections.singletonList(c);
- }
-
- Set entries = StitchUtil.newIdentityHashSet();
- Set entriesNew = StitchUtil.newIdentityHashSet();
- entries.add(c);
- int lastSize = 0;
-
- while (entries.size() > lastSize) {
- lastSize = entries.size();
-
- for (JarClassEntry cc : entries) {
- getMatchingSources(entriesNew, storage, cc);
- }
- entries.addAll(entriesNew);
- entriesNew.clear();
-
- for (JarClassEntry cc : entries) {
- getMatchingEntries(entriesNew, storage, cc, 0);
- }
- entries.addAll(entriesNew);
- entriesNew.clear();
- }
-
- entries.removeIf(cc -> cc.getMethod(getKey()) == null);
-
- return new ArrayList<>(entries);
- }
-
- void getMatchingSources(Collection entries, ClassStorage storage, JarClassEntry c) {
- JarMethodEntry m = c.getMethod(getKey());
- if (m != null) {
- if (!Access.isPrivateOrStatic(m.getAccess())) {
- entries.add(c);
- }
- }
-
- JarClassEntry superClass = c.getSuperClass(storage);
- if (superClass != null) {
- getMatchingSources(entries, storage, superClass);
- }
-
- for (JarClassEntry itf : c.getInterfaces(storage)) {
- getMatchingSources(entries, storage, itf);
- }
- }
-
- void getMatchingEntries(Collection entries, ClassStorage storage, JarClassEntry c, int indent) {
- entries.add(c);
-
- for (JarClassEntry cc : c.getSubclasses(storage)) {
- getMatchingEntries(entries, storage, cc, indent + 1);
- }
-
- for (JarClassEntry cc : c.getImplementers(storage)) {
- getMatchingEntries(entries, storage, cc, indent + 1);
- }
- }
-
- public void remap(JarClassEntry classEntry, String oldOwner, Remapper remapper) {
- String pastDesc = desc;
-
- name = remapper.mapMethodName(oldOwner, name, pastDesc);
- desc = remapper.mapMethodDesc(pastDesc);
- }
-}
\ No newline at end of file
+ protected String desc;
+ protected String signature;
+
+ protected JarMethodEntry(int access, String name, String desc, String signature) {
+ super(name);
+ this.setAccess(access);
+ this.desc = desc;
+ this.signature = signature;
+ }
+
+ public String getDescriptor() {
+ return desc;
+ }
+
+ public String getSignature() {
+ return signature;
+ }
+
+ @Override
+ protected String getKey() {
+ return super.getKey() + desc;
+ }
+
+ public boolean isSource(ClassStorage storage, JarClassEntry c) {
+ if (Access.isPrivateOrStatic(getAccess())) {
+ return true;
+ }
+
+ Set entries = StitchUtil.newIdentityHashSet();
+ entries.add(c);
+ getMatchingSources(entries, storage, c);
+ return entries.size() == 1;
+ }
+
+ public List getMatchingEntries(ClassStorage storage, JarClassEntry c) {
+ if (Access.isPrivateOrStatic(getAccess())) {
+ return Collections.singletonList(c);
+ }
+
+ Set entries = StitchUtil.newIdentityHashSet();
+ Set entriesNew = StitchUtil.newIdentityHashSet();
+ entries.add(c);
+ int lastSize = 0;
+
+ while (entries.size() > lastSize) {
+ lastSize = entries.size();
+
+ for (JarClassEntry cc : entries) {
+ getMatchingSources(entriesNew, storage, cc);
+ }
+
+ entries.addAll(entriesNew);
+ entriesNew.clear();
+
+ for (JarClassEntry cc : entries) {
+ getMatchingEntries(entriesNew, storage, cc, 0);
+ }
+
+ entries.addAll(entriesNew);
+ entriesNew.clear();
+ }
+
+ entries.removeIf(cc -> cc.getMethod(getKey()) == null);
+
+ return new ArrayList<>(entries);
+ }
+
+ void getMatchingSources(Collection entries, ClassStorage storage, JarClassEntry c) {
+ JarMethodEntry m = c.getMethod(getKey());
+
+ if (m != null) {
+ if (!Access.isPrivateOrStatic(m.getAccess())) {
+ entries.add(c);
+ }
+ }
+
+ JarClassEntry superClass = c.getSuperClass(storage);
+
+ if (superClass != null) {
+ getMatchingSources(entries, storage, superClass);
+ }
+
+ for (JarClassEntry itf : c.getInterfaces(storage)) {
+ getMatchingSources(entries, storage, itf);
+ }
+ }
+
+ void getMatchingEntries(Collection entries, ClassStorage storage, JarClassEntry c, int indent) {
+ entries.add(c);
+
+ for (JarClassEntry cc : c.getSubclasses(storage)) {
+ getMatchingEntries(entries, storage, cc, indent + 1);
+ }
+
+ for (JarClassEntry cc : c.getImplementers(storage)) {
+ getMatchingEntries(entries, storage, cc, indent + 1);
+ }
+ }
+
+ public void remap(JarClassEntry classEntry, String oldOwner, Remapper remapper) {
+ String pastDesc = desc;
+
+ name = remapper.mapMethodName(oldOwner, name, pastDesc);
+ desc = remapper.mapMethodDesc(pastDesc);
+ }
+}
diff --git a/src/main/java/net/fabricmc/stitch/representation/JarReader.java b/src/main/java/net/fabricmc/stitch/representation/JarReader.java
index 4f4c048..886b0cb 100644
--- a/src/main/java/net/fabricmc/stitch/representation/JarReader.java
+++ b/src/main/java/net/fabricmc/stitch/representation/JarReader.java
@@ -16,293 +16,305 @@
package net.fabricmc.stitch.representation;
-import net.fabricmc.stitch.util.StitchUtil;
-import org.objectweb.asm.*;
-import org.objectweb.asm.commons.Remapper;
-
import java.io.FileInputStream;
import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.jar.JarInputStream;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.Remapper;
+
+import net.fabricmc.stitch.util.StitchUtil;
+
public class JarReader {
- public static class Builder {
- private final JarReader reader;
-
- private Builder(JarReader reader) {
- this.reader = reader;
- }
-
- public static Builder create(JarRootEntry jar) {
- return new Builder(new JarReader(jar));
- }
-
- public Builder joinMethodEntries(boolean value) {
- reader.joinMethodEntries = value;
- return this;
- }
-
- public Builder withRemapper(Remapper remapper) {
- reader.remapper = remapper;
- return this;
- }
-
- public JarReader build() {
- return reader;
- }
- }
-
- private final JarRootEntry jar;
- private boolean joinMethodEntries = true;
- private Remapper remapper;
-
- public JarReader(JarRootEntry jar) {
- this.jar = jar;
- }
-
- private class VisitorClass extends ClassVisitor {
- private JarClassEntry entry;
-
- public VisitorClass(int api, ClassVisitor classVisitor) {
- super(api, classVisitor);
- }
-
- @Override
- public void visit(final int version, final int access, final String name, final String signature,
- final String superName, final String[] interfaces) {
- this.entry = jar.getClass(name, true);
- this.entry.populate(access, signature, superName, interfaces);
-
- super.visit(version, access, name, signature, superName, interfaces);
- }
-
- @Override
- public FieldVisitor visitField(final int access, final String name, final String descriptor,
- final String signature, final Object value) {
- JarFieldEntry field = new JarFieldEntry(access, name, descriptor, signature);
- this.entry.fields.put(field.getKey(), field);
-
- return new VisitorField(api, super.visitField(access, name, descriptor, signature, value),
- entry, field);
- }
-
- @Override
- public MethodVisitor visitMethod(final int access, final String name, final String descriptor,
- final String signature, final String[] exceptions) {
- JarMethodEntry method = new JarMethodEntry(access, name, descriptor, signature);
- this.entry.methods.put(method.getKey(), method);
-
- return new VisitorMethod(api, super.visitMethod(access, name, descriptor, signature, exceptions),
- entry, method);
- }
- }
-
- private class VisitorClassStageTwo extends ClassVisitor {
- private JarClassEntry entry;
-
- public VisitorClassStageTwo(int api, ClassVisitor classVisitor) {
- super(api, classVisitor);
- }
-
- @Override
- public void visit(final int version, final int access, final String name, final String signature,
- final String superName, final String[] interfaces) {
- this.entry = jar.getClass(name, true);
- super.visit(version, access, name, signature, superName, interfaces);
- }
-
- @Override
- public MethodVisitor visitMethod(final int access, final String name, final String descriptor,
- final String signature, final String[] exceptions) {
- JarMethodEntry method = new JarMethodEntry(access, name, descriptor, signature);
- this.entry.methods.put(method.getKey(), method);
-
- if ((access & (Opcodes.ACC_BRIDGE | Opcodes.ACC_SYNTHETIC)) != 0) {
- return new VisitorBridge(api, access, super.visitMethod(access, name, descriptor, signature, exceptions),
- entry, method);
- } else {
- return super.visitMethod(access, name, descriptor, signature, exceptions);
- }
- }
- }
-
- private class VisitorField extends FieldVisitor {
- private final JarClassEntry classEntry;
- private final JarFieldEntry entry;
-
- public VisitorField(int api, FieldVisitor fieldVisitor, JarClassEntry classEntry, JarFieldEntry entry) {
- super(api, fieldVisitor);
- this.classEntry = classEntry;
- this.entry = entry;
- }
- }
-
- private static class MethodRef {
- final String owner, name, descriptor;
-
- MethodRef(String owner, String name, String descriptor) {
- this.owner = owner;
- this.name = name;
- this.descriptor = descriptor;
- }
- }
-
- private class VisitorBridge extends VisitorMethod {
- private final boolean hasBridgeFlag;
- private final List methodRefs = new ArrayList<>();
-
- public VisitorBridge(int api, int access, MethodVisitor methodVisitor, JarClassEntry classEntry, JarMethodEntry entry) {
- super(api, methodVisitor, classEntry, entry);
- hasBridgeFlag = ((access & Opcodes.ACC_BRIDGE) != 0);
- }
-
- @Override
- public void visitMethodInsn(
- final int opcode,
- final String owner,
- final String name,
- final String descriptor,
- final boolean isInterface) {
- super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
- methodRefs.add(new MethodRef(owner, name, descriptor));
- }
-
- @Override
- public void visitEnd() {
- /* boolean isBridge = hasBridgeFlag;
-
- if (!isBridge && methodRefs.size() == 1) {
- System.out.println("Found suspicious bridge-looking method: " + classEntry.getFullyQualifiedName() + ":" + entry);
- }
-
- if (isBridge) {
- for (MethodRef ref : methodRefs) {
- JarClassEntry targetClass = jar.getClass(ref.owner, true);
- JarMethodEntry targetMethod = new JarMethodEntry(0, ref.name, ref.descriptor, null);
- String targetKey = targetMethod.getKey();
-
- targetClass.relatedMethods.computeIfAbsent(targetKey, (a) -> new HashSet<>()).add(Pair.of(classEntry, entry.getKey()));
- classEntry.relatedMethods.computeIfAbsent(entry.getKey(), (a) -> new HashSet<>()).add(Pair.of(targetClass, targetKey));
- }
- } */
- }
- }
-
- private class VisitorMethod extends MethodVisitor {
- final JarClassEntry classEntry;
- final JarMethodEntry entry;
-
- public VisitorMethod(int api, MethodVisitor methodVisitor, JarClassEntry classEntry, JarMethodEntry entry) {
- super(api, methodVisitor);
- this.classEntry = classEntry;
- this.entry = entry;
- }
- }
-
- public void apply() throws IOException {
- // Stage 1: read .JAR class/field/method meta
- try (FileInputStream fileStream = new FileInputStream(jar.file)) {
- try (JarInputStream jarStream = new JarInputStream(fileStream)) {
- java.util.jar.JarEntry entry;
-
- while ((entry = jarStream.getNextJarEntry()) != null) {
- if (!entry.getName().endsWith(".class")) {
- continue;
- }
-
- ClassReader reader = new ClassReader(jarStream);
- ClassVisitor visitor = new VisitorClass(StitchUtil.ASM_VERSION, null);
- reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
- }
- }
- }
-
- System.err.println("Read " + this.jar.getAllClasses().size() + " (" + this.jar.getClasses().size() + ") classes.");
-
- // Stage 2: find subclasses
- this.jar.getAllClasses().forEach((c) -> c.populateParents(jar));
- System.err.println("Populated subclass entries.");
-
- // Stage 3: join identical MethodEntries
- if (joinMethodEntries) {
- System.err.println("Joining MethodEntries...");
- Set traversedClasses = StitchUtil.newIdentityHashSet();
-
- int joinedMethods = 1;
- int uniqueMethods = 0;
-
- Collection checkedMethods = StitchUtil.newIdentityHashSet();
-
- for (JarClassEntry entry : jar.getAllClasses()) {
- if (traversedClasses.contains(entry)) {
- continue;
- }
-
- ClassPropagationTree tree = new ClassPropagationTree(jar, entry);
- if (tree.getClasses().size() == 1) {
- traversedClasses.add(entry);
- continue;
- }
-
- for (JarClassEntry c : tree.getClasses()) {
- for (JarMethodEntry m : c.getMethods()) {
- if (!checkedMethods.add(m)) {
- continue;
- }
-
- // get all matching entries
- List mList = m.getMatchingEntries(jar, c);
-
- if (mList.size() > 1) {
- for (int i = 0; i < mList.size(); i++) {
- JarClassEntry key = mList.get(i);
- JarMethodEntry value = key.getMethod(m.getKey());
- if (value != m) {
- key.methods.put(m.getKey(), m);
- joinedMethods++;
- }
- }
- }
- }
- }
-
- traversedClasses.addAll(tree.getClasses());
- }
-
- System.err.println("Joined " + joinedMethods + " MethodEntries (" + uniqueMethods + " unique, " + traversedClasses.size() + " classes).");
- }
-
- System.err.println("Collecting additional information...");
-
- // Stage 4: collect additional info
- /* try (FileInputStream fileStream = new FileInputStream(jar.file)) {
- try (JarInputStream jarStream = new JarInputStream(fileStream)) {
- java.util.jar.JarEntry entry;
-
- while ((entry = jarStream.getNextJarEntry()) != null) {
- if (!entry.getName().endsWith(".class")) {
- continue;
- }
-
- ClassReader reader = new ClassReader(jarStream);
- ClassVisitor visitor = new VisitorClassStageTwo(StitchUtil.ASM_VERSION, null);
- reader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
- }
- }
- } */
-
- if (remapper != null) {
- System.err.println("Remapping...");
-
- Map classTree = new HashMap<>(jar.classTree);
- jar.classTree.clear();
-
- for (Map.Entry entry : classTree.entrySet()) {
- entry.getValue().remap(remapper);
- jar.classTree.put(entry.getValue().getKey(), entry.getValue());
- }
- }
-
- System.err.println("- Done. -");
- }
+ public static class Builder {
+ private final JarReader reader;
+
+ private Builder(JarReader reader) {
+ this.reader = reader;
+ }
+
+ public static Builder create(JarRootEntry jar) {
+ return new Builder(new JarReader(jar));
+ }
+
+ public Builder joinMethodEntries(boolean value) {
+ reader.joinMethodEntries = value;
+ return this;
+ }
+
+ public Builder withRemapper(Remapper remapper) {
+ reader.remapper = remapper;
+ return this;
+ }
+
+ public JarReader build() {
+ return reader;
+ }
+ }
+
+ private final JarRootEntry jar;
+ private boolean joinMethodEntries = true;
+ private Remapper remapper;
+
+ public JarReader(JarRootEntry jar) {
+ this.jar = jar;
+ }
+
+ private class VisitorClass extends ClassVisitor {
+ private JarClassEntry entry;
+
+ VisitorClass(int api, ClassVisitor classVisitor) {
+ super(api, classVisitor);
+ }
+
+ @Override
+ public void visit(final int version, final int access, final String name, final String signature,
+ final String superName, final String[] interfaces) {
+ this.entry = jar.getClass(name, true);
+ this.entry.populate(access, signature, superName, interfaces);
+
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public FieldVisitor visitField(final int access, final String name, final String descriptor,
+ final String signature, final Object value) {
+ JarFieldEntry field = new JarFieldEntry(access, name, descriptor, signature);
+ this.entry.fields.put(field.getKey(), field);
+
+ return new VisitorField(api, super.visitField(access, name, descriptor, signature, value),
+ entry, field);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name, final String descriptor,
+ final String signature, final String[] exceptions) {
+ JarMethodEntry method = new JarMethodEntry(access, name, descriptor, signature);
+ this.entry.methods.put(method.getKey(), method);
+
+ return new VisitorMethod(api, super.visitMethod(access, name, descriptor, signature, exceptions),
+ entry, method);
+ }
+ }
+
+ private class VisitorClassStageTwo extends ClassVisitor {
+ private JarClassEntry entry;
+
+ VisitorClassStageTwo(int api, ClassVisitor classVisitor) {
+ super(api, classVisitor);
+ }
+
+ @Override
+ public void visit(final int version, final int access, final String name, final String signature,
+ final String superName, final String[] interfaces) {
+ this.entry = jar.getClass(name, true);
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name, final String descriptor,
+ final String signature, final String[] exceptions) {
+ JarMethodEntry method = new JarMethodEntry(access, name, descriptor, signature);
+ this.entry.methods.put(method.getKey(), method);
+
+ if ((access & (Opcodes.ACC_BRIDGE | Opcodes.ACC_SYNTHETIC)) != 0) {
+ return new VisitorBridge(api, access, super.visitMethod(access, name, descriptor, signature, exceptions),
+ entry, method);
+ } else {
+ return super.visitMethod(access, name, descriptor, signature, exceptions);
+ }
+ }
+ }
+
+ private class VisitorField extends FieldVisitor {
+ private final JarClassEntry classEntry;
+ private final JarFieldEntry entry;
+
+ VisitorField(int api, FieldVisitor fieldVisitor, JarClassEntry classEntry, JarFieldEntry entry) {
+ super(api, fieldVisitor);
+ this.classEntry = classEntry;
+ this.entry = entry;
+ }
+ }
+
+ private static class MethodRef {
+ final String owner, name, descriptor;
+
+ MethodRef(String owner, String name, String descriptor) {
+ this.owner = owner;
+ this.name = name;
+ this.descriptor = descriptor;
+ }
+ }
+
+ private class VisitorBridge extends VisitorMethod {
+ private final boolean hasBridgeFlag;
+ private final List methodRefs = new ArrayList<>();
+
+ VisitorBridge(int api, int access, MethodVisitor methodVisitor, JarClassEntry classEntry, JarMethodEntry entry) {
+ super(api, methodVisitor, classEntry, entry);
+ hasBridgeFlag = ((access & Opcodes.ACC_BRIDGE) != 0);
+ }
+
+ @Override
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String descriptor,
+ final boolean isInterface) {
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ methodRefs.add(new MethodRef(owner, name, descriptor));
+ }
+
+ @Override
+ public void visitEnd() {
+ /* boolean isBridge = hasBridgeFlag;
+
+ if (!isBridge && methodRefs.size() == 1) {
+ System.out.println("Found suspicious bridge-looking method: " + classEntry.getFullyQualifiedName() + ":" + entry);
+ }
+
+ if (isBridge) {
+ for (MethodRef ref : methodRefs) {
+ JarClassEntry targetClass = jar.getClass(ref.owner, true);
+ JarMethodEntry targetMethod = new JarMethodEntry(0, ref.name, ref.descriptor, null);
+ String targetKey = targetMethod.getKey();
+
+ targetClass.relatedMethods.computeIfAbsent(targetKey, (a) -> new HashSet<>()).add(Pair.of(classEntry, entry.getKey()));
+ classEntry.relatedMethods.computeIfAbsent(entry.getKey(), (a) -> new HashSet<>()).add(Pair.of(targetClass, targetKey));
+ }
+ } */
+ }
+ }
+
+ private class VisitorMethod extends MethodVisitor {
+ final JarClassEntry classEntry;
+ final JarMethodEntry entry;
+
+ VisitorMethod(int api, MethodVisitor methodVisitor, JarClassEntry classEntry, JarMethodEntry entry) {
+ super(api, methodVisitor);
+ this.classEntry = classEntry;
+ this.entry = entry;
+ }
+ }
+
+ public void apply() throws IOException {
+ // Stage 1: read .JAR class/field/method meta
+ try (FileInputStream fileStream = new FileInputStream(jar.file)) {
+ try (JarInputStream jarStream = new JarInputStream(fileStream)) {
+ java.util.jar.JarEntry entry;
+
+ while ((entry = jarStream.getNextJarEntry()) != null) {
+ if (!entry.getName().endsWith(".class")) {
+ continue;
+ }
+
+ ClassReader reader = new ClassReader(jarStream);
+ ClassVisitor visitor = new VisitorClass(StitchUtil.ASM_VERSION, null);
+ reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ }
+ }
+ }
+
+ System.err.println("Read " + this.jar.getAllClasses().size() + " (" + this.jar.getClasses().size() + ") classes.");
+
+ // Stage 2: find subclasses
+ this.jar.getAllClasses().forEach((c) -> c.populateParents(jar));
+ System.err.println("Populated subclass entries.");
+
+ // Stage 3: join identical MethodEntries
+ if (joinMethodEntries) {
+ System.err.println("Joining MethodEntries...");
+ Set traversedClasses = StitchUtil.newIdentityHashSet();
+
+ int joinedMethods = 1;
+ int uniqueMethods = 0;
+
+ Collection checkedMethods = StitchUtil.newIdentityHashSet();
+
+ for (JarClassEntry entry : jar.getAllClasses()) {
+ if (traversedClasses.contains(entry)) {
+ continue;
+ }
+
+ ClassPropagationTree tree = new ClassPropagationTree(jar, entry);
+
+ if (tree.getClasses().size() == 1) {
+ traversedClasses.add(entry);
+ continue;
+ }
+
+ for (JarClassEntry c : tree.getClasses()) {
+ for (JarMethodEntry m : c.getMethods()) {
+ if (!checkedMethods.add(m)) {
+ continue;
+ }
+
+ // get all matching entries
+ List mList = m.getMatchingEntries(jar, c);
+
+ if (mList.size() > 1) {
+ for (int i = 0; i < mList.size(); i++) {
+ JarClassEntry key = mList.get(i);
+ JarMethodEntry value = key.getMethod(m.getKey());
+
+ if (value != m) {
+ key.methods.put(m.getKey(), m);
+ joinedMethods++;
+ }
+ }
+ }
+ }
+ }
+
+ traversedClasses.addAll(tree.getClasses());
+ }
+
+ System.err.println("Joined " + joinedMethods + " MethodEntries (" + uniqueMethods + " unique, " + traversedClasses.size() + " classes).");
+ }
+
+ System.err.println("Collecting additional information...");
+
+ // Stage 4: collect additional info
+ /* try (FileInputStream fileStream = new FileInputStream(jar.file)) {
+ try (JarInputStream jarStream = new JarInputStream(fileStream)) {
+ java.util.jar.JarEntry entry;
+
+ while ((entry = jarStream.getNextJarEntry()) != null) {
+ if (!entry.getName().endsWith(".class")) {
+ continue;
+ }
+
+ ClassReader reader = new ClassReader(jarStream);
+ ClassVisitor visitor = new VisitorClassStageTwo(StitchUtil.ASM_VERSION, null);
+ reader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ }
+ }
+ } */
+
+ if (remapper != null) {
+ System.err.println("Remapping...");
+
+ Map classTree = new HashMap<>(jar.classTree);
+ jar.classTree.clear();
+
+ for (Map.Entry entry : classTree.entrySet()) {
+ entry.getValue().remap(remapper);
+ jar.classTree.put(entry.getValue().getKey(), entry.getValue());
+ }
+ }
+
+ System.err.println("- Done. -");
+ }
}
diff --git a/src/main/java/net/fabricmc/stitch/representation/JarRootEntry.java b/src/main/java/net/fabricmc/stitch/representation/JarRootEntry.java
index 169ecb5..86689e2 100644
--- a/src/main/java/net/fabricmc/stitch/representation/JarRootEntry.java
+++ b/src/main/java/net/fabricmc/stitch/representation/JarRootEntry.java
@@ -17,67 +17,74 @@
package net.fabricmc.stitch.representation;
import java.io.File;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
public class JarRootEntry extends AbstractJarEntry implements ClassStorage {
- final Object syncObject = new Object();
- final File file;
- final Map classTree;
- final List allClasses;
-
- public JarRootEntry(File file) {
- super(file.getName());
-
- this.file = file;
- this.classTree = new TreeMap<>(Comparator.naturalOrder());
- this.allClasses = new ArrayList<>();
- }
-
- @Override
- public JarClassEntry getClass(String name, boolean create) {
- if (name == null) {
- return null;
- }
-
- String[] nameSplit = name.split("\\$");
- int i = 0;
-
- JarClassEntry parent;
- JarClassEntry entry = classTree.get(nameSplit[i++]);
- if (entry == null && create) {
- entry = new JarClassEntry(nameSplit[0], nameSplit[0]);
- synchronized (syncObject) {
- allClasses.add(entry);
- classTree.put(entry.getName(), entry);
- }
- }
-
- StringBuilder fullyQualifiedBuilder = new StringBuilder(nameSplit[0]);
-
- while (i < nameSplit.length && entry != null) {
- fullyQualifiedBuilder.append('$');
- fullyQualifiedBuilder.append(nameSplit[i]);
-
- parent = entry;
- entry = entry.getInnerClass(nameSplit[i++]);
-
- if (entry == null && create) {
- entry = new JarClassEntry(nameSplit[i - 1], fullyQualifiedBuilder.toString());
- synchronized (syncObject) {
- allClasses.add(entry);
- parent.innerClasses.put(entry.getName(), entry);
- }
- }
- }
-
- return entry;
- }
-
- public Collection getClasses() {
- return classTree.values();
- }
-
- public Collection getAllClasses() {
- return Collections.unmodifiableList(allClasses);
- }
+ final Object syncObject = new Object();
+ final File file;
+ final Map classTree;
+ final List allClasses;
+
+ public JarRootEntry(File file) {
+ super(file.getName());
+
+ this.file = file;
+ this.classTree = new TreeMap<>(Comparator.naturalOrder());
+ this.allClasses = new ArrayList<>();
+ }
+
+ @Override
+ public JarClassEntry getClass(String name, boolean create) {
+ if (name == null) {
+ return null;
+ }
+
+ String[] nameSplit = name.split("\\$");
+ int i = 0;
+
+ JarClassEntry parent;
+ JarClassEntry entry = classTree.get(nameSplit[i++]);
+
+ if (entry == null && create) {
+ entry = new JarClassEntry(nameSplit[0], nameSplit[0]);
+ synchronized (syncObject) {
+ allClasses.add(entry);
+ classTree.put(entry.getName(), entry);
+ }
+ }
+
+ StringBuilder fullyQualifiedBuilder = new StringBuilder(nameSplit[0]);
+
+ while (i < nameSplit.length && entry != null) {
+ fullyQualifiedBuilder.append('$');
+ fullyQualifiedBuilder.append(nameSplit[i]);
+
+ parent = entry;
+ entry = entry.getInnerClass(nameSplit[i++]);
+
+ if (entry == null && create) {
+ entry = new JarClassEntry(nameSplit[i - 1], fullyQualifiedBuilder.toString());
+ synchronized (syncObject) {
+ allClasses.add(entry);
+ parent.innerClasses.put(entry.getName(), entry);
+ }
+ }
+ }
+
+ return entry;
+ }
+
+ public Collection getClasses() {
+ return classTree.values();
+ }
+
+ public Collection getAllClasses() {
+ return Collections.unmodifiableList(allClasses);
+ }
}
diff --git a/src/main/java/net/fabricmc/stitch/util/FieldNameFinder.java b/src/main/java/net/fabricmc/stitch/util/FieldNameFinder.java
index 8e3ef6d..df8e4a3 100644
--- a/src/main/java/net/fabricmc/stitch/util/FieldNameFinder.java
+++ b/src/main/java/net/fabricmc/stitch/util/FieldNameFinder.java
@@ -16,20 +16,6 @@
package net.fabricmc.stitch.util;
-import net.fabricmc.mappings.EntryTriple;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.FieldInsnNode;
-import org.objectweb.asm.tree.InsnList;
-import org.objectweb.asm.tree.LdcInsnNode;
-import org.objectweb.asm.tree.MethodInsnNode;
-import org.objectweb.asm.tree.MethodNode;
-import org.objectweb.asm.tree.analysis.Analyzer;
-import org.objectweb.asm.tree.analysis.Frame;
-import org.objectweb.asm.tree.analysis.SourceInterpreter;
-import org.objectweb.asm.tree.analysis.SourceValue;
-
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -44,8 +30,22 @@
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
-public class FieldNameFinder {
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.analysis.Analyzer;
+import org.objectweb.asm.tree.analysis.Frame;
+import org.objectweb.asm.tree.analysis.SourceInterpreter;
+import org.objectweb.asm.tree.analysis.SourceValue;
+import net.fabricmc.mappings.EntryTriple;
+
+public class FieldNameFinder {
public Map findNames(Iterable classes) throws Exception {
Map> methods = new HashMap<>();
Map> enumFields = new HashMap<>();
@@ -68,21 +68,25 @@ public Map findNames(Map> allEnumFields
for (Map.Entry> entry : classes.entrySet()) {
String owner = entry.getKey();
Set enumFields = allEnumFields.getOrDefault(owner, Collections.emptySet());
+
for (MethodNode mn : entry.getValue()) {
Frame[] frames = analyzer.analyze(owner, mn);
-
InsnList instrs = mn.instructions;
+
for (int i = 1; i < instrs.size(); i++) {
AbstractInsnNode instr1 = instrs.get(i - 1);
AbstractInsnNode instr2 = instrs.get(i);
String s = null;
- if (instr2.getOpcode() == Opcodes.PUTSTATIC && ((FieldInsnNode) instr2).owner.equals(owner)
- && (instr1 instanceof MethodInsnNode && ((MethodInsnNode) instr1).owner.equals(owner) || enumFields.contains(((FieldInsnNode) instr2).desc + ((FieldInsnNode) instr2).name))
- && (instr1.getOpcode() == Opcodes.INVOKESTATIC || (instr1.getOpcode() == Opcodes.INVOKESPECIAL && "".equals(((MethodInsnNode) instr1).name)))) {
-
+ if (instr2.getOpcode() == Opcodes.PUTSTATIC
+ && ((FieldInsnNode) instr2).owner.equals(owner)
+ && (instr1 instanceof MethodInsnNode
+ && ((MethodInsnNode) instr1).owner.equals(owner)
+ || enumFields.contains(((FieldInsnNode) instr2).desc + ((FieldInsnNode) instr2).name))
+ && (instr1.getOpcode() == Opcodes.INVOKESTATIC || (instr1.getOpcode() == Opcodes.INVOKESPECIAL && "".equals(((MethodInsnNode) instr1).name)))) {
for (int j = 0; j < frames[i - 1].getStackSize(); j++) {
SourceValue sv = frames[i - 1].getStack(j);
+
for (AbstractInsnNode ci : sv.insns) {
if (ci instanceof LdcInsnNode && ((LdcInsnNode) ci).cst instanceof String) {
//if (s == null || !s.equals(((LdcInsnNode) ci).cst)) {
@@ -104,14 +108,17 @@ public Map findNames(Map> allEnumFields
int separator = s.indexOf('/');
String sFirst = s.substring(0, separator);
String sLast;
+
if (s.contains(".") && s.indexOf('.') > separator) {
sLast = s.substring(separator + 1, s.indexOf('.'));
} else {
sLast = s.substring(separator + 1);
}
+
if (sFirst.endsWith("s")) {
sFirst = sFirst.substring(0, sFirst.length() - 1);
}
+
s = sLast + "_" + sFirst;
}
@@ -163,7 +170,7 @@ public Map findNames(File file) {
try {
try (FileInputStream fis = new FileInputStream(file);
- JarInputStream jis = new JarInputStream(fis)) {
+ JarInputStream jis = new JarInputStream(fis)) {
byte[] buffer = new byte[32768];
JarEntry entry;
@@ -174,6 +181,7 @@ public Map findNames(File file) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
int l;
+
while ((l = jis.read(buffer, 0, buffer.length)) > 0) {
stream.write(buffer, 0, l);
}
diff --git a/src/main/java/net/fabricmc/stitch/util/MatcherUtil.java b/src/main/java/net/fabricmc/stitch/util/MatcherUtil.java
index d5865c6..5268853 100644
--- a/src/main/java/net/fabricmc/stitch/util/MatcherUtil.java
+++ b/src/main/java/net/fabricmc/stitch/util/MatcherUtil.java
@@ -16,73 +16,74 @@
package net.fabricmc.stitch.util;
-import net.fabricmc.mappings.EntryTriple;
-
import java.io.BufferedReader;
import java.io.IOException;
import java.util.function.BiConsumer;
-import java.util.function.UnaryOperator;
+
+import net.fabricmc.mappings.EntryTriple;
public final class MatcherUtil {
- private MatcherUtil() {
+ public static void read(BufferedReader reader, boolean invert, BiConsumer classMappingConsumer, BiConsumer fieldMappingConsumer, BiConsumer methodMappingConsumer) throws IOException {
+ String line;
+ String ownerFrom = null, ownerTo = null;
+
+ while ((line = reader.readLine()) != null) {
+ String[] parts = line.split("\t");
+
+ if (parts[0].equals("c") && parts.length == 3) {
+ // class
+ ownerFrom = parts[1].substring(1, parts[1].length() - 1);
+ ownerTo = parts[2].substring(1, parts[2].length() - 1);
- }
+ if (invert) {
+ classMappingConsumer.accept(ownerTo, ownerFrom);
+ } else {
+ classMappingConsumer.accept(ownerFrom, ownerTo);
+ }
+ } else if (parts[0].equals("") && ownerFrom != null && parts.length >= 2) {
+ if (parts[1].equals("f") && parts.length == 4) {
+ String[] fieldFrom = parts[2].split(";;");
+ String[] fieldTo = parts[3].split(";;");
- public static void read(BufferedReader reader, boolean invert, BiConsumer classMappingConsumer, BiConsumer