com.yworks.yguard.obf.ClassTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of retroguard Show documentation
Show all versions of retroguard Show documentation
The open-source Java obfuscation tool working with Ant and Gradle by yWorks - the diagramming experts
/*
* 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 com.yworks.yguard.ParseException;
import com.yworks.yguard.obf.classfile.ClassConstants;
import com.yworks.yguard.obf.classfile.ClassFile;
import com.yworks.yguard.obf.classfile.ClassItemInfo;
import com.yworks.yguard.obf.classfile.FieldInfo;
import com.yworks.yguard.obf.classfile.LineNumberTableAttrInfo;
import com.yworks.yguard.obf.classfile.Logger;
import com.yworks.yguard.obf.classfile.MethodInfo;
import com.yworks.yguard.obf.classfile.NameMapper;
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* Tree structure of package levels, classes, methods and fields used for obfuscation.
*
* @author Mark Welsh
*/
public class ClassTree implements NameMapper
{
// Constants -------------------------------------------------------------
public static final char PACKAGE_LEVEL = '/';
public static final char CLASS_LEVEL = '$';
public static final char METHOD_FIELD_LEVEL = '/';
// Fields ----------------------------------------------------------------
private Vector retainAttrs = new Vector(); // List of attributes to retain
private Pk root = null; // Root package in database (Java default package)
// Class methods ---------------------------------------------------------
/** Return a fully qualified name broken into package/class segments. */
public static Enumeration getNameEnum(String name)
{
Vector vec = new Vector();
String nameOrig = name;
while (!name.equals(""))
{
int posP = name.indexOf(PACKAGE_LEVEL);
int posC = name.indexOf(CLASS_LEVEL);
Cons cons = null;
if (posP == -1 && posC == 0)
{
// this is the rare case when a toplevel class name starts with a dollar sign ('$')
// currently GSon has this in its library and causes problems with yGuard.
int innerClassIndex = name.indexOf(CLASS_LEVEL, 1);
int endIndex = innerClassIndex > 0 ? innerClassIndex : name.length();
cons = new Cons(new Character(CLASS_LEVEL), name.substring(0, endIndex));
name = name.substring(endIndex);
}
if (posP == -1 && posC == -1)
{
cons = new Cons(new Character(CLASS_LEVEL), name);
name = "";
}
if (posP == -1 && posC > 0)
{
cons = new Cons(new Character(CLASS_LEVEL), name.substring(0, posC));
//fixes retroguard bug, where
// 'ClassName$$InnerClassName' leads to a runtimeerror
while ((posC+1 < name.length()) && (name.charAt(posC+1) == CLASS_LEVEL)) posC++;
name = name.substring(posC + 1, name.length());
}
if (posP != -1 && posC == -1)
{
cons = new Cons(new Character(PACKAGE_LEVEL), name.substring(0, posP));
name = name.substring(posP + 1, name.length());
}
if (posP != -1 && posC != -1)
{
if (posP < posC)
{
cons = new Cons(new Character(PACKAGE_LEVEL), name.substring(0, posP));
name = name.substring(posP + 1, name.length());
}
else
{
throw new IllegalArgumentException("Invalid fully qualified name (a): " +
nameOrig);
}
}
if (((String)cons.cdr).equals(""))
{
throw new IllegalArgumentException("Invalid fully qualified name (b): " +
nameOrig);
}
vec.addElement(cons);
}
return vec.elements();
}
// Instance Methods ------------------------------------------------------
/** Ctor. */
public ClassTree()
{
root = Pk.createRoot(this);
}
/** Return the root node. */
public Pk getRoot() {return root;}
/**
* finds tree items by looking for name components only...
*/
public TreeItem findTreeItem(String[] nameParts){
TreeItem tmp = root;
for (int i = 0; tmp != null && i < nameParts.length; i++){
String name = nameParts[i];
tmp = findSubItem(tmp, name);
}
return tmp;
}
/**
* walks the tree of TreeItems in order to find a class forName
*/
public Cl findClassForName(String name){
int dindex = name.indexOf('$');
String innerClass = null;
if (dindex>0){
innerClass = name.substring(dindex+1);
name = name.substring(0, dindex);
}
int pindex = name.lastIndexOf('.');
String packageName = null;
if (pindex>0){
packageName = name.substring(0, pindex);
name = name.substring(pindex+1);
}
Pk pk = root;
if (packageName != null){
for (StringTokenizer st = new StringTokenizer(packageName, ".", false); st.hasMoreTokens();){
String token = st.nextToken();
pk = findPackage(pk, token);
if (pk == null) return null;
}
}
Cl cl = findClass(pk, name);
if (cl != null && innerClass != null){
for (StringTokenizer st = new StringTokenizer(innerClass, "$", false); st.hasMoreTokens();){
String token = st.nextToken();
cl = findClass(cl, token);
if (cl == null) return null;
}
}
return cl;
}
private Pk findPackage(TreeItem parent, String pName){
if (parent instanceof Pk){
for (Enumeration enumeration = ((Pk)parent).getPackageEnum(); enumeration.hasMoreElements();){
Pk subPk = (Pk) enumeration.nextElement();
if (subPk.getInName().equals(pName)){
return subPk;
}
}
}
return null;
}
private Cl findClass(PkCl parent, String pName){
for (Enumeration enumeration = ((PkCl)parent).getClassEnum(); enumeration.hasMoreElements();){
Cl cl = (Cl) enumeration.nextElement();
if (cl.getInName().equals(pName)){
return cl;
}
}
return null;
}
private TreeItem findSubItem(TreeItem parent, String childName){
if (parent instanceof Pk){
for (Enumeration enumeration = ((Pk)parent).getPackageEnum(); enumeration.hasMoreElements();){
Pk subPk = (Pk) enumeration.nextElement();
if (subPk.getInName().equals(childName)){
return subPk;
}
}
for (Enumeration enumeration = ((Pk)parent).getClassEnum(); enumeration.hasMoreElements();){
Cl cl = (Cl) enumeration.nextElement();
if (cl.getInName().equals(childName)){
return cl;
}
}
}
if (parent instanceof Cl){
for (Enumeration enumeration = ((Cl)parent).getClassEnum(); enumeration.hasMoreElements();){
Cl cl = (Cl) enumeration.nextElement();
if (cl.getInName().equals(childName)){
return cl;
}
}
return null;
}
return null;
}
/** Update the path of the passed filename, if that path corresponds to a package. */
public String getOutName(String inName)
{
try
{
TreeItem ti = root;
StringBuffer sb = new StringBuffer();
for (Enumeration nameEnum = getNameEnum(inName); nameEnum.hasMoreElements(); )
{
Cons nameSegment = (Cons)nameEnum.nextElement();
char tag = ((Character)nameSegment.car).charValue();
String name = (String)nameSegment.cdr;
switch (tag)
{
case PACKAGE_LEVEL:
if (ti != null)
{
ti = ((Pk)ti).getPackage(name);
if (ti != null)
{
sb.append(ti.getOutName());
}
else
{
sb.append(name);
}
}
else
{
sb.append(name);
}
sb.append(PACKAGE_LEVEL);
break;
case CLASS_LEVEL:
sb.append(name);
return sb.toString();
default:
throw new RuntimeException("Internal error: illegal package/class name tag");
}
}
}
catch (Exception e)
{
// Just drop through and return the original name
}
return inName;
}
/** Add a classfile's package, class, method and field entries to database. */
public void addClassFile(ClassFile cf)
{
// Add the fully qualified class name
TreeItem ti = root;
char parentTag = PACKAGE_LEVEL;
for (Enumeration nameEnum = getNameEnum(cf.getName()); nameEnum.hasMoreElements(); )
{
Cons nameSegment = (Cons)nameEnum.nextElement();
char tag = ((Character)nameSegment.car).charValue();
String name = (String)nameSegment.cdr;
switch (tag)
{
case PACKAGE_LEVEL:
ti = ((Pk)ti).addPackage(name);
break;
case CLASS_LEVEL:
// If this is an inner class, just add placeholder classes up the tree
if (nameEnum.hasMoreElements())
{
ti = ((PkCl)ti).addPlaceholderClass(name);
}
else
{
Object[] classInfo = {
name, cf.getSuper(), cf.getInterfaces(), cf.getModifiers(), ClassItemInfo.getObfuscationConfig(cf.getAttributes())
};
Cl cl =((PkCl)ti).addClass(classInfo);
cl.setInnerClassModifiers(cf.getInnerClassModifiers());
cl.setClassFileAccess(cf.getClassFileAccess());
ti = cl;
}
break;
default:
throw new ParseException("Internal error: illegal package/class name tag");
}
parentTag = tag;
}
// We must have a class before adding methods and fields
if (ti instanceof Cl)
{
Cl cl = (Cl)ti;
cl.access = cf.getModifiers();
// Add the class's methods to the database
for (Enumeration enumeration = cf.getMethodEnum(); enumeration.hasMoreElements(); )
{
cl.addMethod((MethodInfo) enumeration.nextElement());
}
// Add the class's fields to the database
for (Enumeration enumeration = cf.getFieldEnum(); enumeration.hasMoreElements(); )
{
cl.addField((FieldInfo) enumeration.nextElement());
}
}
else
{
throw new ParseException("Inconsistent class file.");
}
}
/** Mark an attribute type for retention. */
public void retainAttribute(String name)
{
retainAttrs.addElement(name);
}
private boolean modifierMatch(int level, int mods){
if (level == YGuardRule.LEVEL_NONE) return false;
if (Modifier.isPublic(mods)){
return (level & YGuardRule.PUBLIC) == YGuardRule.PUBLIC;
}
if (Modifier.isProtected(mods)){
return (level & YGuardRule.PROTECTED) == YGuardRule.PROTECTED;
}
if (Modifier.isPrivate(mods)){
return (level & YGuardRule.PRIVATE) == YGuardRule.PRIVATE;
}
// package friendly left only
return (level & YGuardRule.FRIENDLY) == YGuardRule.FRIENDLY;
}
/** Mark a class/interface type (and possibly methods and fields defined in class) for retention. */
public void retainClass(String name, int classLevel, int methodLevel, int fieldLevel, boolean retainHierarchy)
{
// Mark the class (or classes, if this is a wildcarded specifier)
for (Enumeration clEnum = getClEnum(name, classLevel); clEnum.hasMoreElements(); )
{
Cl classItem = (Cl)clEnum.nextElement();
if(retainHierarchy && classLevel != YGuardRule.LEVEL_NONE) retainHierarchy(classItem);
// Retain methods if requested
if (methodLevel != YGuardRule.LEVEL_NONE)
{
for (Enumeration enumeration = classItem.getMethodEnum(); enumeration.hasMoreElements(); )
{
Md md = (Md)enumeration.nextElement();
if (modifierMatch(methodLevel, md.getModifiers()))
{
md.setOutName(md.getInName());
md.setFromScript();
}
}
// do super classes and interfaces...
if ((methodLevel & (YGuardRule.PUBLIC | YGuardRule.PROTECTED | YGuardRule.FRIENDLY)) != 0
||(fieldLevel & (YGuardRule.PUBLIC | YGuardRule.PROTECTED | YGuardRule.FRIENDLY)) != 0){
int mask = YGuardRule.PRIVATE;
int ml = methodLevel & ~mask;
int fl = fieldLevel & ~mask;
int cl = classLevel & ~mask;
String[] interfaces = classItem.getInterfaces();
if (interfaces != null){
for (int i = 0; i < interfaces.length; i++){
String interfaceClass = interfaces[i];
retainClass(interfaceClass, cl, ml, fl, false);
}
}
String superClass = classItem.getSuperClass();
if (superClass != null){
// staying in package?!
if (!superClass.startsWith(classItem.getParent().getFullInName())){
mask |= YGuardRule.FRIENDLY;
ml = methodLevel & ~mask;
fl = fieldLevel & ~mask;
cl = classLevel & ~mask;
}
retainClass(superClass, cl, ml, fl, false);
}
}
}
// Retain fields if requested
if (fieldLevel != YGuardRule.LEVEL_NONE)
{
for (Enumeration enumeration = classItem.getFieldEnum(); enumeration.hasMoreElements(); )
{
Fd fd = (Fd)enumeration.nextElement();
if (modifierMatch(fieldLevel, fd.getModifiers()))
{
fd.setOutName(fd.getInName());
fd.setFromScript();
}
}
}
}
}
/** Mark a method type for retention. */
public void retainMethod(String name, String descriptor)
{
for (Enumeration enumeration = getMdEnum(name, descriptor);
enumeration.hasMoreElements(); ) {
Md md = (Md)enumeration.nextElement();
md.setOutName(md.getInName());
md.setFromScript();
}
}
/** Mark a field type for retention. */
public void retainField(String name)
{
for (Enumeration enumeration = getFdEnum(name); enumeration.hasMoreElements(); ) {
Fd fd = (Fd)enumeration.nextElement();
fd.setOutName(fd.getInName());
fd.setFromScript();
}
}
/** Mark a package for retention, and specify its new name. */
public void retainPackageMap(String name, String obfName)
{
retainItemMap(getPk(name), obfName);
}
/** Mark a class/interface type for retention, and specify its new name. */
public void retainClassMap(String name, String obfName)
{
retainItemMap(getCl(name), obfName);
}
/** Mark a method type for retention, and specify its new name. */
public void retainMethodMap(String name, String descriptor,
String obfName)
{
retainItemMap(getMd(name, descriptor), obfName);
}
/** Mark a field type for retention, and specify its new name. */
public void retainFieldMap(String name, String obfName)
{
retainItemMap(getFd(name), obfName);
}
// Mark an item for retention, and specify its new name.
private void retainItemMap(TreeItem item, String obfName)
{
if (!item.isFixed())
{
item.setOutName(obfName);
item.setFromScriptMap();
} else {
if (!item.getOutName().equals(obfName)){
item.setOutName(obfName);
item.setFromScriptMap();
// do warning
Logger.getInstance().warning("'" + item.getFullInName() + "' will be remapped to '" + obfName + "' according to mapping rule!");
}
}
// if (!item.isFixed())
// {
// item.setFromScriptMap();
// }
// item.setOutName(obfName);
}
/** Traverse the class tree, generating obfuscated names within each namespace. */
public void generateNames()
{
walkTree(new TreeAction() {
public void packageAction(Pk pk) {pk.generateNames();}
public void classAction(Cl cl) {cl.generateNames();}
});
}
/** Resolve the polymorphic dependencies of each class. */
public void resolveClasses() throws ClassNotFoundException
{
walkTree(new TreeAction() {
public void classAction(Cl cl) {cl.resetResolve();}
});
walkTree(new TreeAction() {
public void classAction(Cl cl) {cl.setupNameListDowns();}
});
Cl.nameSpace = 0;
final ClassNotFoundException[] ex = new ClassNotFoundException[1];
try{
walkTree(new TreeAction() {
public void classAction(Cl cl) {
try{
cl.resolveOptimally();
} catch (ClassNotFoundException cnfe){
ex[0] = cnfe;
throw new RuntimeException();
}
}
});
} catch (RuntimeException rte){
if (ex[0] != null){
throw ex[0];
} else {
throw rte;
}
}
}
/** Return a list of attributes marked to keep. */
public String[] getAttrsToKeep()
{
String[] attrs = new String[retainAttrs.size()];
for (int i = 0; i < attrs.length; i++)
{
attrs[i] = (String)retainAttrs.elementAt(i);
}
return attrs;
}
/** Get classes in tree from the fully qualified name
(can be wildcarded). */
public Enumeration getClEnum(String fullName)
{
return getClEnum(fullName, YGuardRule.LEVEL_PRIVATE);
}
/** Get classes in tree from the fully qualified name
(can be wildcarded). */
public Enumeration getClEnum(String fullName, final int classMode)
{
final Vector vec = new Vector();
// Wildcarded?
// Then return list of all classes (including inner classes) in package
if (fullName.indexOf('*') != -1) {
// Recursive?
if (fullName.indexOf('!') == 0) {
final String fName = fullName.substring(1);
walkTree(new TreeAction() {
public void classAction(Cl cl) {
if (cl.isWildcardMatch(fName) && modifierMatch(classMode, cl.getModifiers())) {
vec.addElement(cl);
}
}
});
}
else
{
// non-recursive
final String fName = fullName;
walkTree(new TreeAction() {
public void classAction(Cl cl) {
if (cl.isNRWildcardMatch(fName) && modifierMatch(classMode, cl.getModifiers())) {
vec.addElement(cl);
}
}
});
}
}
else
{
// Single class
Cl cl = getCl(fullName);
if (cl != null )
{
int mods = cl.getModifiers();
if (cl.isInnerClass()){
Cl outer = (Cl) cl.getParent();
}
boolean match = modifierMatch(classMode, cl.getModifiers());
if (match || classMode == YGuardRule.LEVEL_NONE ){ //(RW)
vec.addElement(cl);
}
}
}
return vec.elements();
}
/** Get methods in tree from the fully qualified, and possibly
wildcarded, name. */
public Enumeration getMdEnum(String fullName,
String descriptor)
{
final Vector vec = new Vector();
final String fDesc = descriptor;
if (fullName.indexOf('*') != -1 ||
descriptor.indexOf('*') != -1) {
// Recursive?
if (fullName.indexOf('!') == 0) {
final String fName = fullName.substring(1);
// recursive wildcarding
walkTree(new TreeAction() {
public void methodAction(Md md) {
if (md.isWildcardMatch(fName, fDesc)) {
vec.addElement(md);
}
}
});
}
else
{
final String fName = fullName;
// non-recursive wildcarding
walkTree(new TreeAction() {
public void methodAction(Md md) {
if (md.isNRWildcardMatch(fName, fDesc)) {
vec.addElement(md);
}
}
});
}
} else {
Md md = getMd(fullName, descriptor);
if (md != null) {
vec.addElement(md);
}
}
return vec.elements();
}
/** Get fields in tree from the fully qualified, and possibly
wildcarded, name. */
public Enumeration getFdEnum(String fullName)
{
final Vector vec = new Vector();
if (fullName.indexOf('*') != -1) {
// Recursive?
if (fullName.indexOf('!') == 0) {
// recursive wildcarding
final String fName = fullName.substring(1);
walkTree(new TreeAction() {
public void fieldAction(Fd fd) {
if (fd.isWildcardMatch(fName)) {
vec.addElement(fd);
}
}
});
}
else
{
// non-recursive wildcarding
final String fName = fullName;
walkTree(new TreeAction() {
public void fieldAction(Fd fd) {
if (fd.isNRWildcardMatch(fName)) {
vec.addElement(fd);
}
}
});
}
} else {
Fd fd = getFd(fullName);
if (fd != null) {
vec.addElement(fd);
}
}
return vec.elements();
}
/** Get class in tree from the fully qualified name, returning null if name not found. */
public Cl getCl(String fullName)
{
TreeItem ti = root;
for (Enumeration nameEnum = getNameEnum(fullName); nameEnum.hasMoreElements(); )
{
Cons nameSegment = (Cons)nameEnum.nextElement();
char tag = ((Character)nameSegment.car).charValue();
String name = (String)nameSegment.cdr;
switch (tag)
{
case PACKAGE_LEVEL:
ti = ((Pk)ti).getPackage(name);
break;
case CLASS_LEVEL:
ti = ((PkCl)ti).getClass(name);
break;
default:
throw new ParseException("Internal error: illegal package/class name tag");
}
// If the name is not in the database, return null
if (ti == null)
{
return null;
}
}
// It is an error if we do not end up with a class or interface
if (!(ti instanceof Cl))
{
throw new ParseException("Inconsistent class or interface name.");
}
return (Cl)ti;
}
/** Get package in tree from the fully qualified name, returning null if name not found. */
public Pk getPk(String fullName)
{
TreeItem ti = root;
for (Enumeration nameEnum = getNameEnum(fullName); nameEnum.hasMoreElements(); )
{
Cons nameSegment = (Cons)nameEnum.nextElement();
String name = (String)nameSegment.cdr;
ti = ((Pk)ti).getPackage(name);
// If the name is not in the database, return null
if (ti == null)
{
return null;
}
// It is an error if we do not end up with a package
if (!(ti instanceof Pk))
{
throw new ParseException("Inconsistent package.");
}
}
return (Pk)ti;
}
/** Get method in tree from the fully qualified name. */
public Md getMd(String fullName, String descriptor)
{
// Split into class and method names
int pos = fullName.lastIndexOf(METHOD_FIELD_LEVEL);
Cl cl = getCl(fullName.substring(0, pos));
return cl.getMethod(fullName.substring(pos + 1), descriptor);
}
/** Get field in tree from the fully qualified name. */
public Fd getFd(String fullName)
{
// Split into class and field names
int pos = fullName.lastIndexOf(METHOD_FIELD_LEVEL);
Cl cl = getCl(fullName.substring(0, pos));
return cl.getField(fullName.substring(pos + 1));
}
public String[] getAttrsToKeep(String className) {
Cl cl = getCl(className);
if (cl != null){
Set attrs = cl.getAttributesToKeep();
if (attrs != null && attrs.size() > 0){
String[] other = getAttrsToKeep();
Set tmp = new HashSet(attrs);
for (int i = 0; i < other.length; i++) {
tmp.add(other[i]);
}
return (String[]) tmp.toArray(new String[tmp.size()]);
} else {
return getAttrsToKeep();
}
} else {
return getAttrsToKeep();
}
}
public String mapLocalVariable(String thisClassName, String methodName, String descriptor, String string) {
return string;
}
/** Mapping for fully qualified class name.
* @see NameMapper#mapClass */
public String mapClass(String className)
{
// System.out.println("map class " + className);
// Check for array -- requires special handling
if (className.length() > 0 && className.charAt(0) == '[') {
StringBuffer newName = new StringBuffer();
int i = 0;
while (i < className.length()) {
char ch = className.charAt(i++);
switch (ch) {
case '[':
case ';':
newName.append(ch);
break;
case 'L':
newName.append(ch);
int pos = className.indexOf(';', i);
if (pos < 0) {
throw new ParseException("Invalid class name encountered: " + className);
}
newName.append(mapClass(className.substring(i, pos)));
i = pos;
break;
default:
return className;
}
}
return newName.toString();
} else {
Cl cl = getCl(className);
if (cl == null){
try {
Class aClass = Cl.getClassResolver().resolve(Conversion.toJavaClass(className));
// ok class exists...
return className;
} catch (ClassNotFoundException e) {
if (pedantic){
throw new NoSuchMappingException("Class "+Conversion.toJavaClass(className));
} else {
Logger.getInstance().warningToLogfile("Unresolved external dependency: "+Conversion.toJavaClass(className)+
" not found!");
Logger.getInstance().setUnresolved();
return className;
}
}
}
return cl.getFullOutName();
}
}
/** Mapping for method name, of fully qualified class.
* @see NameMapper#mapMethod */
public String mapMethod(String className, String methodName, String descriptor)
{
// check if the className is an array...
if (className.startsWith("[") && className.endsWith(";")){
int count = 0;
while (className.charAt(count) == '['){
count++;
}
if (className.charAt(count) == 'L'){
className = className.substring(count + 1, className.length() - 1);
}
}
Cl cl = getCl(className);
if (cl != null && cl.getMethod(methodName, descriptor) != null)
{
return cl.getMethod(methodName, descriptor).getOutName();
}
else
{
if (cl == null)
{
try {
Class aClass = Cl.getClassResolver().resolve(Conversion.toJavaClass(className));
} catch (ClassNotFoundException e) {
if (pedantic){
throw new NoSuchMappingException("Class "+Conversion.toJavaClass(className));
} else {
Logger.getInstance().warningToLogfile( "No mapping found: " + Conversion.toJavaClass( className ) );
}
}
// method is not in database use unobfuscated name...
return methodName;
}
else
{
try
{
// System.out.println("Try: "+cl.getFieldObfNameUp(fieldName));
String result = cl.getMethodOutNameUp(methodName,descriptor);
if (result != null)
return result;
}
catch (Exception ex)
{
System.out.println(ex);
//System.out.println("ME: Error: Try not succeeded");
}
if ((!methodName.equals("") &&
(!methodName.equals("")))){
if (pedantic){
throw new NoSuchMappingException("Method "+Conversion.toJavaClass(className)+"."+methodName);
} else {
Logger.getInstance().error("Method "+Conversion.toJavaClass(className)+"."+methodName+
" could not be mapped !\n Probably broken code! Try rebuilding from source!");
return methodName;
}
}
return methodName;
}
}
}
/** Mapping for annotation field/method name, of fully qualified class.
* @see NameMapper#mapAnnotationField */
public String mapAnnotationField(String className, String methodName)
{
Cl cl = getCl(className);
if (cl != null)
{
for (Enumeration enumeration = cl.getMethodEnum(); enumeration.hasMoreElements();){
Md md = (Md) enumeration.nextElement();
if (md.getInName().equals(methodName)){
return md.getOutName();
}
}
// actually this should not happen - is this an exception?!
return methodName;
}
else
{
// method is not in database use unobfuscated name...
return methodName;
}
}
/** Mapping for field name, of fully qualified class.
* @see NameMapper#mapField */
public String mapField(String className, String fieldName)
{
// System.out.println("Map "+className+"."+fieldName);
Cl cl = getCl(className);
if ((cl != null) && (cl.getField(fieldName) != null))
{
//special .class construct name mapping....
if (fieldName.startsWith("class$") && isReplaceClassNameStrings()){
String realClassName = fieldName.substring(6);
List nameParts = new ArrayList(20);
for (StringTokenizer st = new StringTokenizer(realClassName, "$", false); st.hasMoreTokens();){
nameParts.add(st.nextToken());
}
String[] names = new String[nameParts.size()];
nameParts.toArray(names);
TreeItem ti = findTreeItem(names);
if (ti instanceof Cl){
Fd fd = cl.getField(fieldName);
String newClassName = mapClass(ti.getFullInName());
String outName = "class$"+newClassName.replace('/','$');
fd.setOutName(outName);
return outName;
}
}
// System.out.println("Standard Field Map");
return cl.getField(fieldName).getOutName();
}
else
{
if (cl == null)
{
// System.out.println("Error: "+className+
// " class not found !");
return fieldName;
}
else
{
// System.out.println("ERROR: "+className+"."+fieldName+
// " cannot be mapped !");
try
{
// System.out.println("Try: "+cl.getFieldObfNameUp(fieldName));
String result = cl.getFieldOutNameUp(fieldName);
if (result != null)
return result;
}
catch (Exception ex)
{
// System.out.println("Try not succeeded");
}
if (!fieldName.equals("this")) {
if (pedantic){
throw new NoSuchMappingException("Field "+className+"."+fieldName);
} else {
Logger.getInstance().error("Field "+className+"."+fieldName+
" could not be mapped !\n Probably broken code! Try rebuilding from source!");
}
}
return fieldName;
}
}
// return cl != null && cl.getField(fieldName) != null ?
// cl.getField(fieldName).getOutName() :
// fieldName;
}
/** Mapping for signatures (used for generics in 1.5).
* @see NameMapper#mapSignature
*/
public String mapSignature(String signature){
// Pass everything through unchanged, except for the String between
// 'L' and ';' -- this is passed through mapClass(String)
// System.out.println( "signature: "+signature );
StringBuffer classString = new StringBuffer();
StringBuffer newSignature = new StringBuffer();
int i = 0;
while (i < signature.length())
{
char ch = signature.charAt(i++);
switch (ch)
{
case '[':
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 'V':
case '(':
case ')':
case '+':
case ':':
case '-':
case '*':
newSignature.append(ch);
break;
case ';':
newSignature.append(ch);
classString.setLength( 0 );
break;
case 'T':
{ // Template name
newSignature.append(ch);
int pos = signature.indexOf(';', i);
if (pos < 0)
{
throw new ParseException("Invalid signature string encountered.");
}
newSignature.append(signature.substring(i, pos));
i = pos;
break;
}
case '<':
{
// formal parameters
newSignature.append(ch);
while (true){
int first = i;
while (signature.charAt(i) != ':'){
i++;
}
String templateName = signature.substring(first, i);
newSignature.append(templateName);
while (signature.charAt(i) == ':'){
newSignature.append(':');
i++;
int firstPos = i;
int bracketCount = 0;
while (!(bracketCount == 0 && signature.charAt(i) == ';')){
if (signature.charAt(i) == '<') {
bracketCount++;
} else if (signature.charAt(i) == '>'){
bracketCount--;
}
i++;
}
i++;
newSignature.append(mapSignature(signature.substring(firstPos, i)));
}
if (signature.charAt(i) == '>'){
newSignature.append('>');
i++;
break;
}
}
break;
}
case '^':
{
newSignature.append(ch);
if (signature.charAt(i) == 'T'){
// identifier
while (signature.charAt(i) != ';'){
newSignature.append(signature.charAt(i));
i++;
}
continue;
} else if (signature.charAt(i) == 'L'){
// class
int first = i;
int bracketCount = 0;
while (signature.charAt(i) != ';' || bracketCount != 0){
char c = signature.charAt(i);
if (c == '<'){
bracketCount++;
} else if (c == '>'){
bracketCount--;
}
i++;
}
i++;
String classSig = signature.substring(first, i);
newSignature.append(mapSignature(classSig));
} else {
throw new IllegalStateException("Could not map signature " + signature);
}
}
break;
case 'L':
case '.': // inner class
{
newSignature.append(ch);
int pos = signature.indexOf(';', i);
int bracketPos = signature.indexOf('<', i);
if (bracketPos >= i && bracketPos < pos){
// found a bracket - find the matching one..
int bracketCount = 0;
int closingBracket = signature.length();
for (int walker = bracketPos + 1; walker < signature.length(); walker++){
char c = signature.charAt(walker);
if (c == '<'){
bracketCount++;
} else if (c == '>'){
if (bracketCount == 0){
closingBracket = walker;
break;
} else {
bracketCount--;
}
}
}
// TODO check!!!!
pos = closingBracket + 1;
// pos = signature.indexOf(';', closingBracket);
String templateArg = signature.substring(bracketPos + 1, closingBracket);
String classNamePart = signature.substring( i, bracketPos );
if (ch == '.'){ // inner class part - translate to class file name
appendInnerClass(classString, newSignature, classNamePart);
} else { // toplevel class 'L'
classString.append( classNamePart );
newSignature.append(mapClass( classString.toString() ));
}
newSignature.append('<');
newSignature.append(mapSignature(templateArg));
newSignature.append('>');
i = pos;
} else {
// no generics to parse
if (pos < 0)
{
throw new ParseException("Invalid signature string encountered: " + signature);
}
String classNamePart = signature.substring( i, pos );
if (ch == '.'){ // inner class part - translate to class file name
appendInnerClass(classString,newSignature,classNamePart);
} else {
classString.append( classNamePart );
newSignature.append(mapClass( classString.toString() ));
}
i = pos;
}
break;
}
default:
throw new ParseException("Invalid signature string encountered: " +signature + " parsing char " + ch);
}
}
return newSignature.toString();
}
private void appendInnerClass(StringBuffer classString, StringBuffer newSignature, String classNamePart) {
classString.append( '$' );
classString.append( classNamePart );
String className = classString.toString();
// do basically the same that mapClass() does, but return the last part only.
String result = getClassNamePart(classNamePart, className);
newSignature.append(result);
}
private String getClassNamePart(String classNamePart, String className) {
int j = className.indexOf(classNamePart);
if( classNamePart.indexOf('.') != -1 && j > 0 ) { // nested inner classes?
// e.g. classNamePart: a.b, className: g/h/j$a.b
String outerClassName = className.substring(0,j-1);
String retval = "";
String currentClassName = outerClassName;
StringBuilder innerClassName = new StringBuilder();
for( int i=0; i 0 ) {
retval = retval + '.';
}
retval = retval + cl.getOutName();
} else {
try {
Class aClass = Cl.getClassResolver().resolve(Conversion.toJavaClass(currentClassName));
// ok class exists...
retval = retval + "." + currentClassName;
} catch (ClassNotFoundException e) {
if (pedantic){
throw new NoSuchMappingException("Class "+Conversion.toJavaClass(currentClassName));
} else {
Logger.getInstance().warningToLogfile("Unresolved external dependency: "+Conversion.toJavaClass(currentClassName)+
" not found!");
Logger.getInstance().setUnresolved();
retval = retval + "." + currentClassName;
}
}
}
return retval;
}
public String mapSourceFile(String className, String sourceFileName){
final Cl cl = getCl(className);
if (cl.isSourceFileMappingSet()){
return cl.getSourceFileMapping();
} else {
return sourceFileName;
}
}
public boolean mapLineNumberTable(String className, String methodName, String methodSignature, LineNumberTableAttrInfo info) {
final Cl cl = getCl(className);
if (cl.getLineNumberTableMapper() != null){
return cl.getLineNumberTableMapper().mapLineNumberTable(className, methodName, methodSignature, info);
} else {
return true;
}
}
/**
* Mapping for descriptor of field or method.
* @see NameMapper#mapDescriptor
*/
public String mapDescriptor(String descriptor)
{
// Pass everything through unchanged, except for the String between
// 'L' and ';' -- this is passed through mapClass(String)
StringBuffer newDesc = new StringBuffer();
int i = 0;
while (i < descriptor.length())
{
char ch = descriptor.charAt(i++);
switch (ch)
{
case '[':
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 'V':
case '(':
case ')':
case ';':
newDesc.append(ch);
break;
case 'L':
newDesc.append(ch);
int pos = descriptor.indexOf(';', i);
if (pos < 0)
{
throw new ParseException("Invalid descriptor string encountered.");
}
newDesc.append(mapClass(descriptor.substring(i, pos)));
i = pos;
break;
default:
throw new ParseException("Invalid descriptor string encountered.");
}
}
return newDesc.toString();
}
/**
* Mapping for package names.
* @see NameMapper#mapPackage(String)
*/
public String mapPackage( final String packageName ) {
final Pk pk = getPk(packageName);
return pk == null ? packageName : pk.getFullOutName();
}
/** Dump the content of the class tree to the specified file (used for logging). */
public void dump(final PrintWriter log)
{
log.println("");
walkTree(new TreeAction() {
public void classAction(Cl cl) {
final String name = cl.getFullInName();
if (cl.isFromScript() && !"module-info".equals(name)) {
String cla = toUtf8XmlString(Conversion.toJavaClass(name));
log.println(" ");
}
}
public void methodAction(Md md) {
if (md.isFromScript()) {
String cla = toUtf8XmlString(Conversion.toJavaClass(md.getParent().getFullInName()));
String method = toUtf8XmlString(Conversion.toJavaMethod(md.getInName(), md.getDescriptor()));
log.println(" ");
}
}
public void fieldAction(Fd fd) {
if (fd.isFromScript()) {
String cla = toUtf8XmlString(Conversion.toJavaClass(fd.getParent().getFullInName()));
log.println(" ");
}
}
public void packageAction(Pk pk) {
// No action
}
});
log.println(" ");
log.println("");
}
public static final String toUtf8XmlString(String s){
boolean bad = false;
for (int i = 0; i< s.length(); i++){
char c = s.charAt(i);
if ((c >= 0x80) || (c =='"') || (c == '<')){
bad = true;
break;
}
}
if (bad){
StringBuffer buf = new StringBuffer(s.length());
for (int i = 0; i < s.length(); i++){
buf.append(toUtf8XmlChar(s.charAt(i)));
}
return buf.toString();
} else {
return s;
}
}
private static final String toUtf8XmlChar(char c){
if (c < 0x80){
if (c == '"'){
return """;
} else if (c == '<'){
return "<";
}
return new String(new char[]{c});
}
else if (c < 0x800)
{
StringBuffer buf = new StringBuffer(8);
buf.append("");
buf.append(hex[(c >> 8) & 0xff]);
buf.append(hex[c & 0xff]);
buf.append(';');
return buf.toString();
}
else
{
StringBuffer buf = new StringBuffer(10);
buf.append("");
buf.append(hex[(c >> 16) & 0xff]);
buf.append(hex[(c >> 8) & 0xff]);
buf.append(hex[c & 0xff]);
buf.append(';');
return buf.toString();
}
}
private static final String[] hex;
static {
hex = new String[256];
for (int i = 0; i < 256; i++){
hex[i] = toHex(i);
}
}
private static final String hexChars = "0123456789abcdef";
/** Holds value of property replaceClassNameStrings. */
private boolean replaceClassNameStrings;
/** Holds value of property pedantic. */
private boolean pedantic;
private static String toHex(int i){
StringBuffer buf = new StringBuffer(2);
buf.append(hexChars.charAt((i/16)&15));
buf.append(hexChars.charAt(i&15));
return buf.toString();
}
// Private Methods -------------------------------------------------------
// Mark TreeItem and all parents for retention.
private void retainHierarchy(TreeItem ti)
{
if (!ti.isFixed())
{
ti.setOutName(ti.getInName());
ti.setFromScript();
}
if (ti.parent != null)
{
retainHierarchy(ti.parent);
}
}
/** Walk the whole tree taking action once only on each package level, class, method and field. */
public void walkTree(TreeAction ta)
{
walkTree(ta, root);
}
// Walk the tree which has TreeItem as its root taking action once only on each
// package level, class, method and field.
private void walkTree(TreeAction ta, TreeItem ti)
{
if (ti instanceof Pk)
{
Enumeration packageEnum = ((Pk)ti).getPackageEnum();
ta.packageAction((Pk)ti);
while (packageEnum.hasMoreElements())
{
walkTree(ta, (TreeItem)packageEnum.nextElement());
}
}
if (ti instanceof PkCl)
{
Enumeration classEnum = ((PkCl)ti).getClassEnum();
while (classEnum.hasMoreElements())
{
walkTree(ta, (TreeItem)classEnum.nextElement());
}
}
if (ti instanceof Cl)
{
Enumeration fieldEnum = ((Cl)ti).getFieldEnum();
Enumeration methodEnum = ((Cl)ti).getMethodEnum();
ta.classAction((Cl)ti);
while (fieldEnum.hasMoreElements())
{
ta.fieldAction((Fd)fieldEnum.nextElement());
}
while (methodEnum.hasMoreElements())
{
ta.methodAction((Md)methodEnum.nextElement());
}
}
}
/** Getter for property replaceClassNameStrings.
* @return Value of property replaceClassNameStrings.
*
*/
public boolean isReplaceClassNameStrings()
{
return this.replaceClassNameStrings;
}
/** Setter for property replaceClassNameStrings.
* @param replaceClassNameStrings New value of property replaceClassNameStrings.
*
*/
public void setReplaceClassNameStrings(boolean replaceClassNameStrings)
{
this.replaceClassNameStrings = replaceClassNameStrings;
}
/** Getter for property pedantic.
* @return Value of property pedantic.
*
*/
public boolean isPedantic()
{
return this.pedantic;
}
/** Setter for property pedantic.
* @param pedantic New value of property pedantic.
*
*/
public void setPedantic(boolean pedantic)
{
this.pedantic = pedantic;
}
public void retainSourceFileAttributeMap(String name, String obfName) {
for (Enumeration clEnum = getClEnum(name); clEnum.hasMoreElements(); )
{
Cl classItem = (Cl)clEnum.nextElement();
classItem.setSourceFileMapping(obfName);
classItem.getAttributesToKeep().add(ClassConstants.ATTR_SourceFile);
}
}
public void retainLineNumberTable(String name, final LineNumberTableMapper lineNumberTableMapper) {
for (Enumeration clEnum = getClEnum(name); clEnum.hasMoreElements(); )
{
Cl classItem = (Cl)clEnum.nextElement();
classItem.setLineNumberTableMapper(lineNumberTableMapper);
classItem.getAttributesToKeep().add(ClassConstants.ATTR_LineNumberTable);
}
}
public void retainAttributeForClass(String className, String attributeDescriptor) {
for (Enumeration clEnum = getClEnum(className); clEnum.hasMoreElements(); )
{
Cl classItem = (Cl)clEnum.nextElement();
final Set set = classItem.getAttributesToKeep();
set.add(attributeDescriptor);
}
}
public void retainPackage(String packageName) {
retainHierarchy(getPk(packageName));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy