Created
February 6, 2019 05:50
-
-
Save tterrag1098/10fdc6c14814aa85a2851736550c01e8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.enderio.core.common.transform; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URL; | |
import java.net.URLClassLoader; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.apache.commons.io.IOUtils; | |
import org.apache.logging.log4j.LogManager; | |
import org.apache.logging.log4j.Logger; | |
import org.objectweb.asm.ClassReader; | |
import org.objectweb.asm.ClassWriter; | |
import org.objectweb.asm.Opcodes; | |
import org.objectweb.asm.tree.ClassNode; | |
import net.minecraft.launchwrapper.Launch; | |
import net.minecraftforge.fml.common.asm.transformers.DeobfuscationTransformer; | |
import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; | |
public class NoClassloadClassWriter extends ClassWriter { | |
private static final Logger logger = LogManager.getLogger(); | |
private static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("legacy.debugClassLoading", "false")); | |
private static final String OBJECT = "java/lang/Object"; | |
private static final Map<String, ClassNode> nodeCache = new HashMap<>(); | |
public NoClassloadClassWriter(int flags) { | |
super(flags); | |
} | |
@Override | |
protected String getCommonSuperClass(String type1, String type2) { | |
String res = _getCommonSuperClass(type1, type2); | |
if (DEBUG) logger.info("Common supertype of {} and {}: {}", type1, type2, res); | |
return res; | |
} | |
protected String _getCommonSuperClass(String type1, String type2) { | |
if (type1.equals(OBJECT) || type2.equals(OBJECT)) { // Short circuit, don't bother loading Object from disk or cache | |
return OBJECT; | |
} | |
ClassNode class1 = getClassNode(type1); | |
ClassNode class2 = getClassNode(type2); | |
if (class1 == null || class1.superName == null || class2 == null || class2.superName == null) { | |
if (DEBUG) logger.info("Input was Object or null"); | |
return OBJECT; | |
} | |
if (class1.superName.equals(type2)) { | |
if (DEBUG) logger.info("type1 super == type2"); | |
return type2; | |
} else if (class2.superName.equals(type1)) { | |
if (DEBUG) logger.info("type2 super == type1"); | |
return type1; | |
} else if ((class1.access & Opcodes.ACC_INTERFACE) != 0 || (class2.access & Opcodes.ACC_INTERFACE) != 0) { | |
if (DEBUG) logger.info("Input was interface"); | |
return OBJECT; | |
} | |
if (DEBUG) logger.info("Walking hierarchy"); | |
do { | |
class1 = getClassNode(class1.superName); | |
if (class1 == null || class1.superName == null) { | |
if (DEBUG) logger.info("Reached object or null, recursing with {} and {}", type1, class2.superName); | |
return _getCommonSuperClass(type1, class2.superName); | |
} | |
if (DEBUG) logger.info("Comparing {} to {}", class2.superName, class1.name); | |
} while (!class2.superName.equals(class1.name)); | |
return type1; | |
} | |
private ClassNode getClassNode(String name) { | |
ClassNode cn = nodeCache.get(name); | |
if (cn != null) { | |
return cn; | |
} | |
cn = new ClassNode(); | |
ClassReader cr; | |
try { | |
cr = new ClassReader(getClassBytes(name)); | |
} catch (ClassNotFoundException e) { | |
e.printStackTrace(); | |
return null; | |
} catch (IOException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
cr.accept(cn, 0); | |
nodeCache.put(name, cn); | |
return cn; | |
} | |
private static final DeobfuscationTransformer deobf = new DeobfuscationTransformer(); | |
/* | |
* Code from Mixin: https://github.com/SpongePowered/Mixin/ | |
* | |
* Licensed under MIT: | |
* https://github.com/SpongePowered/Mixin/blob/master/LICENSE.txt | |
* | |
* Modified to simplify for pure forge environment. | |
*/ | |
public byte[] getClassBytes(String className) throws ClassNotFoundException, IOException { | |
String name = FMLDeobfuscatingRemapper.INSTANCE.unmap(className); | |
String transformedName = className.replace('/', '.'); | |
byte[] classBytes = this.getClassBytes(name, transformedName); | |
classBytes = deobf.transform(className, name, classBytes); | |
if (classBytes == null) { | |
throw new ClassNotFoundException(String.format("The specified class '%s' was not found", transformedName)); | |
} | |
return classBytes; | |
} | |
private byte[] getClassBytes(String name, String transformedName) throws IOException { | |
byte[] classBytes = getClassBytes(name, transformedName, Launch.classLoader); | |
if (classBytes == null) { | |
classBytes = getClassBytes(name, transformedName, (URLClassLoader) Launch.class.getClassLoader()); | |
} | |
return classBytes; | |
} | |
private byte[] getClassBytes(String name, String transformedName, URLClassLoader classLoader) throws IOException { | |
final String resourcePath = name.replace('.', '/').concat(".class"); | |
final String deobfResourcePath = transformedName.replace('.', '/').concat(".class"); | |
if (DEBUG) logger.info("Attempting to load class {} with classloader {}", deobfResourcePath, classLoader); | |
URL resource = classLoader.getResource(resourcePath); | |
if (resource == null && !resourcePath.equals(deobfResourcePath)) { | |
resource = classLoader.getResource(deobfResourcePath); | |
} | |
if (resource == null) { | |
if (DEBUG) logger.info("No resource found"); | |
return null; | |
} | |
try (InputStream classStream = resource.openStream()) { | |
return IOUtils.toByteArray(classStream); | |
} catch (Exception ex) { | |
ex.printStackTrace(); | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment