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.
This commit is contained in:
Agaricus 2013-01-22 22:15:13 -08:00
parent 97d8e80769
commit 3ff134311d
3 changed files with 104 additions and 32 deletions

View File

@ -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<String, String> 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);

View File

@ -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<String, String> packageMap, Map<String, String> 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<String, String> packageMap, Map<String, String> 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);

View File

@ -0,0 +1,79 @@
package net.md_5.specialsource;
import java.util.Map;
public class MethodDescriptorTransformer {
private Map<String, String> packageMap;
private Map<String, String> classMap;
public MethodDescriptorTransformer(Map<String, String> packageMap, Map<String, String> 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();
}
}