2012-11-27 12:02:36 +00:00
|
|
|
/**
|
2012-12-08 23:15:58 +00:00
|
|
|
* Copyright (c) 2012, md_5. All rights reserved.
|
2012-11-27 12:02:36 +00:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
2012-12-08 23:15:58 +00:00
|
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* The name of the author may not be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
2012-11-27 12:02:36 +00:00
|
|
|
*/
|
|
|
|
package net.md_5.specialsource;
|
|
|
|
|
2013-01-27 21:10:54 +00:00
|
|
|
import java.io.*;
|
2013-01-24 03:32:21 +00:00
|
|
|
import java.util.ArrayList;
|
2013-01-23 04:31:41 +00:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2013-01-22 04:12:31 +00:00
|
|
|
|
2013-01-30 04:56:44 +00:00
|
|
|
import com.google.common.collect.BiMap;
|
|
|
|
import com.google.common.collect.HashBiMap;
|
2013-01-22 04:12:31 +00:00
|
|
|
import joptsimple.OptionException;
|
|
|
|
import joptsimple.OptionParser;
|
|
|
|
import joptsimple.OptionSet;
|
2012-11-27 12:02:36 +00:00
|
|
|
import org.objectweb.asm.ClassReader;
|
|
|
|
|
2013-01-22 04:12:31 +00:00
|
|
|
import static java.util.Arrays.asList;
|
|
|
|
|
2012-11-27 12:02:36 +00:00
|
|
|
public class SpecialSource {
|
2013-01-23 08:55:14 +00:00
|
|
|
|
2013-01-22 04:21:33 +00:00
|
|
|
private static OptionSet options;
|
2012-11-27 12:02:36 +00:00
|
|
|
|
|
|
|
public static void main(String[] args) throws Exception {
|
2013-01-22 04:12:31 +00:00
|
|
|
OptionParser parser = new OptionParser() {
|
|
|
|
{
|
|
|
|
acceptsAll(asList("?", "help"), "Show the help");
|
|
|
|
|
2013-01-24 03:47:15 +00:00
|
|
|
acceptsAll(asList("a", "first-jar"), "First jar with original names, for generating mapping")
|
2013-01-22 04:12:31 +00:00
|
|
|
.withRequiredArg()
|
|
|
|
.ofType(File.class);
|
|
|
|
|
2013-01-24 03:47:15 +00:00
|
|
|
acceptsAll(asList("b", "second-jar"), "Second jar with renamed names, for generating mapping")
|
2013-01-22 04:12:31 +00:00
|
|
|
.withRequiredArg()
|
|
|
|
.ofType(File.class);
|
|
|
|
|
2013-01-22 04:47:38 +00:00
|
|
|
acceptsAll(asList("s", "srg-out"), "Mapping file output")
|
2013-01-22 04:12:31 +00:00
|
|
|
.withRequiredArg()
|
|
|
|
.ofType(File.class);
|
|
|
|
|
2013-01-24 03:47:15 +00:00
|
|
|
acceptsAll(asList("c", "compact"), "Output mapping file in compact format");
|
2013-01-29 06:42:01 +00:00
|
|
|
acceptsAll(asList("f", "generate-dupes"), "Include unrenamed symbols in mapping file output");
|
2013-01-24 03:47:15 +00:00
|
|
|
|
2013-01-22 08:17:48 +00:00
|
|
|
acceptsAll(asList("m", "srg-in"), "Mapping file input")
|
|
|
|
.withRequiredArg()
|
|
|
|
.ofType(File.class);
|
|
|
|
|
|
|
|
acceptsAll(asList("i", "in-jar"), "Input jar to remap")
|
2013-01-22 04:12:31 +00:00
|
|
|
.withRequiredArg()
|
|
|
|
.ofType(File.class);
|
|
|
|
|
|
|
|
acceptsAll(asList("o", "out-jar"), "Output jar to write")
|
|
|
|
.withRequiredArg()
|
|
|
|
.ofType(File.class);
|
2013-01-22 04:21:33 +00:00
|
|
|
|
2013-01-23 04:31:41 +00:00
|
|
|
acceptsAll(asList("R", "shade-relocation"), "Simulate maven-shade-plugin relocation patterns on srg-in")
|
|
|
|
.withRequiredArg()
|
|
|
|
.withValuesSeparatedBy(',');
|
|
|
|
|
2013-01-23 08:23:38 +00:00
|
|
|
acceptsAll(asList("l", "live"), "Enable runtime inheritance lookup");
|
2013-01-24 04:41:43 +00:00
|
|
|
acceptsAll(asList("L", "live-remapped"), "Enable runtime inheritance lookup through a mapping");
|
2013-01-23 08:23:38 +00:00
|
|
|
|
2013-01-27 21:10:54 +00:00
|
|
|
acceptsAll(asList("H", "write-inheritance"), "Write inheritance map to file")
|
|
|
|
.withRequiredArg()
|
|
|
|
.ofType(File.class);
|
2013-01-30 04:31:47 +00:00
|
|
|
acceptsAll(asList("h", "read-inheritance"), "Read inheritance map from file")
|
|
|
|
.withRequiredArg()
|
|
|
|
.ofType(File.class);
|
2013-01-27 21:10:54 +00:00
|
|
|
|
2013-02-06 05:09:08 +00:00
|
|
|
//acceptsAll(asList("G", "remap-reflect-field"), "Remap reflection calls to getDeclaredField()"); // TODO
|
|
|
|
|
2013-01-22 04:21:33 +00:00
|
|
|
acceptsAll(asList("q", "quiet"), "Quiet mode");
|
2013-01-22 04:12:31 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
options = parser.parse(args);
|
|
|
|
} catch (OptionException ex) {
|
|
|
|
System.out.println(ex.getLocalizedMessage());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-22 08:17:48 +00:00
|
|
|
if (options == null || options.has("?")) {
|
2013-01-22 04:12:31 +00:00
|
|
|
try {
|
2013-01-22 08:17:48 +00:00
|
|
|
parser.printHelpOn(System.err);
|
2013-01-22 04:12:31 +00:00
|
|
|
return;
|
|
|
|
} catch (IOException ex) {
|
|
|
|
System.out.println(ex.getLocalizedMessage());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-22 08:17:48 +00:00
|
|
|
JarMapping jarMapping;
|
|
|
|
|
|
|
|
if (options.has("first-jar") && options.has("second-jar")) {
|
|
|
|
// Generate mappings from two otherwise-identical jars
|
|
|
|
log("Reading jars");
|
2013-01-23 08:55:14 +00:00
|
|
|
Jar jar1 = Jar.init((File) options.valueOf("first-jar"));
|
|
|
|
Jar jar2 = Jar.init((File) options.valueOf("second-jar"));
|
2013-01-22 08:17:48 +00:00
|
|
|
|
|
|
|
log("Creating jar compare");
|
|
|
|
JarComparer visitor1 = new JarComparer(jar1);
|
|
|
|
JarComparer visitor2 = new JarComparer(jar2);
|
|
|
|
visit(new Pair<Jar>(jar1, jar2), new Pair<JarComparer>(visitor1, visitor2), new Pair<String>(jar1.main, jar2.main));
|
|
|
|
|
2013-01-29 06:42:01 +00:00
|
|
|
jarMapping = new JarMapping(visitor1, visitor2, (File) options.valueOf("srg-out"), options.has("compact"), options.has("generate-dupes"));
|
2013-01-22 08:17:48 +00:00
|
|
|
} else if (options.has("srg-in")) {
|
2013-01-23 04:31:41 +00:00
|
|
|
// Load mappings, possibly shaded
|
|
|
|
ShadeRelocationSimulator shadeRelocationSimulator = null;
|
|
|
|
if (options.has("shade-relocation")) {
|
2013-01-24 03:40:26 +00:00
|
|
|
@SuppressWarnings("unchecked")
|
2013-01-23 08:55:14 +00:00
|
|
|
List<String> relocations = (List<String>) options.valuesOf("shade-relocation");
|
2013-01-23 04:31:41 +00:00
|
|
|
shadeRelocationSimulator = new ShadeRelocationSimulator(relocations);
|
|
|
|
|
|
|
|
for (Map.Entry<String, String> entry : shadeRelocationSimulator.relocations.entrySet()) {
|
2013-01-23 08:55:14 +00:00
|
|
|
log("Relocation: " + entry.getKey() + " -> " + entry.getValue());
|
2013-01-23 04:31:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-22 08:17:48 +00:00
|
|
|
log("Loading mappings");
|
2013-01-24 05:27:45 +00:00
|
|
|
BufferedReader reader = new BufferedReader(new FileReader((File) options.valueOf("srg-in")));
|
|
|
|
jarMapping = new JarMapping(reader, shadeRelocationSimulator);
|
2013-01-22 08:17:48 +00:00
|
|
|
} else {
|
|
|
|
System.err.println("No mappings given, first-jar/second-jar or srg-in required");
|
|
|
|
parser.printHelpOn(System.err);
|
|
|
|
return;
|
|
|
|
}
|
2013-02-03 22:47:05 +00:00
|
|
|
log(jarMapping.packages.size() + " packages, " + jarMapping.classes.size() + " classes, " + jarMapping.fields.size() + " fields, " + jarMapping.methods.size() + " methods");
|
2012-12-27 07:27:22 +00:00
|
|
|
|
2013-02-01 03:49:45 +00:00
|
|
|
InheritanceProviders inheritanceProviders = new InheritanceProviders();
|
2013-01-27 21:10:54 +00:00
|
|
|
|
|
|
|
if (options.has("live-remapped")) {
|
|
|
|
inheritanceProviders.add(new RemappedRuntimeInheritanceProvider(ClassLoader.getSystemClassLoader(), !options.has("quiet"), jarMapping));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.has("live")) {
|
|
|
|
inheritanceProviders.add(new RuntimeInheritanceProvider(ClassLoader.getSystemClassLoader(), !options.has("quiet")));
|
|
|
|
}
|
|
|
|
|
2013-01-30 04:31:47 +00:00
|
|
|
if (options.has("read-inheritance")) {
|
|
|
|
InheritanceMap inheritanceMap = new InheritanceMap();
|
|
|
|
|
|
|
|
BufferedReader reader = new BufferedReader(new FileReader((File) options.valueOf("read-inheritance")));
|
2013-01-30 04:56:44 +00:00
|
|
|
|
|
|
|
BiMap<String, String> inverseClassMap = HashBiMap.create(jarMapping.classes).inverse();
|
|
|
|
inheritanceMap.load(reader, inverseClassMap);
|
2013-01-30 04:31:47 +00:00
|
|
|
log("Loaded inheritance map for "+inheritanceMap.inheritanceMap.size()+" classes");
|
|
|
|
|
|
|
|
inheritanceProviders.add(inheritanceMap);
|
|
|
|
}
|
|
|
|
|
2013-01-27 21:10:54 +00:00
|
|
|
|
2013-01-22 04:12:31 +00:00
|
|
|
if (options.has("in-jar")) {
|
2013-01-22 08:17:48 +00:00
|
|
|
if (!options.has("out-jar")) {
|
2013-01-24 03:47:15 +00:00
|
|
|
System.err.println("No output jar given, in-jar requires out-jar");
|
2013-01-22 08:17:48 +00:00
|
|
|
parser.printHelpOn(System.err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-22 04:21:33 +00:00
|
|
|
log("Remapping final jar");
|
2013-01-23 08:55:14 +00:00
|
|
|
Jar jar3 = Jar.init((File) options.valueOf("in-jar"));
|
2013-01-24 03:32:21 +00:00
|
|
|
|
|
|
|
inheritanceProviders.add(new JarInheritanceProvider(jar3));
|
2013-01-24 04:41:43 +00:00
|
|
|
|
2013-01-24 03:32:21 +00:00
|
|
|
JarRemapper jarRemapper = new JarRemapper(jarMapping, inheritanceProviders);
|
|
|
|
jarRemapper.remapJar(jar3, (File) options.valueOf("out-jar"));
|
2013-01-22 03:18:54 +00:00
|
|
|
}
|
2013-01-27 21:10:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
if (options.has("write-inheritance")) {
|
|
|
|
InheritanceMap inheritanceMap = new InheritanceMap();
|
|
|
|
|
|
|
|
inheritanceMap.generate(inheritanceProviders, jarMapping.classes.values());
|
|
|
|
PrintWriter printWriter = new PrintWriter((File) options.valueOf("write-inheritance"));
|
|
|
|
inheritanceMap.save(printWriter);
|
|
|
|
printWriter.close();
|
|
|
|
}
|
2012-11-27 12:02:36 +00:00
|
|
|
}
|
|
|
|
|
2013-01-26 07:27:40 +00:00
|
|
|
private static void log(String message) {
|
2013-01-24 06:24:52 +00:00
|
|
|
if (options != null && !options.has("quiet")) {
|
2013-01-22 04:21:33 +00:00
|
|
|
System.out.println(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-08 23:29:04 +00:00
|
|
|
private static void visit(Pair<Jar> jars, Pair<JarComparer> visitors, Pair<String> classes) throws IOException {
|
2012-11-27 12:02:36 +00:00
|
|
|
JarComparer visitor1 = visitors.first;
|
|
|
|
JarComparer visitor2 = visitors.second;
|
|
|
|
|
|
|
|
ClassReader clazz1 = new ClassReader(jars.first.getClass(classes.first));
|
|
|
|
ClassReader clazz2 = new ClassReader(jars.second.getClass(classes.second));
|
|
|
|
clazz1.accept(visitor1, 0);
|
|
|
|
clazz2.accept(visitor2, 0);
|
|
|
|
|
|
|
|
validate(visitor1, visitor2);
|
|
|
|
|
|
|
|
while (visitor1.iterDepth < visitor1.classes.size()) {
|
|
|
|
String className1 = visitor1.classes.get(visitor1.iterDepth);
|
|
|
|
String className2 = visitor2.classes.get(visitor1.iterDepth);
|
2012-12-08 23:29:04 +00:00
|
|
|
Pair<String> pair = new Pair<String>(className1, className2);
|
2012-11-27 12:02:36 +00:00
|
|
|
visitor1.iterDepth++;
|
|
|
|
visit(jars, visitors, pair);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void validate(JarComparer visitor1, JarComparer visitor2) {
|
|
|
|
if (visitor1.classes.size() != visitor2.classes.size()) {
|
|
|
|
throw new IllegalStateException("classes");
|
|
|
|
}
|
|
|
|
if (visitor1.fields.size() != visitor2.fields.size()) {
|
|
|
|
throw new IllegalStateException("fields");
|
|
|
|
}
|
|
|
|
if (visitor1.methods.size() != visitor2.methods.size()) {
|
|
|
|
throw new IllegalStateException("methods");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|