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:
parent
97d8e80769
commit
3ff134311d
@ -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);
|
||||
|
@ -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) {
|
||||
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);
|
||||
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 = 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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user