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;
|
package net.md_5.specialsource;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class JarMapping {
|
public class JarMapping {
|
||||||
@ -79,7 +78,7 @@ public class JarMapping {
|
|||||||
} else if (tokens.length == 4) {
|
} else if (tokens.length == 4) {
|
||||||
String oldClassName = shader.shade(tokens[0]);
|
String oldClassName = shader.shade(tokens[0]);
|
||||||
String oldMethodName = tokens[1];
|
String oldMethodName = tokens[1];
|
||||||
String oldMethodDescriptor = tokens[2];
|
String oldMethodDescriptor = tokens[2]; // TODO shader.shadeMethodSignature(tokens[2]);
|
||||||
String newMethodName = tokens[3];
|
String newMethodName = tokens[3];
|
||||||
methods.put(oldClassName + "/" + oldMethodName + " " + oldMethodDescriptor, newMethodName);
|
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
|
* Generate a mapping given an original jar and renamed jar
|
||||||
* @param oldJar Original jar
|
* @param oldJar Original jar
|
||||||
@ -144,10 +135,8 @@ public class JarMapping {
|
|||||||
String key = oldMethod.owner + "/" + oldMethod.name + " " + oldMethod.descriptor;
|
String key = oldMethod.owner + "/" + oldMethod.name + " " + oldMethod.descriptor;
|
||||||
methods.put(key, newMethod.name);
|
methods.put(key, newMethod.name);
|
||||||
|
|
||||||
String oldDescriptor = oldMethod.descriptor;
|
MethodDescriptorTransformer methodDescriptorTransformer = new MethodDescriptorTransformer(null, classes);
|
||||||
for (Map.Entry<String, String> entry : classes.entrySet()) {
|
String oldDescriptor = methodDescriptorTransformer.transform(oldMethod.descriptor);
|
||||||
oldDescriptor = oldDescriptor.replaceAll("L" + entry.getKey() + ";", "L" + entry.getValue() + ";"); // TODO: efficiency
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Objects.equals(oldMethod.name + " " + oldDescriptor, newMethod.name + " " + newMethod.descriptor)) {
|
if (!Objects.equals(oldMethod.name + " " + oldDescriptor, newMethod.name + " " + newMethod.descriptor)) {
|
||||||
srgWriter.addMethodMap(oldMethod, newMethod);
|
srgWriter.addMethodMap(oldMethod, newMethod);
|
||||||
|
@ -33,16 +33,9 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
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.Enumeration;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarOutputStream;
|
import java.util.jar.JarOutputStream;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
@ -64,23 +57,34 @@ public class JarRemapper extends Remapper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String map(String typeName) {
|
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('$');
|
int index = typeName.indexOf('$');
|
||||||
String key = (index == -1) ? typeName : typeName.substring(0, index);
|
String key = (index == -1) ? typeName : typeName.substring(0, index);
|
||||||
String mapped = null;
|
String mapped = mapClassName(typeName, packageMap, classMap);
|
||||||
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 mapped != null ? mapped + (index == -1 ? "" : typeName.substring(index, typeName.length())) : typeName;
|
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
|
@Override
|
||||||
public String mapFieldName(String owner, String name, String desc) {
|
public String mapFieldName(String owner, String name, String desc) {
|
||||||
String mapped = tryClimb(jarMapping.fields, NodeType.FIELD, owner, name);
|
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