All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yworks.yguard.obf.Cl Maven / Gradle / Ivy

/**
 * YGuard -- an obfuscation library for Java(TM) classfiles.
 *
 * Original Copyright (c) 1999 Mark Welsh ([email protected])
 * Modifications Copyright (c) 2002 yWorks GmbH ([email protected])
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * The author may be contacted at [email protected] 
 *
 * Java and all Java-based marks are trademarks or registered 
 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
 */
package com.yworks.yguard.obf;

import com.yworks.yguard.Conversion;

import java.lang.reflect.*;
import java.util.*;
import com.yworks.yguard.obf.classfile.*;

/**
 * Tree item representing a class or interface.
 *
 * @author      Mark Welsh
 */
public class Cl extends PkCl implements NameListUp, NameListDown
{
  private boolean sourceFileMappingSet;

  public Set getAttributesToKeep() {
        return attributesToKeep;
    }

    public interface ClassResolver extends AutoCloseable {
      Class resolve(String className) throws ClassNotFoundException;
    }
    
    private static final class DefaultClassResolver implements ClassResolver{
      public Class resolve(String className) throws ClassNotFoundException
      {
        return Class.forName(className, false, this.getClass().getClassLoader());
      }

      @Override
      public void close() throws Exception {
      }
    }
    
    private static boolean pedantic = false;
  
    // Constants -------------------------------------------------------------
    private static ClassResolver resolver;

    static {
      resolver = new DefaultClassResolver();
    }
    
    public static ClassResolver getClassResolver(){
      return resolver;
    }
    
    public static void setPedantic(boolean val){
      pedantic = val;
    }
    
    public static void setClassResolver(ClassResolver res){
      if (res != null){
        resolver = res;
      } else {
        resolver = new DefaultClassResolver();
      }
    }

    // Fields ----------------------------------------------------------------
    private Hashtable mds = new Hashtable(); // Owns a list of methods
    private Hashtable fds = new Hashtable(); // Owns a list of fields
    private boolean isResolved = false; // Has the class been resolved already?
    private boolean isScanned = false; // Has the class been scanned already?
    private String superClass; // Our superclass name
    private String[] superInterfaces; // Names of implemented interfaces
    private boolean isInnerClass; // Is this an inner class?
  private ObfuscationConfig obfuscationConfig;
  private String sourceFileMapping;
    private int classFileAccess;
    private LineNumberTableMapper lineNumberTableMapper;
    private Vector nameListUps = new Vector(); // NameListUp interfaces for super-class/interfaces
    private Vector nameListDowns = new Vector(); // NameListDown interfaces for derived class/interfaces
    public static int nameSpace = 0;
    private static NameMaker methodNameMaker;
    private static NameMaker fieldNameMaker;
    private Map innerClassModifiers = new HashMap();
    private Set attributesToKeep = new HashSet();
//    private boolean isPublic = true;

    // Class Methods ---------------------------------------------------------


    // Instance Methods ------------------------------------------------------
    /** Ctor. */
    public Cl(TreeItem parent, boolean isInnerClass, String name, String superClass, String[] superInterfaces,
              int modifiers, ObfuscationConfig obfuscationConfig)
    {
        super(parent, name);
        this.superClass = superClass;
        this.superInterfaces = superInterfaces;
        this.isInnerClass = isInnerClass;
      this.obfuscationConfig = obfuscationConfig;
      this.access = modifiers;
        if (parent == null || name.equals(""))
        {
            System.err.println("Internal error: class must have parent and name");
        }
        if (parent instanceof Cl)
        {
            sep = ClassFile.SEP_INNER;
        }

        // Do not obfuscate anonymous inner classes
        if (isInnerClass && Character.isDigit(name.charAt(0)))
        {
            setOutName(getInName());
        }
    }
    
    void setClassFileAccess(int classFileAccess){
      this.classFileAccess = classFileAccess;
    }

    public LineNumberTableMapper getLineNumberTableMapper() {
      return lineNumberTableMapper;
    }

    public void setLineNumberTableMapper(LineNumberTableMapper lineNumberTableMapper) {
      this.lineNumberTableMapper = lineNumberTableMapper;
    }

    public String getSourceFileMapping() {
      return sourceFileMapping;
    }

    public void setSourceFileMapping(String sourceFileMapping) {
      this.sourceFileMappingSet = true;
      this.sourceFileMapping = sourceFileMapping;
    }

    public boolean isSourceFileMappingSet(){
      return sourceFileMappingSet;
    }

    public String getSuperClass(){
      return this.superClass;
    }
    
    public String[] getInterfaces(){
      return this.superInterfaces;
    }
    
    public void setInnerClassModifiers(Map map){
      this.innerClassModifiers.putAll(map);
    }
    
    public int getInnerClassModifier(String fqn){
      Integer i = (Integer) innerClassModifiers.get(fqn);
      if (i == null){
        return Modifier.PRIVATE;
      } else {
        return i.intValue();
      }
    }
    
    /** Is this an inner class? */
    public boolean isInnerClass() {return isInnerClass;}

    /** Get a method by name. */
    public Md getMethod(String name, String descriptor)  {return (Md)mds.get(name + descriptor);}

    /** Get a field by name. */
    public Fd getField(String name)  {return (Fd)fds.get(name);}

    /** Get an Enumeration of methods. */
    public Enumeration getMethodEnum()  {return mds.elements();}

    /** Get an Enumeration of fields. */
    public Enumeration getFieldEnum()  {return fds.elements();}

    /** Is this class's name a match to the wildcard pattern? */
    public boolean isWildcardMatch(String pattern) {
        return isMatch(pattern, getFullInName());
    }

    /** Is this class's name a non-recursive match to the wildcard pattern? */
    public boolean isNRWildcardMatch(String pattern) {
        return isNRMatch(pattern, getFullInName());
    }

    /** Does this class have the specified class in its super chain? */
    public boolean hasAsSuper(String queryName) throws ClassNotFoundException
    {
        // Special case: we are java/lang/Object
        if (superClass == null) return false;

        try
        {
            if (superClass.equals(queryName))
            {
                return true;
            }
            else
            {
                Cl superClassItem = classTree.getCl(superClass);
                if (superClassItem != null)
                {
                    return superClassItem.hasAsSuper(queryName);
                }
                else
                {
                    Class extSuper = resolver.resolve(ClassFile.translate(superClass));
                    while (extSuper != null)
                    {
                        if (extSuper.getName().equals(ClassFile.translate(queryName)))
                        {
                            return true;
                        }
                        extSuper = extSuper.getSuperclass();
                    }
                    return false;
                }
            }
        }
        catch (ClassNotFoundException cnfe)
        {
          if (pedantic){
            throw cnfe;
          } else {
            return false;
          }
        }
    }

    /** Add an inner class. */
    public Cl addClass(Object[] classInfo)
    {
        return addClass(true, classInfo);
    }

    /** Add an inner class, used when copying inner classes from a placeholder. */
    public Cl addClass(Cl cl) 
    {
        cls.put(cl.getInName(), cl);
        return cl;
    }

    /** Add a placeholder class. */
    public Cl addPlaceholderClass(String name) 
    {
        return addPlaceholderClass(true, name);
    }

    /** Add a method.
     * @param methodInfo*/
    public Md addMethod(MethodInfo methodInfo)
    {
      boolean isSynthetic = methodInfo.isSynthetic();
      String name = methodInfo.getName();
      String descriptor = methodInfo.getDescriptor();
      int access = methodInfo.getAccessFlags();
        // Exclude the  and  methods
        if (name.charAt(0) == '<')
        {
            return null;
        }
        Md md = getMethod(name, descriptor);
        if (md == null)
        {
            md = new Md(this, isSynthetic, name, descriptor, access, methodInfo.getObfuscationConfig());
            mds.put(name + descriptor, md);
        }
        // Exclude the public ... valueOf(String) and values() methods of the Enum classes.
        final int PublicStatic = ClassConstants.ACC_PUBLIC | ClassConstants.ACC_STATIC;
        if ((this.classFileAccess & ClassConstants.ACC_ENUM) == ClassConstants.ACC_ENUM &&
            ((access & PublicStatic) == PublicStatic)){
          final String desc = "(Ljava/lang/String;)L" + getFullInName() + ';';
          if ("valueOf".equals(name) && desc.equals(descriptor)){
            md.setOutName(name);
          } else if ("values".equals(name) && descriptor.equals("()[L" + getFullInName() + ';')){
            md.setOutName(name);
          }
        }
        return md;
    }

    /** Add a field. */
    public Fd addField(FieldInfo fieldInfo)
    {
      boolean isSynthetic = fieldInfo.isSynthetic();
      String name = fieldInfo.getName();
      String descriptor = fieldInfo.getDescriptor();
      int access = fieldInfo.getAccessFlags();
        Fd fd = getField(name);
        if (fd == null)
        {
            fd = new Fd(this, isSynthetic, name, descriptor, access, fieldInfo.getObfuscationConfig());
            fds.put(name, fd);
        }
        return fd;
    }

    /** Prepare for resolve of a class entry by resetting flags. */
    public void resetResolve() 
    {
        isScanned = false;
        isResolved = false;
        nameListDowns.removeAllElements();
    }

    /** Set up reverse list of reserved names prior to resolving classes. */
    public void setupNameListDowns() 
    {
        // Special case: we are java/lang/Object
        if (superClass == null) return;

        // Add this class as a NameListDown to the super and each interface, if they are in the JAR
        Cl superClassItem = classTree.getCl(superClass);
        if (superClassItem != null)
        {
            superClassItem.nameListDowns.addElement(this);
        }
        for (int i = 0; i < superInterfaces.length; i++)
        {
            Cl interfaceItem = classTree.getCl(superInterfaces[i]);
            if (interfaceItem != null)
            {
                interfaceItem.nameListDowns.addElement(this);
            }
        }
    }

    /**
     * Resolve a class entry - set obfuscation permissions based on super class and interfaces.
     * Overload method and field names maximally.
     */
    public void resolveOptimally() throws ClassNotFoundException
    {
        // Already processed, then do nothing

        if (!isResolved)
        {
            // Get lists of method and field names in inheritance namespace
            Vector methods = new Vector();
            Vector fields = new Vector();
            scanNameSpaceExcept(null, methods, fields);
            String[] methodNames = new String[methods.size()];
            for (int i = 0; i < methodNames.length; i++)
            {
                methodNames[i] = (String)methods.elementAt(i);
            }
            String[] fieldNames = new String[fields.size()];
            for (int i = 0; i < fieldNames.length; i++)
            {
                fieldNames[i] = (String)fields.elementAt(i);
            }

            NameMakerFactory nmf = NameMakerFactory.getInstance();

            // Create new name-makers for the namespace
            methodNameMaker = nmf.getMethodNameMaker(methodNames, getFullInName());
            fieldNameMaker = nmf.getFieldNameMaker(fieldNames, getFullInName());

            // Resolve a full name space
            resolveNameSpaceExcept(null);

            // and move to next
            nameSpace++;
        }
    }

    // Get lists of method and field names in inheritance namespace
    private void scanNameSpaceExcept(Cl ignoreCl, Vector methods, 
                                     Vector fields) throws ClassNotFoundException
    {
//      System.out.println("Scan: "+getInName());

        // Special case: we are java/lang/Object
        if (superClass == null) return;

        // Traverse one step in each direction in name space, scanning
        if (!isScanned)
        {
            // First step up to super classes, scanning them
            Cl superCl = classTree.getCl(superClass);
            if (superCl != null) // internal to JAR
            {
                if (superCl != ignoreCl)
                {
                    superCl.scanNameSpaceExcept(this, methods, fields);
                }
            }
            else // external to JAR
            {
              scanExtSupers(superClass, methods, fields);
            }
            for (int i = 0; i < superInterfaces.length; i++)
            {
                Cl interfaceItem = classTree.getCl(superInterfaces[i]);
                if (interfaceItem != null)
                {
                  if (interfaceItem != ignoreCl){
                    interfaceItem.scanNameSpaceExcept(this, methods, fields);
                  }
                } else { // external to JAR
                  scanExtSupers(superInterfaces[i], methods, fields);
                }
            }

            // Next, scan ourself
            if (!isScanned)
            {
                scanThis(methods, fields);

                // Signal class has been scanned
                isScanned = true;
            }

            // Finally step down to derived classes, resolving them
            for (Enumeration clEnum = nameListDowns.elements(); clEnum.hasMoreElements(); )
            {
                Cl cl = (Cl)clEnum.nextElement();
                if (cl != ignoreCl)
                {
                    cl.scanNameSpaceExcept(this, methods, fields);
                }
            }
        }
    }


    // Get lists of method and field names in inheritance namespace
    private void scanExtSupers(String name, Vector methods,
                               Vector fields) throws ClassNotFoundException
    {
      try {
        Class extClass = resolver.resolve(ClassFile.translate(name));
        scanExtSupers(extClass, methods, fields);
      } catch(ClassNotFoundException cnfe)
      {
        if (pedantic){
          throw cnfe;
        } else {
          Logger.getInstance().warningToLogfile("Unresolved external dependency: "+Conversion.toJavaClass(name)+" not found!");
          Logger.getInstance().setUnresolved();
        }
      }
    }

    // Get lists of method and field names in inheritance namespace
    private void scanExtSupers(Class extClass, Vector methods, 
                               Vector fields) throws ClassNotFoundException
    {
      // Get public methods and fields from supers and interfaces up the tree
      Method[] allPubMethods = extClass.getMethods();
      if (allPubMethods != null) {
          for (int i = 0; i < allPubMethods.length; i++) {
              String methodName = allPubMethods[i].getName();
              if (methods.indexOf(methodName) == -1) {
                  methods.addElement(methodName);
              }
          }
      }
      Field[] allPubFields = extClass.getFields();
      if (allPubFields != null) {
          for (int i = 0; i < allPubFields.length; i++) {
              String fieldName = allPubFields[i].getName();
              if (fields.indexOf(fieldName) == -1) {
                  fields.addElement(fieldName);
              }
          }
      }
      
      // Go up the super hierarchy, adding all non-public methods/fields
      while (extClass != null) {
          Method[] allClassMethods = extClass.getDeclaredMethods();
          if (allClassMethods != null) {
              for (int i = 0; i < allClassMethods.length; i++) {
                  if (!Modifier.isPublic(allClassMethods[i].getModifiers())) {
                      String methodName = allClassMethods[i].getName();
                      if (methods.indexOf(methodName) == -1) {
                          methods.addElement(methodName);
                      }
                  }
              }
          }
          Field[] allClassFields = extClass.getDeclaredFields();
          if (allClassFields != null) {
              for (int i = 0; i < allClassFields.length; i++) {
                  if (!Modifier.isPublic(allClassFields[i].getModifiers())) {
                      String fieldName = allClassFields[i].getName();
                      if (fields.indexOf(fieldName) == -1) {
                          fields.addElement(fieldName);
                      }
                  }
              }
          }
          extClass = extClass.getSuperclass();
      }
    }

    // Add method and field names from this class to the lists
    private void scanThis(Vector methods, Vector fields) 
    {
        for (Enumeration mdEnum = mds.elements(); mdEnum.hasMoreElements(); )
        {
            Md md = (Md)mdEnum.nextElement();
            if (md.isFixed()) 
            {
                String name = md.getOutName();
                if (methods.indexOf(name) == -1) 
                {
                    methods.addElement(name);
                }
            }
        }
        for (Enumeration fdEnum = fds.elements(); fdEnum.hasMoreElements(); )
        {
            Fd fd = (Fd)fdEnum.nextElement();
            if (fd.isFixed()) 
            {
                String name = fd.getOutName();
                if (fields.indexOf(name) == -1) 
                {
                    fields.addElement(name);
                }
            }
        }
    }

    // Resolve an entire inheritance name space optimally.
    private void resolveNameSpaceExcept(Cl ignoreCl) throws ClassNotFoundException
    {
        // Special case: we are java/lang/Object
        if (superClass == null) return;

        // Traverse one step in each direction in name space, resolving
        if (!isResolved)
        {
            // First step up to super classes, resolving them, since we depend on them
            Cl superCl = classTree.getCl(superClass);
            if (superCl != null && superCl != ignoreCl)
            {
                superCl.resolveNameSpaceExcept(this);
            }
            for (int i = 0; i < superInterfaces.length; i++)
            {
                Cl interfaceItem = classTree.getCl(superInterfaces[i]);
                if (interfaceItem != null && interfaceItem != ignoreCl)
                {
                    interfaceItem.resolveNameSpaceExcept(this);
                }
            }

            // Next, resolve ourself
            if (!isResolved)
            {
//              System.out.println("Resolve: "+getInName());
//              System.out.println("fds: "+fds);              
              resolveThis();

                // Signal class has been processed
                isResolved = true;
            }

            // Finally step down to derived classes, resolving them
            for (Enumeration clEnum = nameListDowns.elements(); clEnum.hasMoreElements(); )
            {
                Cl cl = (Cl)clEnum.nextElement();
                if (cl != ignoreCl)
                {
                    cl.resolveNameSpaceExcept(this);
                }
            }
        }
    }

    // For each super interface and the super class, if it is outside DB, use reflection
    // to merge its list of public/protected methods/fields --
    // while for those in the DB, resolve to get the name-mapping lists
    private void resolveThis() throws ClassNotFoundException
    {
        // Special case: we are java/lang/Object
        if (superClass == null) return;

        Cl superClassItem = classTree.getCl(superClass);
        nameListUps.addElement(superClassItem != null ?
                               (NameListUp)superClassItem :
                               getExtNameListUp(superClass));
        for (int i = 0; i < superInterfaces.length; i++)
        {
            Cl interfaceItem = classTree.getCl(superInterfaces[i]);
            nameListUps.addElement(interfaceItem != null ?
                                   (NameListUp)interfaceItem :
                                   getExtNameListUp(superInterfaces[i]));
        }

        // Run through each method/field in this class checking for reservations and
        // obfuscating accordingly
        nextMethod:
        for (Enumeration mdEnum = mds.elements(); mdEnum.hasMoreElements(); )
        {
            Md md = (Md)mdEnum.nextElement();
          if (!md.isFixed()) {
            //muellese: private should never make any problems
            if (!Modifier.isPrivate(md.getModifiers())) {
              // Check for name reservation via derived classes
              for (Enumeration nlEnum = nameListDowns.elements(); nlEnum.hasMoreElements();) {
                String theOutName = ((NameListDown) nlEnum.nextElement()).getMethodObfNameDown(this, md.getInName(),
                    md.getDescriptor());
                if (theOutName != null) {
                  md.setOutName(theOutName);
                  continue nextMethod;
                }
              }
              // Check for name reservation via super classes
              for (Enumeration nlEnum = nameListUps.elements(); nlEnum.hasMoreElements();) {
                String theOutName = ((NameListUp) nlEnum.nextElement()).getMethodOutNameUp(md.getInName(),
                    md.getDescriptor());
                if (theOutName != null) {
                  md.setOutName(theOutName);
                  continue nextMethod;
                }
              }
            }
            // If no other restrictions, obfuscate it
            md.setOutName(methodNameMaker.nextName(md.getDescriptor()));
          } else {
            if (Modifier.isNative(md.access)) {
              // native method, check if hierarchy is fixed, too, otherwise - this will break JNI calls
              if (!md.getParent().getFullOutName().equals(md.getParent().getFullInName())) {
                Logger.getInstance().warning(
                    "Method " + md.getOutName() + " is native but " + md.getParent().getFullInName() + " is not kept/exposed.");
              }
            }
          }
        }
        nextField:
        for (Enumeration fdEnum = fds.elements(); fdEnum.hasMoreElements(); )
        {
            Fd fd = (Fd)fdEnum.nextElement();
            if (!fd.isFixed())
            {
                //muellese: private should never make any problems
                if (!Modifier.isPrivate(fd.getModifiers())){
                  // Check for name reservation via derived classes
                  for (Enumeration nlEnum = nameListDowns.elements(); nlEnum.hasMoreElements(); )
                  {
                      String theOutName = ((NameListDown)nlEnum.nextElement()).getFieldObfNameDown(this, fd.getInName());
                      if (theOutName != null)
                      {
                          fd.setOutName(theOutName);
                          continue nextField;
                      }
                  }
                  // Check for name reservation via super classes
                  for (Enumeration nlEnum = nameListUps.elements(); nlEnum.hasMoreElements(); )
                  {
                      String superOutName = ((NameListUp)nlEnum.nextElement()).getFieldOutNameUp(fd.getInName());
                      if (superOutName != null)
                      {
                          fd.setOutName(superOutName);
                          continue nextField;
                      }
                  }
                }
                // If no other restrictions, obfuscate it
                fd.setOutName(fieldNameMaker.nextName(null));
            }
        }
    }

    /** Get output method name from list, or null if no mapping exists. */
    public String getMethodOutNameUp(String name, String descriptor) throws ClassNotFoundException
    {
        // Check supers
        for (Enumeration enumeration = nameListUps.elements(); enumeration.hasMoreElements(); )
        {
            String superOutName = ((NameListUp)enumeration.nextElement()).getMethodOutNameUp(name, descriptor);
            if (superOutName != null)
            {
                return superOutName;
            }
        }

        // Check self
        Md md = getMethod(name, descriptor);
        if (md != null && !Modifier.isPrivate(md.access))
        {
            return md.getOutName();
        }
        else
        {
            return null;
        }
    }

    /** Get obfuscated method name from list, or null if no mapping exists. */
    public String getMethodObfNameUp(String name, String descriptor) throws ClassNotFoundException
    {
        // Check supers
        for (Enumeration enumeration = nameListUps.elements(); enumeration.hasMoreElements(); )
        {
            String superObfName = ((NameListUp)enumeration.nextElement()).getMethodObfNameUp(name, descriptor);
            if (superObfName != null)
            {
                return superObfName;
            }
        }

        // Check self
        Md md = getMethod(name, descriptor);
        if (md != null && !Modifier.isPrivate(md.access))
        {
            return md.getObfName();
        }
        else
        {
            return null;
        }
    }

    /** Get output field name from list, or null if no mapping exists. */
    public String getFieldOutNameUp(String name) throws ClassNotFoundException
    {
        // Check supers
        for (Enumeration enumeration = nameListUps.elements(); enumeration.hasMoreElements(); )
        {
            String superOutName = ((NameListUp)enumeration.nextElement()).getFieldOutNameUp(name);
            if (superOutName != null)
            {
                return superOutName;
            }
        }

        // Check self
        Fd fd = getField(name);
        if (fd != null && !Modifier.isPrivate(fd.access))
        {
            return fd.getOutName();
        }
        else
        {
            return null;
        }
    }

    /** Get obfuscated field name from list, or null if no mapping exists. */
    public String getFieldObfNameUp(String name) throws ClassNotFoundException
    {
        // Check supers
        for (Enumeration enumeration = nameListUps.elements(); enumeration.hasMoreElements(); )
        {
            String superObfName = ((NameListUp)enumeration.nextElement()).getFieldObfNameUp(name);
            if (superObfName != null)
            {
                return superObfName;
            }
        }

        // Check self
        Fd fd = getField(name);
        if (fd != null && !Modifier.isPrivate(fd.access))
        {
            return fd.getObfName();
        }
        else
        {
            return null;
        }
    }

    /** Is the method reserved because of its reservation down the class hierarchy? */
    public String getMethodObfNameDown(Cl caller, String name, String descriptor) throws ClassNotFoundException
    {
        // Check ourself for an explicit 'do not obfuscate'
        Md md = getMethod(name, descriptor);
        if (md != null && md.isFixed())
        {
            return md.getOutName();
        }

        // Check our supers, except for our caller (special case if we are java/lang/Object)
        String theObfName = null;
        if (superClass != null)
        {
            Cl superClassItem = classTree.getCl(superClass);
            if (superClassItem != caller)
            {
                NameListUp nl = superClassItem != null ? (NameListUp)superClassItem : getExtNameListUp(superClass);
                theObfName = nl.getMethodObfNameUp(name, descriptor);
                if (theObfName != null)
                {
                    return theObfName;
                }
            }
            for (int i = 0; i < superInterfaces.length; i++)
            {
                Cl interfaceItem = classTree.getCl(superInterfaces[i]);
                if (interfaceItem != caller) 
                {
                    NameListUp nl = interfaceItem != null ? (NameListUp)interfaceItem : getExtNameListUp(superInterfaces[i]);
                    theObfName = nl.getMethodObfNameUp(name, descriptor);
                    if (theObfName != null)
                    {
                        return theObfName;
                    }
                }
            }
        }

        // Check our derived classes
        for (Enumeration enumeration = nameListDowns.elements(); enumeration.hasMoreElements(); )
        {
            theObfName = ((NameListDown)enumeration.nextElement()).getMethodObfNameDown(this, name, descriptor);
            if (theObfName != null)
            {
                return theObfName;
            }
        }

        // No reservation found
        return null;
    }

    /** Is the field reserved because of its reservation down the class hierarchy? */
    public String getFieldObfNameDown(Cl caller, String name) throws ClassNotFoundException
    {
        // Check ourself for an explicit 'do not obfuscate'
        Fd fd = getField(name);
        if (fd != null && fd.isFixed())
        {
            return fd.getOutName();
        }

        // Check our supers, except for our caller (special case if we are java/lang/Object)
        String theObfName = null;
        if (superClass != null)
        {
            Cl superClassItem = classTree.getCl(superClass);
            if (superClassItem != caller)
            {
                NameListUp nl = superClassItem != null ? (NameListUp)superClassItem : getExtNameListUp(superClass);
                theObfName = nl.getFieldObfNameUp(name);
                if (theObfName != null)
                {
                    return theObfName;
                }
            }
            for (int i = 0; i < superInterfaces.length; i++)
            {
                Cl interfaceItem = classTree.getCl(superInterfaces[i]);
                if (interfaceItem != caller)
                {
                    NameListUp nl = interfaceItem != null ? (NameListUp)interfaceItem : getExtNameListUp(superInterfaces[i]);
                    theObfName = nl.getFieldObfNameUp(name);
                    if (theObfName != null)
                    {
                        return theObfName;
                    }
                }
            }
        }

        // Check our derived classes
        for (Enumeration enumeration = nameListDowns.elements(); enumeration.hasMoreElements(); )
        {
            theObfName = ((NameListDown)enumeration.nextElement()).getFieldObfNameDown(this, name);
            if (theObfName != null)
            {
                return theObfName;
            }
        }

        // No reservation found
        return null;
    }

    // Construct, or retrieve from cache, the NameListUp object for an external class/interface
    private static Hashtable extNameListUpCache = new Hashtable();
    private NameListUp getExtNameListUp(String name) throws ClassNotFoundException
    {
        NameListUp nl = (NameListUp)extNameListUpCache.get(name);
        if (nl == null)
        {
            nl = new ExtNameListUp(name);
            extNameListUpCache.put(name, nl);
        }
        return nl;
    }

    // NameListUp for class/interface not in the database.
    class ExtNameListUp implements NameListUp
    {
        // Class's fully qualified name
        private Class extClass;
        private Method[] methods = null;

        // Ctor.
        public ExtNameListUp(String name) throws ClassNotFoundException
        {
          try
          {
            extClass = resolver.resolve(ClassFile.translate(name));
          }
          catch (ClassNotFoundException cnfe)
          {
            if (pedantic){
              throw cnfe;
            } else {
              Logger.getInstance().warningToLogfile("Unresolved external dependency: "+Conversion.toJavaClass(name)+" not found!");
              Logger.getInstance().setUnresolved();
            }
          }
        }

        // Ctor.
        public ExtNameListUp(Class extClass) 
        {
            this.extClass = extClass;
        }

        // Get obfuscated method name from list, or null if no mapping exists.
        public String getMethodObfNameUp(String name, String descriptor) 
        {
            return getMethodOutNameUp(name, descriptor);
        }

        // Get obfuscated method name from list, or null if no mapping exists.
        public String getMethodOutNameUp(String name, String descriptor) 
        {
          //RW
          if(extClass == null) return name;
          
            // Get list of public/protected methods
            if (methods == null)
            {
                methods = getAllDeclaredMethods(extClass);
                Vector pruned = new Vector();
                for (int i = 0; i < methods.length; i++)
                {
                    int modifiers = methods[i].getModifiers();
                    if (!Modifier.isPrivate(modifiers))
                    {
                        pruned.addElement(methods[i]);
                    }
                }
                methods = new Method[pruned.size()];
                for (int i = 0; i < methods.length; i++)
                {
                    methods[i] = (Method)pruned.elementAt(i);
                }
            }

            // Check each public/protected class method against the named one
            nextMethod:
            for (int i = 0; i < methods.length; i++)
            {
                if (name.equals(methods[i].getName()))
                {
                    String[] paramAndReturnNames = ClassFile.parseDescriptor(descriptor);
                    Class[] paramTypes = methods[i].getParameterTypes();
                    Class returnType = methods[i].getReturnType();
                    if (paramAndReturnNames.length == paramTypes.length + 1)
                    {
                        for (int j = 0; j < paramAndReturnNames.length - 1; j++)
                        {
                            if (!paramAndReturnNames[j].equals(paramTypes[j].getName()))
                            {
                                continue nextMethod;
                            }
                        }
                        String returnName = returnType.getName();
                        if (!paramAndReturnNames[paramAndReturnNames.length - 1].equals(returnName))
                        {
                            continue nextMethod;
                        }

                        // We have a match, and so the derived class method name must be made to match
                        return name;
                    }
                }
            }

            // Method is not present
            return null;
        }

        // Get obfuscated field name from list, or null if no mapping exists.
        public String getFieldObfNameUp(String name) 
        {
            return getFieldOutNameUp(name);
        }

        // Get obfuscated field name from list, or null if no mapping exists.
        public String getFieldOutNameUp(String name) 
        {
          if(extClass == null) return name;
            // Use reflection to check class for field
            Field field = getAllDeclaredField(extClass, name);
            if (field != null)
            {
                // Field must be public or protected
                int modifiers = field.getModifiers();
                if (!Modifier.isPrivate(modifiers))
                {
                    return name;
                }
            }

            // Field is not present
            return null;
        }

        // Get all methods (from supers too) regardless of access level
        private Method[] getAllDeclaredMethods(Class theClass)
        {
            Vector ma = new Vector();
            int length = 0;

            // Get the public methods from all supers and interfaces up the tree
            Method[] allPubMethods = theClass.getMethods();
            ma.addElement(allPubMethods);
            length += allPubMethods.length;

            // Go up the super hierarchy, getting arrays of all methods (some redundancy
            // here, but that's okay)
            while (theClass != null)
            {
                Method[] methods = theClass.getDeclaredMethods();
                ma.addElement(methods);
                length += methods.length;
                theClass = theClass.getSuperclass();
            }

            // Merge the arrays
            Method[] allMethods = new Method[length];
            int pos = 0;
            for (Enumeration enumeration = ma.elements(); enumeration.hasMoreElements(); )
            {
                Method[] methods = (Method[])enumeration.nextElement();
                System.arraycopy(methods, 0, allMethods, pos, methods.length);
                pos += methods.length;
            }
            return allMethods;
        }

        // Get a specified field (from supers and interfaces too) regardless of access level
        private Field getAllDeclaredField(Class theClass, String name)
        {
            Class origClass = theClass;

            // Check for field in supers
            while (theClass != null)
            {
                Field field = null;
                try
                {
                    field = theClass.getDeclaredField(name);
                }
                catch (Exception e)
                {
                    field = null;
                }
                if (field != null)
                {
                    return field;
                }
                theClass = theClass.getSuperclass();
            }

            // Check for public field in supers and interfaces (some redundancy here,
            // but that's okay)
            try
            {
                return origClass.getField(name);
            }
            catch (SecurityException nsfe)
            {
                return null;
            }
            catch (NoSuchFieldException nsfe)
            {
                return null;
            }
        }
    }

  public ObfuscationConfig getObfuscationConfig() {
    return obfuscationConfig;
  }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy