Advertisement
PaleoCrafter

Untitled

Sep 5th, 2014
286
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 15.96 KB | None | 0 0
  1. package de.mineformers.core.asm.util;
  2.  
  3. import com.google.common.cache.CacheBuilder;
  4. import com.google.common.cache.CacheLoader;
  5. import com.google.common.cache.LoadingCache;
  6. import com.google.common.collect.ImmutableSet;
  7. import com.google.common.collect.Maps;
  8. import com.google.common.collect.Sets;
  9. import cpw.mods.fml.relauncher.ReflectionHelper;
  10. import net.minecraft.launchwrapper.Launch;
  11. import org.objectweb.asm.Type;
  12. import org.objectweb.asm.tree.ClassNode;
  13. import sun.management.MethodInfo;
  14. import sun.reflect.FieldInfo;
  15.  
  16. import java.io.IOException;
  17. import java.lang.reflect.Method;
  18. import java.util.*;
  19. import java.util.concurrent.TimeUnit;
  20.  
  21. import static org.objectweb.asm.Opcodes.*;
  22.  
  23. /**
  24.  * Some information about a class, obtain via {@link ClassInfo#of(org.objectweb.asm.tree.ClassNode)}
  25.  * Licensed under LGPL v3
  26.  *
  27.  * @author diesieben07
  28.  */
  29. public abstract class ClassInfo
  30. {
  31.     private ClassInfo zuper;
  32.  
  33.     // limit subclasses to this package
  34.     ClassInfo()
  35.     {
  36.     }
  37.  
  38.     /**
  39.      * <p>Create a {@code ClassInfo} representing the given class.</p>
  40.      *
  41.      * @param clazz the Class
  42.      * @return a ClassInfo
  43.      */
  44.     public static ClassInfo of(Class<?> clazz)
  45.     {
  46.         return new ClassInfoReflect(clazz);
  47.     }
  48.  
  49.     /**
  50.      * <p>Create a {@code ClassInfo} representing the given ClassNode.</p>
  51.      *
  52.      * @param clazz the ClassNode
  53.      * @return a ClassInfo
  54.      */
  55.     public static ClassInfo of(ClassNode clazz)
  56.     {
  57.         return new ClassInfoASM(clazz);
  58.     }
  59.  
  60.     /**
  61.      * <p>Create a {@code ClassInfo} representing the given Type.</p>
  62.      * <p>This method will try to avoid loading actual classes into the JVM, but will instead use the ASM library
  63.      * to analyze the raw class bytes if possible.</p>
  64.      *
  65.      * @param type a Type representing the class to load, must not be a method type
  66.      * @return a ClassInfo
  67.      */
  68.     public static ClassInfo of(Type type)
  69.     {
  70.         switch (type.getSort())
  71.         {
  72.             case Type.ARRAY:
  73.             case Type.OBJECT:
  74.                 // Type.getClassName incorrectly returns something like "java.lang.Object[][]" instead of "[[Ljava.lang.Object"
  75.                 // so we have to convert the internal name (which is correct) manually
  76.                 return create(ASMUtils.binaryName(type.getInternalName()));
  77.             case Type.METHOD:
  78.                 throw new IllegalArgumentException("Invalid Type!");
  79.             default:
  80.                 // primitives
  81.                 return of(type.getClassName());
  82.         }
  83.     }
  84.  
  85.     /**
  86.      * <p>Create a {@code ClassInfo} representing the given class.</p>
  87.      * <p>This method will try to avoid loading actual classes into the JVM, but will instead use the ASM library
  88.      * to analyze the raw class bytes if possible.</p>
  89.      *
  90.      * @param className the internal or binary name representing the class
  91.      * @return a ClassInfo
  92.      */
  93.     public static ClassInfo of(String className)
  94.     {
  95.         return create(ASMUtils.binaryName(className));
  96.     }
  97.  
  98.     static ClassInfo create(String className)
  99.     {
  100.         switch (className)
  101.         {
  102.             case "boolean":
  103.                 return of(boolean.class);
  104.             case "byte":
  105.                 return of(byte.class);
  106.             case "short":
  107.                 return of(short.class);
  108.             case "int":
  109.                 return of(int.class);
  110.             case "long":
  111.                 return of(long.class);
  112.             case "float":
  113.                 return of(float.class);
  114.             case "double":
  115.                 return of(double.class);
  116.             case "char":
  117.                 return of(char.class);
  118.             default:
  119.                 if (className.indexOf('[') >= 0)
  120.                 {
  121.                     try
  122.                     {
  123.                         // array classes should always be accessible via Class.forName
  124.                         // without loading the element-type class (Object[].class doesn't load Object.class)
  125.                         return forceLoad(className);
  126.                     }
  127.                     catch (ReflectiveOperationException e)
  128.                     {
  129.                         return null;
  130.                     }
  131.                 }
  132.                 else
  133.                 {
  134.                     return ofObject(className);
  135.                 }
  136.         }
  137.     }
  138.  
  139.     private static Method findLoadedClass =
  140.             ReflectionHelper.findMethod(ClassLoader.class, Launch.classLoader, new String[] {"findLoadedClass"}, String.class);
  141.  
  142.     private static ClassInfo ofObject(String className)
  143.     {
  144.         Class<?> clazz;
  145.         findLoadedClass.setAccessible(true);
  146.         // first, try to get the class if it's already loaded
  147.         try
  148.         {
  149.             System.out.println(SevenASMUtils.transformName(className));
  150.             if ((clazz = ((Class<?>) findLoadedClass.invoke(Launch.classLoader, SevenASMUtils.transformName(className)))) != null)
  151.             {
  152.                 return new ClassInfoReflect(clazz);
  153.             }
  154.             else if ((clazz = ((Class<?>) findLoadedClass.invoke(Launch.classLoader, className))) != null)
  155.             {
  156.                 return new ClassInfoReflect(clazz);
  157.             }
  158.             else
  159.             {
  160.                 try
  161.                 {
  162.                     // the class is definitely not loaded, get it's bytes
  163.                     byte[] bytes = Launch.classLoader.getClassBytes(className);
  164.                     // somehow we can't access the class bytes (happens for JDK classes for example)
  165.                     // we try and load the class now
  166.                     if (bytes == null)
  167.                     {
  168.                         return forceLoad(className);
  169.                     }
  170.                     else
  171.                     {
  172.                         // we found the bytes, lets use them
  173.                         return new ClassInfoASM(ASMUtils.getThinClassNode(bytes));
  174.                     }
  175.                 }
  176.                 catch (IOException e)
  177.                 {
  178.                     // something went wrong getting the class bytes. try and load it
  179.                     return forceLoad(className);
  180.                 }
  181.             }
  182.         }
  183.         catch (ReflectiveOperationException e)
  184.         {
  185.             e.printStackTrace();
  186.         }
  187.         return null;
  188.     }
  189.  
  190.     private static ClassInfo forceLoad(String className) throws ReflectiveOperationException
  191.     {
  192.         return of(Class.forName(className));
  193.     }
  194.  
  195.     /**
  196.      * <p>Get all interfaces directly implemented by this class (equivalent to {@link Class#getInterfaces()}.</p>
  197.      *
  198.      * @return the interfaces implemented by this class
  199.      */
  200.     public abstract List<String> interfaces();
  201.  
  202.     /**
  203.      * <p>Get the internal name of the superclass of this class.</p>
  204.      *
  205.      * @return the superclass, or null if this ClassInfo is an interface or represents {@code java/lang/Object}.
  206.      */
  207.     public abstract String superName();
  208.  
  209.     public boolean hasSuper()
  210.     {
  211.         return superName() != null;
  212.     }
  213.  
  214.     /**
  215.      * <p>Get the internal name of this class (e.g. {@code java/lang/Object}.</p>
  216.      *
  217.      * @return the internal name
  218.      */
  219.     public abstract String internalName();
  220.  
  221.     /**
  222.      * <p>Get a {@code ClassInfo} representing the superclass of this class.</p>
  223.      *
  224.      * @return the superclass, or null if this class has no superclass (see {@link #superName()}
  225.      */
  226.     public ClassInfo superclass()
  227.     {
  228.         if (zuper != null)
  229.         {
  230.             return zuper;
  231.         }
  232.         if (superName() == null)
  233.         {
  234.             return null;
  235.         }
  236.         return (zuper = of(superName()));
  237.     }
  238.  
  239.     /**
  240.      * <p>Determine if the given class can be safely casted to this class (equivalent to {@link java.lang.Class#isAssignableFrom(Class)}.</p>
  241.      * <p>Like {@link #of(String)} this method will try to avoid loading actual classes.</p>
  242.      *
  243.      * @param child the class to check for
  244.      * @return true if the given class can be casted to this class
  245.      */
  246.     public final boolean isAssignableFrom(ClassInfo child)
  247.     {
  248.         return child.callRightAssignableFrom(this);
  249.     }
  250.  
  251.     boolean callRightAssignableFrom(ClassInfo parent)
  252.     {
  253.         return parent.isAssignableFromNormal(this);
  254.     }
  255.  
  256.     boolean isAssignableFromNormal(ClassInfo child)
  257.     {
  258.         // some cheap tests first
  259.         String childName = child.internalName();
  260.         String myName = internalName();
  261.  
  262.         if (childName.equals("java/lang/Object"))
  263.         {
  264.             // Object is only assignable to itself
  265.             return myName.equals("java/lang/Object");
  266.         }
  267.         if (myName.equals("java/lang/Object") // everything is assignable to Object
  268.                 || childName.equals(myName) // we are the same
  269.                 || myName.equals(child.superName()) // we are the superclass of child
  270.                 || child.interfaces().contains(myName))
  271.         { // we are an interface that child implements
  272.             return true;
  273.         }
  274.  
  275.         // if we are a class no interface can be cast to us
  276.         if (!isInterface() && child.isInterface())
  277.         {
  278.             return false;
  279.         }
  280.         // need to compute supers now
  281.         return child.getSupers().contains(myName);
  282.     }
  283.  
  284.     boolean isAssignableFromReflect(ClassInfoReflect child)
  285.     {
  286.         return isAssignableFromNormal(child);
  287.     }
  288.  
  289.     /**
  290.      * <p>Get all superclasses in the hierarchy chain of this class as well as all interfaces this class
  291.      * implements directly or indirectly.</p>
  292.      * <p>In other words return all classes that this class can be safely casted to.</p>
  293.      *
  294.      * @return an immutable Set containing all superclasses and interfaces
  295.      */
  296.     public Set<String> getSupers()
  297.     {
  298.         return getSupers(this);
  299.     }
  300.  
  301.     public static Map<String, Set<String>> superCache = Maps.newHashMap();
  302.  
  303.     public static Set<String> getSupers(ClassInfo classInfo)
  304.     {
  305.         // grab a local var in case of concurrency
  306.         Map<String, Set<String>> superCacheLocal = superCache;
  307.         if (superCacheLocal != null)
  308.         {
  309.             Set<String> supers = superCacheLocal.get(classInfo.internalName());
  310.             if (supers == null)
  311.             {
  312.                 superCacheLocal.put(classInfo.internalName(), (supers = buildSupers(classInfo)));
  313.             }
  314.             return supers;
  315.         }
  316.         else
  317.         {
  318.             return buildSupers(classInfo);
  319.         }
  320.     }
  321.  
  322.     private static Set<String> buildSupers(ClassInfo classInfo)
  323.     {
  324.         Set<String> set = Sets.newHashSet();
  325.         String superName = classInfo.superName();
  326.         if (superName != null)
  327.         {
  328.             set.add(superName);
  329.             set.addAll(classInfo.superclass().getSupers());
  330.         }
  331.         for (String iface : classInfo.interfaces())
  332.         {
  333.             if (set.add(iface))
  334.             {
  335.                 set.addAll(ClassInfo.of(iface).getSupers());
  336.             }
  337.         }
  338.         // use immutable set to reduce memory footprint and potentially increase performance
  339.         // cannot use builder because we need the boolean return from set.add
  340.         return ImmutableSet.copyOf(set);
  341.     }
  342.  
  343.     /**
  344.      * <p>Get the number of dimensions of this array class, or 0 if this ClassInfo does not represent an array class.</p>
  345.      *
  346.      * @return the number of dimensions
  347.      */
  348.     public abstract int getDimensions();
  349.  
  350.     /**
  351.      * <p>Determine if this class is an array class (equivalent to {@link Class#isArray()}</p>
  352.      *
  353.      * @return true if this class is an array class
  354.      */
  355.     public boolean isArray()
  356.     {
  357.         return getDimensions() > 0;
  358.     }
  359.  
  360.     /**
  361.      * <p>Get the component type of this array class.</p>
  362.      * <p>The component type of {@code int[][]} is {@code int[]}.</p>
  363.      *
  364.      * @return the component type
  365.      * @throws java.lang.IllegalStateException if this class is not an array
  366.      */
  367.     public abstract Type getComponentType();
  368.  
  369.     /**
  370.      * <p>Get the root component type of this array class.</p>
  371.      * <p>The root component type of {@code int[][]} is {@code int}.</p>
  372.      *
  373.      * @return the root component type
  374.      * @throws java.lang.IllegalStateException if this class is not an array
  375.      */
  376.     public Type getRootComponentType()
  377.     {
  378.         Type t = getComponentType();
  379.         if (t.getSort() == Type.ARRAY)
  380.         {
  381.             return t.getElementType();
  382.         }
  383.         else
  384.         {
  385.             return t;
  386.         }
  387.     }
  388.  
  389.     /**
  390.      * <p>Determine if this class is an interface.</p>
  391.      *
  392.      * @return true if this class is an interface
  393.      */
  394.     public boolean isInterface()
  395.     {
  396.         return hasModifier(ACC_INTERFACE);
  397.     }
  398.  
  399.     /**
  400.      * <p>Determine if this class is abstract</p>
  401.      *
  402.      * @return true if this class is abstract
  403.      */
  404.     public boolean isAbstract()
  405.     {
  406.         return hasModifier(ACC_ABSTRACT);
  407.     }
  408.  
  409.     /**
  410.      * <p>Determine if this class is an annotation.</p>
  411.      *
  412.      * @return true if this class is an annotation
  413.      */
  414.     public boolean isAnnotation()
  415.     {
  416.         return hasModifier(ACC_ANNOTATION);
  417.     }
  418.  
  419.     /**
  420.      * <p>Determine if this ClassInfo represents an enum class (equivalent to {@link Class#isEnum()}.</p>
  421.      * <p>Note: Like the JDK method this method will return false for the classes generated for specialized enum constants.
  422.      * Use {@code hasModifier(ACC_ENUM)} to include those explicitly.</p>
  423.      *
  424.      * @return true if this ClassInfo represents an enum class
  425.      */
  426.     public boolean isEnum()
  427.     {
  428.         return hasModifier(ACC_ENUM) && superName().equals("java/lang/Enum");
  429.     }
  430.  
  431.     /**
  432.      * <p>Get all Java modifiers present on this element.</p>
  433.      *
  434.      * @return the modifiers
  435.      * @see java.lang.reflect.Modifier
  436.      */
  437.     public abstract int modifiers();
  438.  
  439.     /**
  440.      * <p>Determine if the given Java language modifier is set on this element.</p>
  441.      *
  442.      * @param mod the modifier to check
  443.      * @return true if the given modifier is set
  444.      * @see java.lang.reflect.Modifier
  445.      */
  446.     public boolean hasModifier(int mod)
  447.     {
  448.         return (modifiers() & mod) == mod;
  449.     }
  450.  
  451.     /**
  452.      * <p>Determine if this element has public visibility.</p>
  453.      *
  454.      * @return true if this element has public visibility
  455.      */
  456.     public boolean isPublic()
  457.     {
  458.         return hasModifier(ACC_PUBLIC);
  459.     }
  460.  
  461.     /**
  462.      * <p>Determine if this element has protected visibility.</p>
  463.      *
  464.      * @return true if this element has protected visibility
  465.      */
  466.     public boolean isProtected()
  467.     {
  468.         return hasModifier(ACC_PROTECTED);
  469.     }
  470.  
  471.     /**
  472.      * <p>Determine if this element has private visibility.</p>
  473.      *
  474.      * @return true if this element has private visibility
  475.      */
  476.     public boolean isPrivate()
  477.     {
  478.         return hasModifier(ACC_PRIVATE);
  479.     }
  480.  
  481.     /**
  482.      * <p>Determine if this element has package-private (default) visibility.</p>
  483.      *
  484.      * @return true if this element has package-private (default) visibility
  485.      */
  486.     public boolean isPackagePrivate()
  487.     {
  488.         return !isPrivate() && !isPublic() && !isProtected();
  489.     }
  490.  
  491.     /**
  492.      * <p>Determine if this element is final.</p>
  493.      *
  494.      * @return true if this element is final
  495.      */
  496.     public boolean isFinal()
  497.     {
  498.         return hasModifier(ACC_FINAL);
  499.     }
  500.  
  501.     /**
  502.      * <p>Determine if this element is a synthetic element generated by the compiler.</p>
  503.      *
  504.      * @return true if this element is synthetic
  505.      */
  506.     public boolean isSynthetic()
  507.     {
  508.         return hasModifier(ACC_SYNTHETIC);
  509.     }
  510.  
  511.     @Override
  512.     public boolean equals(Object o)
  513.     {
  514.         return this == o || o instanceof ClassInfo && internalName().equals(((ClassInfo) o).internalName());
  515.  
  516.     }
  517.  
  518.     @Override
  519.     public int hashCode()
  520.     {
  521.         return internalName().hashCode();
  522.     }
  523. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement