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
6d75d94496
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.
This commit is contained in:
parent
fa2089271a
commit
297b9f5a33
@ -5,6 +5,9 @@ import org.objectweb.asm.tree.ClassNode;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup inheritance from a class given a jar
|
||||||
|
*/
|
||||||
public class JarInheritanceProvider implements IInheritanceProvider {
|
public class JarInheritanceProvider implements IInheritanceProvider {
|
||||||
private final Jar self;
|
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
|
@SuppressWarnings("unchecked") // Saddens me to see ASM strip vital info like that
|
||||||
public List<String> getParents(String owner) {
|
public List<String> getParents(String owner) {
|
||||||
|
System.out.println("jar: owner "+owner);
|
||||||
List<String> parents = new ArrayList<String>();
|
List<String> parents = new ArrayList<String>();
|
||||||
ClassNode node = self.getNode(owner);
|
ClassNode node = self.getNode(owner);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
for (String iface : (List<String>) node.interfaces) {
|
for (String iface : (List<String>) node.interfaces) {
|
||||||
|
System.out.println("jar: add iface="+iface);
|
||||||
parents.add(iface);
|
parents.add(iface);
|
||||||
}
|
}
|
||||||
|
System.out.println("jar: add super="+node.superName);
|
||||||
parents.add(node.superName);
|
parents.add(node.superName);
|
||||||
|
} else {
|
||||||
|
System.out.println("jar: nothing for "+owner);
|
||||||
}
|
}
|
||||||
return parents;
|
return parents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName()+"("+self.file.getName()+")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ 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.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -47,12 +48,12 @@ import org.objectweb.asm.tree.ClassNode;
|
|||||||
public class JarRemapper extends Remapper {
|
public class JarRemapper extends Remapper {
|
||||||
|
|
||||||
private static final int CLASS_LEN = ".class".length();
|
private static final int CLASS_LEN = ".class".length();
|
||||||
private final IInheritanceProvider inheritanceProvider;
|
private final List<IInheritanceProvider> inheritanceProviders;
|
||||||
private final JarMapping jarMapping;
|
private final JarMapping jarMapping;
|
||||||
|
|
||||||
private JarRemapper(JarMapping jarMapping, IInheritanceProvider inheritanceProvider) throws IOException {
|
private JarRemapper(JarMapping jarMapping, List<IInheritanceProvider> inheritanceProviders) throws IOException {
|
||||||
this.jarMapping = jarMapping;
|
this.jarMapping = jarMapping;
|
||||||
this.inheritanceProvider = inheritanceProvider;
|
this.inheritanceProviders = inheritanceProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -96,10 +97,12 @@ public class JarRemapper extends Remapper {
|
|||||||
|
|
||||||
String mapped = map.get(key);
|
String mapped = map.get(key);
|
||||||
if (mapped == null) {
|
if (mapped == null) {
|
||||||
for (String parent : inheritanceProvider.getParents(owner)) {
|
for (IInheritanceProvider inheritanceProvider : inheritanceProviders) {
|
||||||
mapped = tryClimb(map, type, parent, name);
|
for (String parent : inheritanceProvider.getParents(owner)) {
|
||||||
if (mapped != null) {
|
mapped = tryClimb(map, type, parent, name);
|
||||||
return mapped;
|
if (mapped != null) {
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,11 +115,15 @@ public class JarRemapper extends Remapper {
|
|||||||
return mapped == null ? name : mapped;
|
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));
|
JarOutputStream out = new JarOutputStream(new FileOutputStream(target));
|
||||||
try {
|
try {
|
||||||
JarInheritanceProvider jarInheritanceProvider = new JarInheritanceProvider(jar);
|
List<IInheritanceProvider> inheritanceProviders = new ArrayList<IInheritanceProvider>();
|
||||||
JarRemapper self = new JarRemapper(jarMapping, jarInheritanceProvider);
|
inheritanceProviders.add(new JarInheritanceProvider(jar));
|
||||||
|
if (live) {
|
||||||
|
inheritanceProviders.add(new RuntimeInheritanceProvider());
|
||||||
|
}
|
||||||
|
JarRemapper self = new JarRemapper(jarMapping, inheritanceProviders);
|
||||||
if (jar == null) {
|
if (jar == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -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<String> getParents(String internalClassName) {
|
||||||
|
List<String> parents = new ArrayList<String>();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
@ -76,6 +76,8 @@ public class SpecialSource {
|
|||||||
.withRequiredArg()
|
.withRequiredArg()
|
||||||
.withValuesSeparatedBy(',');
|
.withValuesSeparatedBy(',');
|
||||||
|
|
||||||
|
acceptsAll(asList("l", "live"), "Enable runtime inheritance lookup");
|
||||||
|
|
||||||
acceptsAll(asList("q", "quiet"), "Quiet mode");
|
acceptsAll(asList("q", "quiet"), "Quiet mode");
|
||||||
acceptsAll(asList("c", "compact"), "Output mapping file in compact format");
|
acceptsAll(asList("c", "compact"), "Output mapping file in compact format");
|
||||||
}
|
}
|
||||||
@ -150,7 +152,7 @@ public class SpecialSource {
|
|||||||
|
|
||||||
log("Remapping final jar");
|
log("Remapping final jar");
|
||||||
Jar jar3 = Jar.init((File)options.valueOf("in-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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user