From 297b9f5a33cb49a9c3d1f619ade6dce054d36954 Mon Sep 17 00:00:00 2001 From: Agaricus Date: Wed, 23 Jan 2013 00:23:38 -0800 Subject: [PATCH] Add runtime class inheritance remapping If -l is specified, will now traverse the runtime class hierarchy in tryClimb to determine the method/symbol from the parent to remap, in addition to the classes in the jar. This can be used to remap plugins that call into external classes. For example, with a Bukkit plugin: java -cp ../jars/craftbukkit-1.4.7-R0.1.jar:target/SpecialSource-1.1-SNAPSHOT.jar net.md_5.specialsource.SpecialSource --shade-relocation net.minecraft.server=net.minecraft.server.v1_4_R1 --shade-relocation org.bouncycastle=net.minecraft.v1_4_R1.org.bouncycastle --srg-in ../jars/1.4.7/cb2obf.csrg --in ../IncompatiblePlugin/IncompatiblePlugin-01.jar --out /tmp/bp/out.jar using: https://bitbucket.org/agaricusb/incompatibleplugin/downloads/IncompatiblePlugin-01.jar https://github.com/agaricusb/IncompatiblePlugin/tree/6d75d944962fea6033d7cec370af5a83e248312b agaricus/plugins/IncompatiblePlugin/SamplePosCommand.java will be remapped: worldServer.q(...) from worldServer.getTileEntity(). World provides getTileEntity and is remapped in cb2obf; but WorldServer subclasses World and overrides getTileEntiy.. this information is not available in the plugin itself and would not be remapped alone. By adding the CB jar to the classpath and enabling runtime inheritance, the call is correctly remapped. --- .../specialsource/JarInheritanceProvider.java | 12 +++++ .../net/md_5/specialsource/JarRemapper.java | 27 ++++++---- .../RuntimeInheritanceProvider.java | 49 +++++++++++++++++++ .../net/md_5/specialsource/SpecialSource.java | 4 +- 4 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 src/main/java/net/md_5/specialsource/RuntimeInheritanceProvider.java diff --git a/src/main/java/net/md_5/specialsource/JarInheritanceProvider.java b/src/main/java/net/md_5/specialsource/JarInheritanceProvider.java index a30f370..d249d28 100644 --- a/src/main/java/net/md_5/specialsource/JarInheritanceProvider.java +++ b/src/main/java/net/md_5/specialsource/JarInheritanceProvider.java @@ -5,6 +5,9 @@ import org.objectweb.asm.tree.ClassNode; import java.util.ArrayList; import java.util.List; +/** + * Lookup inheritance from a class given a jar + */ public class JarInheritanceProvider implements IInheritanceProvider { private final Jar self; @@ -14,14 +17,23 @@ public class JarInheritanceProvider implements IInheritanceProvider { @SuppressWarnings("unchecked") // Saddens me to see ASM strip vital info like that public List getParents(String owner) { + System.out.println("jar: owner "+owner); List parents = new ArrayList(); ClassNode node = self.getNode(owner); if (node != null) { for (String iface : (List) node.interfaces) { + System.out.println("jar: add iface="+iface); parents.add(iface); } + System.out.println("jar: add super="+node.superName); parents.add(node.superName); + } else { + System.out.println("jar: nothing for "+owner); } return parents; } + + public String toString() { + return getClass().getSimpleName()+"("+self.file.getName()+")"; + } } diff --git a/src/main/java/net/md_5/specialsource/JarRemapper.java b/src/main/java/net/md_5/specialsource/JarRemapper.java index f44cacc..ab48cd4 100644 --- a/src/main/java/net/md_5/specialsource/JarRemapper.java +++ b/src/main/java/net/md_5/specialsource/JarRemapper.java @@ -33,6 +33,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Map; @@ -47,12 +48,12 @@ import org.objectweb.asm.tree.ClassNode; public class JarRemapper extends Remapper { private static final int CLASS_LEN = ".class".length(); - private final IInheritanceProvider inheritanceProvider; + private final List inheritanceProviders; private final JarMapping jarMapping; - private JarRemapper(JarMapping jarMapping, IInheritanceProvider inheritanceProvider) throws IOException { + private JarRemapper(JarMapping jarMapping, List inheritanceProviders) throws IOException { this.jarMapping = jarMapping; - this.inheritanceProvider = inheritanceProvider; + this.inheritanceProviders = inheritanceProviders; } @Override @@ -96,10 +97,12 @@ public class JarRemapper extends Remapper { String mapped = map.get(key); if (mapped == null) { - for (String parent : inheritanceProvider.getParents(owner)) { - mapped = tryClimb(map, type, parent, name); - if (mapped != null) { - return mapped; + for (IInheritanceProvider inheritanceProvider : inheritanceProviders) { + for (String parent : inheritanceProvider.getParents(owner)) { + mapped = tryClimb(map, type, parent, name); + if (mapped != null) { + return mapped; + } } } } @@ -112,11 +115,15 @@ public class JarRemapper extends Remapper { return mapped == null ? name : mapped; } - public static void renameJar(Jar jar, File target, JarMapping jarMapping) throws IOException { + public static void renameJar(Jar jar, File target, JarMapping jarMapping, boolean live) throws IOException { JarOutputStream out = new JarOutputStream(new FileOutputStream(target)); try { - JarInheritanceProvider jarInheritanceProvider = new JarInheritanceProvider(jar); - JarRemapper self = new JarRemapper(jarMapping, jarInheritanceProvider); + List inheritanceProviders = new ArrayList(); + inheritanceProviders.add(new JarInheritanceProvider(jar)); + if (live) { + inheritanceProviders.add(new RuntimeInheritanceProvider()); + } + JarRemapper self = new JarRemapper(jarMapping, inheritanceProviders); if (jar == null) { return; } diff --git a/src/main/java/net/md_5/specialsource/RuntimeInheritanceProvider.java b/src/main/java/net/md_5/specialsource/RuntimeInheritanceProvider.java new file mode 100644 index 0000000..521cad7 --- /dev/null +++ b/src/main/java/net/md_5/specialsource/RuntimeInheritanceProvider.java @@ -0,0 +1,49 @@ +package net.md_5.specialsource; + +import java.util.List; +import java.util.ArrayList; + +/** + * Lookup class inheritance from classes loaded at runtime + */ +public class RuntimeInheritanceProvider implements IInheritanceProvider { + // TODO: option to transform through a jarRemapper at runtime + + public List getParents(String internalClassName) { + List parents = new ArrayList(); + String sourceClassName = toSourceName(internalClassName); + Class clazz; + try { + clazz = ClassLoader.getSystemClassLoader().loadClass(sourceClassName); // load class without initializing + //clazz = Class.forName(toSourceName(sourceClassName)); // runs static initializers - avoid! + } catch (Throwable t) { + System.out.println("RuntimeInheritanceProvider failed: "+t); + return parents; + } + + for (Class iface : clazz.getInterfaces()) { + parents.add(toInternalName(iface.getName())); + } + + Class superClass = clazz.getSuperclass(); + if (superClass != null) { + parents.add(toInternalName(superClass.getName())); + } + + return parents; + } + + // Convert class name from internal name to source name + public String toSourceName(String className) { + return className.replace('/', '.'); + } + + // .. and vice versa + public String toInternalName(String className) { + return className.replace('.', '/'); + } + + public String toString() { + return this.getClass().getSimpleName(); + } +} diff --git a/src/main/java/net/md_5/specialsource/SpecialSource.java b/src/main/java/net/md_5/specialsource/SpecialSource.java index 3923c00..0edc33a 100644 --- a/src/main/java/net/md_5/specialsource/SpecialSource.java +++ b/src/main/java/net/md_5/specialsource/SpecialSource.java @@ -76,6 +76,8 @@ public class SpecialSource { .withRequiredArg() .withValuesSeparatedBy(','); + acceptsAll(asList("l", "live"), "Enable runtime inheritance lookup"); + acceptsAll(asList("q", "quiet"), "Quiet mode"); acceptsAll(asList("c", "compact"), "Output mapping file in compact format"); } @@ -150,7 +152,7 @@ public class SpecialSource { log("Remapping final jar"); Jar jar3 = Jar.init((File)options.valueOf("in-jar")); - JarRemapper.renameJar(jar3, (File)options.valueOf("out-jar"), jarMapping); + JarRemapper.renameJar(jar3, (File)options.valueOf("out-jar"), jarMapping, options.has("live")); } }