From 3ff134311d76824e5ba362b359c532af1720662f Mon Sep 17 00:00:00 2001 From: Agaricus Date: Tue, 22 Jan 2013 22:15:13 -0800 Subject: [PATCH] 13x performance increase in jar comparison by parsing method signatures New class to parse and replace method signatures, instead of iterating over each class. On my machine, old technique takes 6.8 to compare Minecraft 1.4.7, new technique: 0.5 seconds, with identical results. --- .../net/md_5/specialsource/JarMapping.java | 17 +--- .../net/md_5/specialsource/JarRemapper.java | 40 +++++----- .../MethodDescriptorTransformer.java | 79 +++++++++++++++++++ 3 files changed, 104 insertions(+), 32 deletions(-) create mode 100644 src/main/java/net/md_5/specialsource/MethodDescriptorTransformer.java diff --git a/src/main/java/net/md_5/specialsource/JarMapping.java b/src/main/java/net/md_5/specialsource/JarMapping.java index 9e01c74..b75e4cb 100644 --- a/src/main/java/net/md_5/specialsource/JarMapping.java +++ b/src/main/java/net/md_5/specialsource/JarMapping.java @@ -29,7 +29,6 @@ package net.md_5.specialsource; import java.io.*; -import java.text.MessageFormat; import java.util.*; public class JarMapping { @@ -79,7 +78,7 @@ public class JarMapping { } else if (tokens.length == 4) { String oldClassName = shader.shade(tokens[0]); String oldMethodName = tokens[1]; - String oldMethodDescriptor = tokens[2]; + String oldMethodDescriptor = tokens[2]; // TODO shader.shadeMethodSignature(tokens[2]); String newMethodName = tokens[3]; methods.put(oldClassName + "/" + oldMethodName + " " + oldMethodDescriptor, newMethodName); } @@ -87,14 +86,6 @@ public class JarMapping { } } - private static String shade(String className, ShadeRelocationSimulator shadeRelocationSimulator) { - if (shadeRelocationSimulator == null) { - return className; - } - - return shadeRelocationSimulator.shade(className); - } - /** * Generate a mapping given an original jar and renamed jar * @param oldJar Original jar @@ -144,10 +135,8 @@ public class JarMapping { String key = oldMethod.owner + "/" + oldMethod.name + " " + oldMethod.descriptor; methods.put(key, newMethod.name); - String oldDescriptor = oldMethod.descriptor; - for (Map.Entry entry : classes.entrySet()) { - oldDescriptor = oldDescriptor.replaceAll("L" + entry.getKey() + ";", "L" + entry.getValue() + ";"); // TODO: efficiency - } + MethodDescriptorTransformer methodDescriptorTransformer = new MethodDescriptorTransformer(null, classes); + String oldDescriptor = methodDescriptorTransformer.transform(oldMethod.descriptor); if (!Objects.equals(oldMethod.name + " " + oldDescriptor, newMethod.name + " " + newMethod.descriptor)) { srgWriter.addMethodMap(oldMethod, newMethod); diff --git a/src/main/java/net/md_5/specialsource/JarRemapper.java b/src/main/java/net/md_5/specialsource/JarRemapper.java index c1fb300..3bfee91 100644 --- a/src/main/java/net/md_5/specialsource/JarRemapper.java +++ b/src/main/java/net/md_5/specialsource/JarRemapper.java @@ -33,16 +33,9 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.PrintWriter; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; import java.util.Enumeration; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import org.objectweb.asm.ClassReader; @@ -64,23 +57,34 @@ public class JarRemapper extends Remapper { @Override public String map(String typeName) { + return mapTypeName(typeName, jarMapping.packages, jarMapping.classes); + } + + public static String mapTypeName(String typeName, Map packageMap, Map classMap) { int index = typeName.indexOf('$'); String key = (index == -1) ? typeName : typeName.substring(0, index); - String mapped = null; - for (String oldPackage : jarMapping.packages.keySet()) { - if (key.startsWith(oldPackage)) { - String newPackage = jarMapping.packages.get(oldPackage); - mapped = newPackage + key.substring(oldPackage.length()); - break; - } - } - if (mapped == null) { - mapped = jarMapping.classes.get(key); - } + String mapped = mapClassName(typeName, packageMap, classMap); return mapped != null ? mapped + (index == -1 ? "" : typeName.substring(index, typeName.length())) : typeName; } + /** + * Helper method to map a class name by package (prefix) or class (exact) map + */ + private static String mapClassName(String className, Map packageMap, Map classMap) { + if (packageMap != null) { + for (String oldPackage : packageMap.keySet()) { + if (className.startsWith(oldPackage)) { + String newPackage = packageMap.get(oldPackage); + + return newPackage + className.substring(oldPackage.length()); + } + } + } + + return classMap != null ? classMap.get(className) : null; + } + @Override public String mapFieldName(String owner, String name, String desc) { String mapped = tryClimb(jarMapping.fields, NodeType.FIELD, owner, name); diff --git a/src/main/java/net/md_5/specialsource/MethodDescriptorTransformer.java b/src/main/java/net/md_5/specialsource/MethodDescriptorTransformer.java new file mode 100644 index 0000000..0ea2f43 --- /dev/null +++ b/src/main/java/net/md_5/specialsource/MethodDescriptorTransformer.java @@ -0,0 +1,79 @@ +package net.md_5.specialsource; + +import java.util.Map; + +public class MethodDescriptorTransformer { + private Map packageMap; + private Map classMap; + + public MethodDescriptorTransformer(Map packageMap, Map classMap) { + this.packageMap = packageMap; + this.classMap = classMap; + } + + public String transform(String input) { + StringBuilder output = new StringBuilder(); + + int i = 0; + while(i < input.length()) { + char c = input.charAt(i); + + switch(c) + { + // class + case 'L': + String rest = input.substring(i); + int end = rest.indexOf(';'); + if (end == -1) { + throw new IllegalArgumentException("Invalid method descriptor, found L but missing ;: " + input); + } + String className = rest.substring(1, end); + i += className.length() + 1; + + String newClassName = JarRemapper.mapTypeName(className, packageMap, classMap); + + output.append("L" + newClassName + ";"); + break; + + // primitive type + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'V': + case 'Z': + + // arguments + case '(': + case ')': + + // array + case '[': + output.append(c); + break; + + case 'T': + throw new IllegalArgumentException("Method descriptors with type variables unsupported: "+c); + case '<': + throw new IllegalArgumentException("Method descriptors with optional arguments unsupported: "+c); + case '*': + case '+': + case '-': + throw new IllegalArgumentException("Method descriptors with wildcards unsupported: "+c); + case '!': + case '|': + case 'Q': + throw new IllegalArgumentException("Method descriptors with advanced types unsupported: "+c); + default: + throw new IllegalArgumentException("Unrecognized type in method descriptor: " + c); + } + + i += 1; + } + + return output.toString(); + } +}