2013-07-16 17:58:02 +10:00
package codechicken.lib.asm ;
2013-07-23 16:04:41 +10:00
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper ;
2013-07-16 17:58:02 +10:00
import net.minecraft.launchwrapper.IClassTransformer ;
import net.minecraft.launchwrapper.LaunchClassLoader ;
2014-04-09 13:38:05 +10:00
import org.objectweb.asm.tree.ClassNode ;
import java.util.ArrayList ;
import java.util.HashMap ;
import java.util.HashSet ;
2013-07-16 17:58:02 +10:00
/**
* This is added as a class transformer if CodeChickenCore is installed. Adding it as a class transformer will speed evaluation up slightly by automatically caching superclasses when they are first loaded.
*/
public class ClassHeirachyManager implements IClassTransformer
{
2013-07-29 22:53:29 +10:00
public static class SuperCache
{
String superclass ;
public HashSet < String > parents = new HashSet < String > ();
private boolean flattened ;
public void add ( String parent )
{
parents . add ( parent );
}
public void flatten ()
{
if ( flattened )
return ;
for ( String s : new ArrayList < String > ( parents ))
{
SuperCache c = declareClass ( s );
if ( c != null )
{
c . flatten ();
parents . addAll ( c . parents );
}
}
flattened = true ;
}
}
public static HashMap < String , SuperCache > superclasses = new HashMap < String , SuperCache > ();
2013-07-16 17:58:02 +10:00
private static LaunchClassLoader cl = ( LaunchClassLoader ) ClassHeirachyManager . class . getClassLoader ();
2013-07-17 18:18:57 +10:00
static
{
cl . addTransformerExclusion ( "codechicken.lib.asm" );
}
2013-07-23 16:04:41 +10:00
public static String toKey ( String name )
{
if ( ObfMapping . obfuscated )
2013-07-29 22:53:29 +10:00
name = FMLDeobfuscatingRemapper . INSTANCE . map ( name . replace ( '.' , '/' )). replace ( '/' , '.' );
return name ;
}
public static String unKey ( String name )
{
if ( ObfMapping . obfuscated )
name = FMLDeobfuscatingRemapper . INSTANCE . unmap ( name . replace ( '.' , '/' )). replace ( '/' , '.' );
2013-07-23 16:04:41 +10:00
return name ;
}
2013-07-16 17:58:02 +10:00
/**
2013-07-23 16:04:41 +10:00
* @param name The class in question
2013-07-16 17:58:02 +10:00
* @param superclass The class being extended
2014-04-09 13:38:05 +10:00
* @return true if clazz extends, either directly or indirectly, superclass.
2013-07-16 17:58:02 +10:00
*/
2013-07-29 22:53:29 +10:00
public static boolean classExtends ( String name , String superclass )
2013-07-16 17:58:02 +10:00
{
2013-07-23 16:04:41 +10:00
name = toKey ( name );
2013-07-29 22:53:29 +10:00
superclass = toKey ( superclass );
2013-07-23 16:04:41 +10:00
2013-07-29 22:53:29 +10:00
if ( name . equals ( superclass ))
2013-07-16 17:58:02 +10:00
return true ;
2013-07-29 22:53:29 +10:00
SuperCache cache = declareClass ( name );
if ( cache == null ) //just can't handle this
2013-07-16 17:58:02 +10:00
return false ;
2013-07-29 22:53:29 +10:00
cache . flatten ();
return cache . parents . contains ( superclass );
2013-07-16 17:58:02 +10:00
}
2013-07-29 22:53:29 +10:00
private static SuperCache declareClass ( String name )
2013-07-16 17:58:02 +10:00
{
2013-07-23 16:04:41 +10:00
name = toKey ( name );
2013-07-29 22:53:29 +10:00
SuperCache cache = superclasses . get ( name );
if ( cache != null )
return cache ;
2013-07-23 16:04:41 +10:00
2013-07-16 17:58:02 +10:00
try
{
2013-07-29 22:53:29 +10:00
byte [] bytes = cl . getClassBytes ( unKey ( name ));
if ( bytes != null )
cache = declareASM ( bytes );
}
catch ( Exception e )
{
}
if ( cache != null )
return cache ;
try
{
cache = declareReflection ( name );
2013-07-16 17:58:02 +10:00
}
catch ( ClassNotFoundException e )
{
}
2013-07-29 22:53:29 +10:00
return cache ;
}
private static SuperCache declareReflection ( String name ) throws ClassNotFoundException
{
Class <?> aclass = Class . forName ( name );
SuperCache cache = getOrCreateCache ( name );
if ( aclass . isInterface ())
cache . superclass = "java.lang.Object" ;
else if ( name . equals ( "java.lang.Object" ))
return cache ;
else
cache . superclass = toKey ( aclass . getSuperclass (). getName ());
cache . add ( cache . superclass );
for ( Class <?> iclass : aclass . getInterfaces ())
cache . add ( toKey ( iclass . getName ()));
return cache ;
}
private static SuperCache declareASM ( byte [] bytes )
{
ClassNode node = ASMHelper . createClassNode ( bytes );
String name = toKey ( node . name );
SuperCache cache = getOrCreateCache ( name );
cache . superclass = toKey ( node . superName . replace ( '/' , '.' ));
cache . add ( cache . superclass );
for ( String iclass : node . interfaces )
cache . add ( toKey ( iclass . replace ( '/' , '.' )));
return cache ;
2013-07-16 17:58:02 +10:00
}
@Override
public byte [] transform ( String name , String tname , byte [] bytes )
{
2013-07-29 22:53:29 +10:00
if ( bytes == null )
return null ;
if ( ! superclasses . containsKey ( tname ))
declareASM ( bytes );
2013-07-16 17:58:02 +10:00
return bytes ;
}
2013-07-29 22:53:29 +10:00
public static SuperCache getOrCreateCache ( String name )
2013-07-16 17:58:02 +10:00
{
2013-07-29 22:53:29 +10:00
SuperCache cache = superclasses . get ( name );
if ( cache == null )
superclasses . put ( name , cache = new SuperCache ());
return cache ;
2013-07-16 17:58:02 +10:00
}
2013-07-23 16:04:41 +10:00
public static String getSuperClass ( String name , boolean runtime )
2013-07-16 17:58:02 +10:00
{
2013-07-23 16:04:41 +10:00
name = toKey ( name );
2013-07-29 22:53:29 +10:00
SuperCache cache = declareClass ( name );
if ( cache == null )
2013-07-16 17:58:02 +10:00
return "java.lang.Object" ;
2013-07-23 16:04:41 +10:00
2013-07-29 22:53:29 +10:00
cache . flatten ();
String s = cache . superclass ;
if ( ! runtime )
s = FMLDeobfuscatingRemapper . INSTANCE . unmap ( s );
return s ;
2013-07-16 17:58:02 +10:00
}
}