diff --git a/pom.xml b/pom.xml index a3fc4dc..6f65598 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ net.md-5 SpecialSource - 1.9.1-SNAPSHOT + 1.10.0 jar SpecialSource diff --git a/src/main/java/net/md_5/specialsource/JarMapping.java b/src/main/java/net/md_5/specialsource/JarMapping.java index 8069ab6..9362338 100644 --- a/src/main/java/net/md_5/specialsource/JarMapping.java +++ b/src/main/java/net/md_5/specialsource/JarMapping.java @@ -41,6 +41,8 @@ import net.md_5.specialsource.transformer.MappingTransformer; import java.io.*; import java.lang.reflect.Modifier; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.objectweb.asm.commons.Remapper; @@ -94,10 +96,13 @@ public class JarMapping { return false; } - public String tryClimb(Map map, NodeType type, String owner, String name, int access) { + public String tryClimb(Map map, NodeType type, String owner, String name, String desc, int access) { String key = owner + "/" + name; - String mapped = map.get(key); + String mapped = (desc != null) ? map.get(key + "/" + desc) : null; + if (mapped == null) { + mapped = map.get(key); + } if (mapped == null && (access == -1 || (!Modifier.isPrivate(access) && !Modifier.isStatic(access)))) { Collection parents = null; @@ -111,7 +116,7 @@ public class JarMapping { if (parents != null) { // climb the inheritance tree for (String parent : parents) { - mapped = tryClimb(map, type, parent, name, access); + mapped = tryClimb(map, type, parent, name, desc, access); if (mapped != null) { return mapped; } @@ -279,19 +284,29 @@ public class JarMapping { //Gather class mappings here so that we can support reversing csrg/tsrg. final Map clsMap = new HashMap<>(); + final Map prgMap = new HashMap<>(); for (String l : lines) { - if (l.contains(":")) { + if (l.endsWith(":")) { + String[] parts = l.split(" -> "); + String orig = parts[0].replace('.', '/'); + String obf = parts[1].substring(0, parts[1].length() - 1).replace('.', '/'); + + clsMap.put(obf, orig); + prgMap.put(orig, obf); + } else if (l.contains(":")) { if (!l.startsWith("CL:")) { continue; } String[] tokens = l.split(" "); - clsMap.put(tokens[1], tokens[1]); + clsMap.put(tokens[0], tokens[1]); } else { if (l.startsWith("\t")) { continue; } String[] tokens = l.split(" "); - clsMap.put(tokens[0], tokens[1]); + if (tokens.length == 2) { + clsMap.put(tokens[0], tokens[1]); + } } meter.makeProgress(); } @@ -308,7 +323,9 @@ public class JarMapping { if (l.startsWith("tsrg2")) { continue; } - if (l.contains(":")) { + if (l.contains(" -> ")) { + parseProguardLine(l, inputTransformer, outputTransformer, reverse, reverseMapper, prgMap); + } else if (l.contains(":")) { // standard srg parseSrgLine(l, inputTransformer, outputTransformer, reverse); } else { @@ -321,6 +338,96 @@ public class JarMapping { currentClass = null; } + private static final Pattern MEMBER_PATTERN = Pattern.compile("(?:\\d+:\\d+:)?(.*?) (.*?) \\-> (.*)"); + private void parseProguardLine(String line, MappingTransformer inputTransformer, MappingTransformer outputTransformer, boolean reverse, Remapper reverseMap, Map prgMap) throws IOException { + //Tsrg format, identical to Csrg, except the field and method lines start with \t and should use the last class the was parsed. + if (line.startsWith(" ")) { + if (this.currentClass == null) { + throw new IOException("Invalid proguard file, tsrg field/method line before class line: " + line); + } + line = line.trim(); + } + + if (line.endsWith(":")) { + String[] parts = line.split(" -> "); + String orig = parts[0].replace('.', '/'); + String obf = parts[1].substring(0, parts[1].length() - 1).replace('.', '/'); + + String oldClassName = inputTransformer.transformClassName(obf); + String newClassName = outputTransformer.transformClassName(orig); + + if (oldClassName.endsWith("/")) { + // Special case: mapping an entire hierarchy of classes + if (reverse) { + packages.put(newClassName, oldClassName.substring(0, oldClassName.length() - 1)); + } else { + packages.put(oldClassName.substring(0, oldClassName.length() - 1), newClassName); + } + } else { + if (reverse) { + classes.put(newClassName, oldClassName); + currentClass = orig; + } else { + classes.put(oldClassName, newClassName); + currentClass = obf; + } + } + } else { + Matcher matcher = MEMBER_PATTERN.matcher(line); + matcher.find(); + + String obfName = matcher.group(3); + String nameDesc = matcher.group(2); + if (nameDesc.contains("(")) { + String desc = ProguardUtil.csrgDesc(prgMap, nameDesc.substring(nameDesc.indexOf('(')), matcher.group(1)); + String newName = nameDesc.substring(0, nameDesc.indexOf('(')); + + // System.out.println( lastClass + " " + matcher.group( 3 ) + " " + sig + " " + mojName ); + String oldClassName = inputTransformer.transformClassName(currentClass); + String oldMethodName = inputTransformer.transformMethodName(currentClass, obfName, desc); + String oldMethodDescriptor = inputTransformer.transformMethodDescriptor(desc); + String newMethodName = outputTransformer.transformMethodName(currentClass, newName, desc); + + if (reverse) { + String newClassName = reverseMap.map(oldClassName); + if (newClassName.equals(oldClassName)) { + // throw new IOException("Invalid csrg file line, could not be reversed: " + line); + } + oldClassName = newClassName; + oldMethodDescriptor = reverseMap.mapMethodDesc(oldMethodDescriptor); + + String temp = newMethodName; + newMethodName = oldMethodName; + oldMethodName = temp; + } + + methods.put(oldClassName + "/" + oldMethodName + " " + oldMethodDescriptor, newMethodName); + } else { + String desc = ProguardUtil.toJVMType(prgMap, matcher.group(1)); + + String oldClassName = inputTransformer.transformClassName(currentClass); + String oldFieldName = inputTransformer.transformFieldName(currentClass, obfName); + String oldFieldDescriptor = inputTransformer.transformMethodDescriptor(desc); + String newFieldName = outputTransformer.transformFieldName(currentClass, nameDesc); + + if (reverse) { + String newClassName = reverseMap.map(oldClassName); + if (newClassName.equals(oldClassName)) { + // throw new IOException("Invalid csrg file line, could not be reversed: " + line); + } + oldClassName = newClassName; + oldFieldDescriptor = reverseMap.mapDesc(oldFieldDescriptor); + + String temp = newFieldName; + newFieldName = oldFieldName; + oldFieldName = temp; + } + + fields.put(oldClassName + "/" + oldFieldName + "/" + oldFieldDescriptor, newFieldName); + } + } + } + /** * Parse a 'csrg' mapping format line and populate the data structures */ @@ -367,7 +474,7 @@ public class JarMapping { if (reverse) { String newClassName = reverseMap.map(oldClassName); if (newClassName.equals(oldClassName)) { - throw new IOException("Invalid csrg file line, could not be reversed: " + line); + // throw new IOException("Invalid csrg file line, could not be reversed: " + line); } oldClassName = newClassName; @@ -386,7 +493,7 @@ public class JarMapping { if (reverse) { String newClassName = reverseMap.map(oldClassName); if (newClassName.equals(oldClassName)) { - throw new IOException("Invalid csrg file line, could not be reversed: " + line); + // throw new IOException("Invalid csrg file line, could not be reversed: " + line); } oldClassName = newClassName; oldMethodDescriptor = reverseMap.mapMethodDesc(oldMethodDescriptor); @@ -612,4 +719,52 @@ public class JarMapping { srgWriter.write(out); } } + + private static class ProguardUtil { + + private static String csrgDesc(Map data, String args, String ret) { + String[] parts = args.substring(1, args.length() - 1).split(","); + StringBuilder desc = new StringBuilder("("); + for (String part : parts) { + if (part.isEmpty()) { + continue; + } + desc.append(toJVMType(data, part)); + } + desc.append(")"); + desc.append(toJVMType(data, ret)); + return desc.toString(); + } + + private static String toJVMType(Map data, String type) { + switch (type) { + case "byte": + return "B"; + case "char": + return "C"; + case "double": + return "D"; + case "float": + return "F"; + case "int": + return "I"; + case "long": + return "J"; + case "short": + return "S"; + case "boolean": + return "Z"; + case "void": + return "V"; + default: + if (type.endsWith("[]")) { + return "[" + toJVMType(data, type.substring(0, type.length() - 2)); + } + String clazzType = type.replace('.', '/'); + String mappedType = data.get(clazzType); + + return "L" + ((mappedType != null) ? mappedType : clazzType) + ";"; + } + } + } } diff --git a/src/main/java/net/md_5/specialsource/JarRemapper.java b/src/main/java/net/md_5/specialsource/JarRemapper.java index b7460fa..e4ee298 100644 --- a/src/main/java/net/md_5/specialsource/JarRemapper.java +++ b/src/main/java/net/md_5/specialsource/JarRemapper.java @@ -155,13 +155,13 @@ public class JarRemapper extends CustomRemapper { @Override public String mapFieldName(String owner, String name, String desc, int access) { - String mapped = jarMapping.tryClimb(jarMapping.fields, NodeType.FIELD, owner, name, access); + String mapped = jarMapping.tryClimb(jarMapping.fields, NodeType.FIELD, owner, name, desc, access); return mapped == null ? name : mapped; } @Override public String mapMethodName(String owner, String name, String desc, int access) { - String mapped = jarMapping.tryClimb(jarMapping.methods, NodeType.METHOD, owner, name + " " + desc, access); + String mapped = jarMapping.tryClimb(jarMapping.methods, NodeType.METHOD, owner, name + " " + desc, null, access); return mapped == null ? name : mapped; } diff --git a/src/main/java/net/md_5/specialsource/RemapperProcessor.java b/src/main/java/net/md_5/specialsource/RemapperProcessor.java index b17b8de..65e381a 100644 --- a/src/main/java/net/md_5/specialsource/RemapperProcessor.java +++ b/src/main/java/net/md_5/specialsource/RemapperProcessor.java @@ -229,7 +229,7 @@ public class RemapperProcessor { } String className = ((Type) ldcClass.cst).getInternalName(); - String newName = jarMapping.tryClimb(jarMapping.fields, NodeType.FIELD, className, fieldName, 0); + String newName = jarMapping.tryClimb(jarMapping.fields, NodeType.FIELD, className, fieldName, null, 0); logR("Remapping " + className + "/" + fieldName + " -> " + newName); if (newName != null) {