com.yworks.yguard.obf.classfile.ClassFile 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.classfile;
import java.io.*;
import java.util.*;
import com.yworks.yguard.obf.*;
import java.lang.reflect.Modifier;
import com.yworks.yguard.Conversion;
import com.yworks.yguard.ParseException;
/**
* This is a representation of the data in a Java class-file (*.class).
* A ClassFile instance representing a *.class file can be generated
* using the static create(DataInput) method, manipulated using various
* operators, and persisted back using the write(DataOutput) method.
*
* @author Mark Welsh
*/
public class ClassFile implements ClassConstants
{
// Constants -------------------------------------------------------------
public static final String SEP_REGULAR = "/";
public static final String SEP_INNER = "$";
public static final String LOG_DANGER_HEADER1 = "Methods are called which may break in obfuscated version at runtime.";
public static final String LOG_DANGER_HEADER2 = "Please review your source code to ensure that the dangerous methods are not intended";
public static final String LOG_DANGER_HEADER3 = "to act on classes which have been obfuscated.";
private static final String[] SEMI_DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY = {
"forName(Ljava/lang/String;)Ljava/lang/Class;",
"forName(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;",
};
private static final String[] DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY = {
"forName(Ljava/lang/String;)Ljava/lang/Class;",
"forName(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;",
"getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
"getField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
"getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
"getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"
};
private static final String LOG_DANGER_CLASS_PRE = " Your class ";
private static final String LOG_DANGER_CLASS_MID = " calls the java.lang.Class method ";
private static final String[] DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY = {
"defineClass(Ljava/lang/String;[BII)Ljava/lang/Class;",
"findLoadedClass(Ljava/lang/String;)Ljava/lang/Class;",
"findSystemClass(Ljava/lang/String;)Ljava/lang/Class;",
"loadClass(Ljava/lang/String;)Ljava/lang/Class;",
"loadClass(Ljava/lang/String;Z)Ljava/lang/Class;"
};
private static final String LOG_DANGER_CLASSLOADER_PRE = " Your class ";
private static final String LOG_DANGER_CLASSLOADER_MID = " calls the java.lang.ClassLoader method ";
/**
* {@link java.lang.invoke.StringConcatFactory} method.
*/
private static final int BM_TYPE_SCF = 2;
/**
* {@link java.lang.invoke.java.lang.invoke.LambdaMetafactory} method.
*/
private static final int BM_TYPE_LMF = 1;
/**
* Unknown bootstrap method, throw exception.
*/
private static final int BM_TYPE_UNKNOWN = 0;
// Fields ----------------------------------------------------------------
private int u4magic;
private int u2minorVersion;
private int u2majorVersion;
private ConstantPool constantPool;
private int u2accessFlags;
private int u2thisClass;
private int u2superClass;
private int u2interfacesCount;
private int u2interfaces[];
private int u2fieldsCount;
private FieldInfo fields[];
private int u2methodsCount;
private MethodInfo methods[];
private int u2attributesCount;
private AttrInfo attributes[];
private boolean isUnkAttrGone = false;
private static boolean writeIdString = false;
private static CpInfo cpIdString = null;
// Class Methods ---------------------------------------------------------
/**
* Define a constant String to include in every output class file.
*/
public static void defineIdString(String id)
{
if (id != null) {
writeIdString = true;
cpIdString = new Utf8CpInfo(id);
} else {
writeIdString = false;
cpIdString = null;
}
}
/**
* Create a new ClassFile from the class file format data in the DataInput
* stream.
*
* @throws IOException if class file is corrupt or incomplete
*/
public static ClassFile create(DataInput din) throws java.io.IOException
{
if (din == null) throw new NullPointerException("No input stream was provided.");
ClassFile cf = new ClassFile();
cf.read(din);
return cf;
}
/** Parse a method or field descriptor into a list of parameter names (for methods)
* and a return type, in same format as the Class.forName() method returns . */
public static String[] parseDescriptor(String descriptor)
{
return parseDescriptor(descriptor, false);
}
/** Parse a method or field descriptor into a list of parameter names (for methods)
* and a return type, in same format as the Class.forName() method returns . */
public static String[] parseDescriptor(String descriptor, boolean isDisplay)
{
// Check for field descriptor
String[] names = null;
if (descriptor.charAt(0) != '(')
{
names = new String[1];
names[0] = descriptor;
}
else
{
// Method descriptor
Vector namesVec = new Vector();
descriptor = descriptor.substring(1);
String type = "";
while (descriptor.length() > 0)
{
switch (descriptor.charAt(0))
{
case '[':
type = type + "[";
descriptor = descriptor.substring(1);
break;
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 'V':
namesVec.addElement(type + descriptor.substring(0, 1));
descriptor = descriptor.substring(1);
type = "";
break;
case ')':
descriptor = descriptor.substring(1);
break;
case 'L':
{
int pos = descriptor.indexOf(';') + 1;
namesVec.addElement(type + descriptor.substring(0, pos));
descriptor = descriptor.substring(pos);
type = "";
}
break;
default:
throw new IllegalArgumentException("Illegal field or method descriptor: " + descriptor);
}
}
names = new String[namesVec.size()];
for (int i = 0; i < names.length; i++)
{
names[i] = (String)namesVec.elementAt(i);
}
}
// Translate the names from JVM to Class.forName() format.
String[] translatedNames = new String[names.length];
for (int i = 0; i < names.length; i++)
{
translatedNames[i] = translateType(names[i], isDisplay);
}
return translatedNames;
}
/** Translate a type specifier from the internal JVM convention to the Class.forName() one. */
public static String translateType(String inName, boolean isDisplay)
{
String outName = null;
switch (inName.charAt(0))
{
case '[': // For array types, Class.forName() inconsistently uses the internal type name
// but with '/' --> '.'
if (!isDisplay)
{
// return the Class.forName() form
outName = translate(inName);
}
else
{
// return the pretty display form
outName = translateType(inName.substring(1), true) + "[]";
}
break;
case 'B':
outName = Byte.TYPE.getName();
break;
case 'C':
outName = Character.TYPE.getName();
break;
case 'D':
outName = Double.TYPE.getName();
break;
case 'F':
outName = Float.TYPE.getName();
break;
case 'I':
outName = Integer.TYPE.getName();
break;
case 'J':
outName = Long.TYPE.getName();
break;
case 'S':
outName = Short.TYPE.getName();
break;
case 'Z':
outName = Boolean.TYPE.getName();
break;
case 'V':
outName = Void.TYPE.getName();
break;
case 'L':
{
int pos = inName.indexOf(';');
outName = translate(inName.substring(1, inName.indexOf(';')));
}
break;
default:
throw new IllegalArgumentException("Illegal field or method name: " + inName);
}
return outName;
}
/** Translate a class name from the internal '/' convention to the regular '.' one. */
public static String translate(String name)
{
return name.replace('/', '.');
}
// Instance Methods ------------------------------------------------------
// Private constructor.
private ClassFile() {}
// Import the class data to internal representation.
private void read(DataInput din) throws java.io.IOException
{
// Read the class file
u4magic = din.readInt();
u2minorVersion = din.readUnsignedShort();
u2majorVersion = din.readUnsignedShort();
// Check this is a valid classfile that we can handle
if (u4magic != MAGIC)
{
throw new IOException("Invalid magic number in class file.");
}
if (u2majorVersion > MAJOR_VERSION)
{
throw new IOException("Incompatible version number for class file format: " + u2majorVersion+"."+u2minorVersion);
}
int u2constantPoolCount = din.readUnsignedShort();
CpInfo[] cpInfo = new CpInfo[u2constantPoolCount];
// Fill the constant pool, recalling the zero entry
// is not persisted, nor are the entries following a Long or Double
for (int i = 1; i < u2constantPoolCount; i++)
{
cpInfo[i] = CpInfo.create(din);
if ((cpInfo[i] instanceof LongCpInfo) ||
(cpInfo[i] instanceof DoubleCpInfo))
{
i++;
}
// todo: remove once remapping CONSTANT_Dynamic_info is supported
else if (cpInfo[i] instanceof DynamicCpInfo) {
throw new IOException("Unsupported tag type in constant pool: dynamic");
}
}
constantPool = new ConstantPool(this, cpInfo);
u2accessFlags = din.readUnsignedShort();
u2thisClass = din.readUnsignedShort();
u2superClass = din.readUnsignedShort();
u2interfacesCount = din.readUnsignedShort();
u2interfaces = new int[u2interfacesCount];
for (int i = 0; i < u2interfacesCount; i++)
{
u2interfaces[i] = din.readUnsignedShort();
}
u2fieldsCount = din.readUnsignedShort();
fields = new FieldInfo[u2fieldsCount];
for (int i = 0; i < u2fieldsCount; i++)
{
fields[i] = FieldInfo.create(din, this);
}
u2methodsCount = din.readUnsignedShort();
methods = new MethodInfo[u2methodsCount];
for (int i = 0; i < u2methodsCount; i++)
{
methods[i] = MethodInfo.create(din, this);
}
u2attributesCount = din.readUnsignedShort();
attributes = new AttrInfo[u2attributesCount];
for (int i = 0; i < u2attributesCount; i++)
{
attributes[i] = AttrInfo.create(din, this);
}
}
public int getClassFileAccess(){
return u2accessFlags;
}
public int getModifiers(){
int mods = 0;
if ((u2accessFlags & 0x0001) == 0x0001) mods |= Modifier.PUBLIC;
if ((u2accessFlags & 0x0010) == 0x0010) mods |= Modifier.FINAL;
if ((u2accessFlags & 0x0200) == 0x0200) mods |= Modifier.INTERFACE;
if ((u2accessFlags & 0x0400) == 0x0400) mods |= Modifier.ABSTRACT;
return mods;
}
/** Return the name of this classfile. */
public String getName()
{
return toName(u2thisClass);
}
/** Return the name of this class's superclass. */
public String getSuper()
{
// This may be java/lang/Object, in which case there is no super
return (u2superClass == 0) ? null : toName(u2superClass);
}
/** Return the names of this class's interfaces. */
public String[] getInterfaces()
{
String[] interfaces = new String[u2interfacesCount];
for (int i = 0; i < u2interfacesCount; i++)
{
interfaces[i] = toName(u2interfaces[i]);
}
return interfaces;
}
// Convert a CP index to a class name.
private String toName(int u2index)
{
CpInfo classEntry = getCpEntry(u2index);
if (classEntry instanceof ClassCpInfo)
{
CpInfo nameEntry = getCpEntry(((ClassCpInfo)classEntry).getNameIndex());
if (nameEntry instanceof Utf8CpInfo)
{
return ((Utf8CpInfo)nameEntry).getString();
}
else
{
throw new ParseException("Inconsistent Constant Pool in class file.");
}
}
else
{
throw new ParseException("Inconsistent Constant Pool in class file.");
}
}
/** Return an enumeration of method name/descriptor pairs. */
public Enumeration getMethodEnum()
{
Vector vec = new Vector();
for (int i = 0; i < methods.length; i++)
{
vec.addElement(methods[i]);
}
return vec.elements();
}
/** Return an enumeration of field name/descriptor pairs. */
public Enumeration getFieldEnum()
{
Vector vec = new Vector();
for (int i = 0; i < fields.length; i++)
{
vec.addElement(fields[i]);
}
return vec.elements();
}
/** Lookup the entry in the constant pool and return as an Object. */
public CpInfo getCpEntry(int cpIndex)
{
return constantPool.getCpEntry(cpIndex);
}
private String getUtf8(int cpIndex) {
return ((Utf8CpInfo) getCpEntry(cpIndex)).getString();
}
public ConstantPool getConstantPool() {
return constantPool;
}
/** Check for methods which can break the obfuscated code, and log them to a String[]. */
public String[] logDangerousMethods(boolean replaceClassNameStrings)
{
Vector warningVec = new Vector();
// Need only check CONSTANT_Methodref entries of constant pool since
// dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
for (Enumeration enumeration = constantPool.elements(); enumeration.hasMoreElements(); )
{
Object o = enumeration.nextElement();
if (o instanceof MethodrefCpInfo)
{
// Get the method class name, simple name and descriptor
MethodrefCpInfo entry = (MethodrefCpInfo)o;
ClassCpInfo classEntry = (ClassCpInfo)getCpEntry(entry.getClassIndex());
String className = ((Utf8CpInfo)getCpEntry(classEntry.getNameIndex())).getString();
NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo)getCpEntry(entry.getNameAndTypeIndex());
String name = ((Utf8CpInfo)getCpEntry(ntEntry.getNameIndex())).getString();
String descriptor = ((Utf8CpInfo)getCpEntry(ntEntry.getDescriptorIndex())).getString();
// Check if this is on the proscribed list
if (className.equals("java/lang/Class") &&
Tools.isInArray(name + descriptor, DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY))
{
if (replaceClassNameStrings){
if (!Tools.isInArray(name+descriptor, SEMI_DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY)){
String jMethod = Conversion.toJavaMethod(name, descriptor);
warningVec.addElement(LOG_DANGER_CLASS_PRE + Conversion.toJavaClass(getName()) + LOG_DANGER_CLASS_MID + jMethod);
}
}
}
else if (Tools.isInArray(name + descriptor, DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY))
{
String jMethod = Conversion.toJavaMethod(name, descriptor);
warningVec.addElement(LOG_DANGER_CLASSLOADER_PRE + Conversion.toJavaClass(getName()) + LOG_DANGER_CLASSLOADER_MID + jMethod);
} else if ("class$(Ljava/lang/String;)Ljava/lang/Class;".equals(name+descriptor)){
if (!replaceClassNameStrings){
warningVec.addElement(LOG_DANGER_CLASS_PRE + Conversion.toJavaClass(getName()) +" seems to be using the '.class' construct!");
}
}
}
}
// Copy any warnings to a String[]
String[] warnings = new String[warningVec.size()];
for (int i = 0; i < warnings.length; i++)
{
warnings[i] = (String)warningVec.elementAt(i);
}
return warnings;
}
/** Check for methods which can break the obfuscated code, and log them. */
private static boolean hasHeader = false;
public static void resetDangerHeader() {hasHeader = false;}
public void logDangerousMethods(PrintWriter log, boolean replaceClassNameStrings)
{
// Get any warnings and print them to the logfile
String[] warnings = logDangerousMethods(replaceClassNameStrings);
if (warnings != null && warnings.length > 0)
{
if (!hasHeader)
{
log.println("");
hasHeader = true;
}
if (warnings.length > 0){
log.println("");
}
}
}
/** Check for direct references to Utf8 constant pool entries. */
public void markUtf8Refs(ConstantPool pool)
{
try
{
// Check for references to Utf8 from outside the constant pool
for (int i = 0; i < fields.length; i++)
{
fields[i].markUtf8Refs(pool);
}
for (int i = 0; i < methods.length; i++)
{
methods[i].markUtf8Refs(pool); // also checks Code/LVT attrs here
}
for (int i = 0; i < attributes.length; i++)
{
attributes[i].markUtf8Refs(pool); // checks InnerClasses, SourceFile and all attr names
}
// Now check for references from other CP entries
for (Enumeration enumeration = pool.elements(); enumeration.hasMoreElements(); )
{
Object o = enumeration.nextElement();
if (o instanceof NameAndTypeCpInfo ||
o instanceof AbstractTypeCpInfo ||
o instanceof MethodTypeCpInfo ||
o instanceof StringCpInfo)
{
((CpInfo)o).markUtf8Refs(pool);
}
}
}
catch (ArrayIndexOutOfBoundsException e)
{
throw new ParseException("Inconsistent reference to constant pool.");
}
}
/** Check for direct references to NameAndType constant pool entries. */
public void markNTRefs(ConstantPool pool)
{
try
{
// Now check the method and field CP entries
for (Enumeration enumeration = pool.elements(); enumeration.hasMoreElements(); )
{
Object o = enumeration.nextElement();
if (o instanceof RefCpInfo ||
o instanceof AbstractDynamicCpInfo)
{
((CpInfo)o).markNTRefs(pool);
}
}
}
catch (ArrayIndexOutOfBoundsException e)
{
throw new ParseException("Inconsistent reference to constant pool.");
}
}
/**
* Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
* are preserved, all others except the list in the String[] are killed).
*/
public void trimAttrsExcept(String[] extraAttrs)
{
// Merge additional attributes with required list
String[] keepAttrs = REQUIRED_ATTRS;
if (extraAttrs != null && extraAttrs.length > 0)
{
String[] tmp = new String[keepAttrs.length + extraAttrs.length];
System.arraycopy(keepAttrs, 0, tmp, 0, keepAttrs.length);
System.arraycopy(extraAttrs, 0, tmp, keepAttrs.length, extraAttrs.length);
keepAttrs = tmp;
}
// Traverse all attributes, removing all except those on 'keep' list
for (int i = 0; i < fields.length; i++)
{
fields[i].trimAttrsExcept(keepAttrs);
}
for (int i = 0; i < methods.length; i++)
{
methods[i].trimAttrsExcept(keepAttrs);
}
for (int i = 0; i < attributes.length; i++)
{
if (Tools.isInArray(attributes[i].getAttrName(), keepAttrs))
{
attributes[i].trimAttrsExcept(keepAttrs);
}
else
{
attributes[i] = null;
}
}
// Delete the marked attributes
AttrInfo[] left = new AttrInfo[attributes.length];
int j = 0;
for (int i = 0; i < attributes.length; i++)
{
if (attributes[i] != null)
{
left[j++] = attributes[i];
}
}
attributes = new AttrInfo[j];
System.arraycopy(left, 0, attributes, 0, j);
u2attributesCount = j;
// Signal that unknown attributes are gone
isUnkAttrGone = true;
// Update the constant pool reference counts
constantPool.updateRefCount();
}
public Map getInnerClassModifiers() {
Map map = new HashMap();
for (int i = 0; i < u2attributesCount; i++)
{
AttrInfo attrInfo = attributes[i];
if (attrInfo instanceof InnerClassesAttrInfo)
{
InnerClassesInfo[] info = ((InnerClassesAttrInfo)attrInfo).getInfo();
for (int j = 0; j < info.length; j++)
{
InnerClassesInfo ici = info[j];
int index = info[j].getInnerNameIndex();
if (index == 0){ // unnamed inner class
continue;
}
CpInfo cpInfo = getCpEntry(info[j].getInnerNameIndex());
if (cpInfo instanceof Utf8CpInfo)
{
Utf8CpInfo utf = (Utf8CpInfo)cpInfo;
String origClass = utf.getString();
map.put(origClass, new Integer(ici.getModifiers()));
}
}
}
}
return map;
}
/**
* Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
* are preserved, all others are killed).
*/
public void trimAttrs() {trimAttrsExcept(null);}
private boolean containsDotClassMethodReference(){
// Need only check CONSTANT_Methodref entries of constant pool since
// dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
for (Enumeration enumeration = constantPool.elements(); enumeration.hasMoreElements(); )
{
Object o = enumeration.nextElement();
if (o instanceof MethodrefCpInfo)
{
// Get the method class name, simple name and descriptor
MethodrefCpInfo entry = (MethodrefCpInfo)o;
ClassCpInfo classEntry = (ClassCpInfo)getCpEntry(entry.getClassIndex());
String className = ((Utf8CpInfo)getCpEntry(classEntry.getNameIndex())).getString();
NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo)getCpEntry(entry.getNameAndTypeIndex());
String name = ((Utf8CpInfo)getCpEntry(ntEntry.getNameIndex())).getString();
if (name.equals("class$")){
String descriptor = ((Utf8CpInfo)getCpEntry(ntEntry.getDescriptorIndex())).getString();
if (descriptor.equals("(Ljava/lang/String;)Ljava/lang/Class;")){
return true;
}
}
}
}
return false;
}
private boolean containsClassMethodReference(String cName, String des){
// Need only check CONSTANT_Methodref entries of constant pool since
// dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
for (Enumeration enumeration = constantPool.elements(); enumeration.hasMoreElements(); )
{
Object o = enumeration.nextElement();
if (o instanceof MethodrefCpInfo)
{
// Get the method class name, simple name and descriptor
MethodrefCpInfo entry = (MethodrefCpInfo)o;
ClassCpInfo classEntry = (ClassCpInfo)getCpEntry(entry.getClassIndex());
String className = ((Utf8CpInfo)getCpEntry(classEntry.getNameIndex())).getString();
NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo)getCpEntry(entry.getNameAndTypeIndex());
String name = ((Utf8CpInfo)getCpEntry(ntEntry.getNameIndex())).getString();
String descriptor = ((Utf8CpInfo)getCpEntry(ntEntry.getDescriptorIndex())).getString();
// Check if this is on the proscribed list
if (className.equals(cName) && (name+descriptor).equals(des)){
return true;
}
}
}
return false;
}
/** Remap the entities in the specified ClassFile. */
public void remap(NameMapper nm, boolean replaceClassNameStrings, PrintWriter log)
{
// Remap all the package/interface/class/method/field names
//
String thisClassName = ((Utf8CpInfo)getCpEntry(((ClassCpInfo)getCpEntry(u2thisClass)).getNameIndex())).getString();
// Remove unnecessary attributes from the class
final String[] attributesToKeep = nm.getAttrsToKeep(thisClassName);
if (attributesToKeep.length > 0)
{
trimAttrsExcept(attributesToKeep);
}
else
{
trimAttrs();
}
// Remap the 'inner name' reference of the 'InnerClasses' attribute
for (int i = 0; i < u2attributesCount; i++)
{
AttrInfo attrInfo = attributes[i];
if (attrInfo instanceof RuntimeVisibleAnnotationsAttrInfo){
remapAnnotations((RuntimeVisibleAnnotationsAttrInfo)attrInfo, nm);
} else if (attrInfo instanceof InnerClassesAttrInfo) {
// For each inner class referemce,
InnerClassesInfo[] info = ((InnerClassesAttrInfo)attrInfo).getInfo();
for (int j = 0; j < info.length; j++)
{
// Get the 'inner name' (it is a CONSTANT_Utf8)
CpInfo cpInfo = getCpEntry(info[j].getInnerNameIndex());
if (cpInfo instanceof Utf8CpInfo)
{
// Get the remapped class name
Utf8CpInfo utf = (Utf8CpInfo)cpInfo;
String origClass = utf.getString();
// Only remap non-anonymous classes (anon are "")
if (!origClass.equals(""))
{
// Get the full inner class name
ClassCpInfo innerClassInfo = (ClassCpInfo)getCpEntry(info[j].getInnerClassIndex());
String innerClassName = ((Utf8CpInfo)getCpEntry(innerClassInfo.getNameIndex())).getString();
// It is the remapped simple name that must be stored, so truncate it
String remapClass = nm.mapClass(innerClassName);
remapClass = remapClass.substring(remapClass.lastIndexOf('$') + 1);
int remapIndex = constantPool.remapUtf8To(remapClass, info[j].getInnerNameIndex());
info[j].setInnerNameIndex(remapIndex);
}
}
}
} else if (attrInfo instanceof EnclosingMethodAttrInfo){
EnclosingMethodAttrInfo eam = (EnclosingMethodAttrInfo) attrInfo;
// get the class name of the enclosing file:
CpInfo cpi = getCpEntry(eam.getClassIndex());
if (cpi instanceof ClassCpInfo){
ClassCpInfo ccpi = (ClassCpInfo) cpi;
cpi = getCpEntry(ccpi.getNameIndex());
if (cpi instanceof Utf8CpInfo){
Utf8CpInfo utf = (Utf8CpInfo) cpi;
String origClass = utf.getString();
// do not remap the ClassCpInfo now, it will be remapped automatically, later!
String remapClass = nm.mapClass(origClass);
// if NT > 0 there is a valid NT to be remapped
if (eam.getNameAndTypeIndex() > 0) {
cpi = getCpEntry(eam.getNameAndTypeIndex());
if (cpi instanceof NameAndTypeCpInfo){
NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo) cpi;
Utf8CpInfo refUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getNameIndex());
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getDescriptorIndex());
String origMethodName = refUtf.getString();
String origDescriptor = descUtf.getString();
String remapRef = nm.mapMethod(origClass, origMethodName, origDescriptor);
String remapDesc = nm.mapDescriptor(descUtf.getString());
eam.setNameAndTypeIndex(remapNT(refUtf, remapRef, descUtf, remapDesc, nameTypeInfo, eam.getNameAndTypeIndex()));
}
}
}
}
} else if (attrInfo instanceof SignatureAttrInfo){
remapSignature(nm, (SignatureAttrInfo) attrInfo);
} else if (attrInfo instanceof SourceFileAttrInfo) {
SourceFileAttrInfo source = (SourceFileAttrInfo) attrInfo;
CpInfo cpInfo = getCpEntry(source.getSourceFileIndex());
if (cpInfo instanceof Utf8CpInfo){
Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
String origName = utf.getString();
if (origName != null && origName.length() > 0){
String newName = nm.mapSourceFile(thisClassName, origName);
if (!origName.equals(newName)){
if (newName == null || newName.length() < 1){
AttrInfo[] newAttributes = new AttrInfo[attributes.length - 1];
System.arraycopy(attributes, 0, newAttributes, 0, i);
if (newAttributes.length > i){
System.arraycopy(attributes, i + 1, newAttributes, i, newAttributes.length - i);
}
attributes = newAttributes;
u2attributesCount--;
i--;
constantPool.decRefCount(source.getAttrNameIndex());
utf.decRefCount();
} else {
int remapIndex = constantPool.remapUtf8To(newName, source.getSourceFileIndex());
source.setSourceFileIndex(remapIndex);
}
}
}
}
// } else if (attrInfo instanceof ModuleAttrInfo) {
// should not be necessary to adjust anything because the
// attribute references
// CONSTANT_Class_Info,
// CONSTANT_Module_Info, and
// CONSTANT_Package_Info
// structures
// } else if (attrInfo instanceof ModuleMainClassAttrInfo) {
// should not be necessary to adjust anything because the
// attribute references a
// CONSTANT_Class_Info
// structure
// } else if (attrInfo instanceof ModulePackagesAttrInfo) {
// should not be necessary to adjust anything because the
// attribute references
// CONSTANT_Package_Info
// structures
}
}
// Remap the 'name' and 'descriptor' references of the 'LocalVariableTable'
// attribute, in the 'Code' attribute of method structures.
for (int i = 0; i < u2methodsCount; i++)
{
for (int j = 0; j < methods[i].u2attributesCount; j++)
{
final String methodName = methods[i].getName();
final String descriptor = methods[i].getDescriptor();
AttrInfo attrInfo = methods[i].attributes[j];
if (attrInfo instanceof AnnotationDefaultAttrInfo){
remapAnnotationDefault((AnnotationDefaultAttrInfo)attrInfo, nm);
} else if (attrInfo instanceof RuntimeVisibleAnnotationsAttrInfo){
remapAnnotations((RuntimeVisibleAnnotationsAttrInfo)attrInfo, nm);
} else if (attrInfo instanceof RuntimeVisibleParameterAnnotationsAttrInfo){
remapAnnotations((RuntimeVisibleParameterAnnotationsAttrInfo)attrInfo, nm);
} else if (attrInfo instanceof SignatureAttrInfo){
remapSignature(nm, (SignatureAttrInfo) attrInfo);
} else if (attrInfo instanceof CodeAttrInfo) {
CodeAttrInfo codeAttrInfo = (CodeAttrInfo)attrInfo;
for (int k = 0; k < codeAttrInfo.u2attributesCount; k++)
{
AttrInfo innerAttrInfo = codeAttrInfo.attributes[k];
if (innerAttrInfo instanceof LocalVariableTableAttrInfo)
{
LocalVariableTableAttrInfo lvtAttrInfo = (LocalVariableTableAttrInfo)innerAttrInfo;
LocalVariableInfo[] lvts = lvtAttrInfo.getLocalVariableTable();
for (int m = 0; m < lvts.length; m++)
{
// Remap name
Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(lvts[m].getNameIndex());
String remapName = nm.mapLocalVariable(thisClassName, methodName, descriptor, nameUtf.getString());
if (remapName == null || remapName.length() < 1){
constantPool.decRefCount(lvts[m].getNameIndex());
constantPool.decRefCount(lvts[m].getDescriptorIndex());
LocalVariableInfo[] newArray = new LocalVariableInfo[lvts.length - 1];
System.arraycopy(lvts, 0, newArray, 0, m);
if (newArray.length > m ){
System.arraycopy(lvts, m + 1, newArray, m, newArray.length - m);
}
lvts = newArray;
lvtAttrInfo.setLocalVariableTable(lvts);
m--;
} else {
lvts[m].setNameIndex(constantPool.remapUtf8To(remapName, lvts[m].getNameIndex()));
// Remap descriptor
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(lvts[m].getDescriptorIndex());
String remapDesc = nm.mapDescriptor(descUtf.getString());
lvts[m].setDescriptorIndex(constantPool.remapUtf8To(remapDesc, lvts[m].getDescriptorIndex()));
}
}
} else if (innerAttrInfo instanceof LocalVariableTypeTableAttrInfo){
LocalVariableTypeTableAttrInfo lvttAttrInfo = (LocalVariableTypeTableAttrInfo) innerAttrInfo;
LocalVariableTypeInfo[] lvts = lvttAttrInfo.getLocalVariableTypeTable();
for (int m = 0; m < lvts.length; m++){
// Remap name
Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(lvts[m].getNameIndex());
String remapName = nm.mapLocalVariable(thisClassName, methodName, descriptor, nameUtf.getString());
if (remapName == null || remapName.length() < 1){
constantPool.decRefCount(lvts[m].getNameIndex());
constantPool.decRefCount(lvts[m].getSignatureIndex());
LocalVariableTypeInfo[] newArray = new LocalVariableTypeInfo[lvts.length - 1];
System.arraycopy(lvts, 0, newArray, 0, m);
if (newArray.length > m ){
System.arraycopy(lvts, m + 1, newArray, m, newArray.length - m);
}
lvts = newArray;
lvttAttrInfo.setLocalVariableTypeTable(lvts);
m--;
} else {
lvts[m].setNameIndex(constantPool.remapUtf8To(remapName, lvts[m].getNameIndex()));
// Remap descriptor
Utf8CpInfo signatureUtf = (Utf8CpInfo)getCpEntry(lvts[m].getSignatureIndex());
String remapSig = nm.mapSignature(signatureUtf.getString());
lvts[m].setSignatureIndex(constantPool.remapUtf8To(remapSig, lvts[m].getSignatureIndex()));
}
}
} else if (innerAttrInfo instanceof LineNumberTableAttrInfo) {
LineNumberTableAttrInfo ltai = (LineNumberTableAttrInfo) innerAttrInfo;
if (!nm.mapLineNumberTable(thisClassName, methodName, descriptor, ltai)){
AttrInfo[] newAtt = new AttrInfo[codeAttrInfo.u2attributesCount - 1];
System.arraycopy(codeAttrInfo.attributes, 0, newAtt, 0, k);
if (newAtt.length > k ){
System.arraycopy(codeAttrInfo.attributes, k + 1, newAtt, k, newAtt.length - k);
}
codeAttrInfo.attributes = newAtt;
codeAttrInfo.u2attributesCount--;
k--;
}
}
}
}
}
}
// Go through all of class's fields and methods mapping 'name' and 'descriptor' references
for (int i = 0; i < u2fieldsCount; i++)
{
// Remap field 'name', unless it is 'Synthetic'
FieldInfo field = fields[i];
Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(field.getNameIndex());
if (!field.isSynthetic() || nameUtf.getString().startsWith("class$"))
{
String remapName = nm.mapField(thisClassName, nameUtf.getString());
field.setNameIndex(constantPool.remapUtf8To(remapName, field.getNameIndex()));
}
for (int j = 0; j < field.u2attributesCount; j++){
AttrInfo attrInfo = field.attributes[j];
if (attrInfo instanceof RuntimeVisibleAnnotationsAttrInfo){
remapAnnotations((RuntimeVisibleAnnotationsAttrInfo)attrInfo, nm);
} else if (attrInfo instanceof SignatureAttrInfo){
remapSignature(nm, (SignatureAttrInfo) attrInfo);
}
}
// Remap field 'descriptor'
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(field.getDescriptorIndex());
String remapDesc = nm.mapDescriptor(descUtf.getString());
field.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, field.getDescriptorIndex()));
}
for (int i = 0; i < u2methodsCount; i++)
{
// Remap method 'name', unless it is 'Synthetic'
MethodInfo method = methods[i];
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(method.getDescriptorIndex());
if (!method.isSynthetic())
{
Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(method.getNameIndex());
String remapName = nm.mapMethod(thisClassName, nameUtf.getString(), descUtf.getString());
method.setNameIndex(constantPool.remapUtf8To(remapName, method.getNameIndex()));
}
// Remap method 'descriptor'
String remapDesc = nm.mapDescriptor(descUtf.getString());
method.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, method.getDescriptorIndex()));
}
// check whether .class constructs of Class.forName calls reside in the code..
if (replaceClassNameStrings && nm instanceof ClassTree)
// &&
// (containsClassMethodReference("java/lang/Class","forName(Ljava/lang/String;)Ljava/lang/Class;")) ||
// (containsClassMethodReference("java/lang/Class","forName(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;")) ||
// (containsDotClassMethodReference()))
{
this.replaceConstantPoolStrings((ClassTree)nm);
}
int currentCpLength = constantPool.length(); // constant pool can be extended (never contracted) during loop
for (int i = 0; i < currentCpLength; i++) {
CpInfo cpInfo = getCpEntry(i);
if (cpInfo != null) {
// If this is an entry that references Descriptors and/or names, adjust them correspondingly
if (cpInfo instanceof InvokeDynamicCpInfo) {
InvokeDynamicCpInfo id = (InvokeDynamicCpInfo) cpInfo;
final BootstrapMethod bm = getBootstrapMethod(id);
switch (getType(bm)) {
case BM_TYPE_LMF: {
NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo) getCpEntry(id.getNameAndTypeIndex());
Utf8CpInfo refUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo.getNameIndex());
Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo.getDescriptorIndex());
final String descriptor = descUtf.getString();
String className = descriptor.substring(descriptor.indexOf(")L") + 2, descriptor.length() - 1);
// find out the method descriptor of the method
MethodTypeCpInfo methodTypeInfo = (MethodTypeCpInfo) getCpEntry(bm.getBootstrapArguments()[0]);
Utf8CpInfo samMethodDescriptor = (Utf8CpInfo) getCpEntry(methodTypeInfo.getU2descriptorIndex());
// now find the mapping of the method
String remapName = nm.mapMethod(className, refUtf.getString(), samMethodDescriptor.getString());
String remapDesc = nm.mapDescriptor(descUtf.getString());
id.setNameAndTypeIndex(remapNT(refUtf, remapName, descUtf, remapDesc, nameTypeInfo, id.getNameAndTypeIndex()));
} break;
case BM_TYPE_SCF: {
final int idx = id.getNameAndTypeIndex();
final NameAndTypeCpInfo ntInfo = (NameAndTypeCpInfo) getCpEntry(idx);
final Utf8CpInfo refUtf = (Utf8CpInfo) getCpEntry(ntInfo.getNameIndex());
final Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(ntInfo.getDescriptorIndex());
final String remapDesc = nm.mapDescriptor(descUtf.getString());
id.setNameAndTypeIndex(remapNT(refUtf, refUtf.getString(), descUtf, remapDesc, ntInfo, idx));
} break;
default:
final String sig = getBootstrapMethodSignature(bm);
throw new IllegalArgumentException("Unrecognized bootstrap method: " + sig);
}
}
}
}
// Remap all field/method names and descriptors in the constant pool (depends on class names)
currentCpLength = constantPool.length(); // constant pool can be extended (never contracted) during loop
for (int i = 0; i < currentCpLength; i++)
{
CpInfo cpInfo = getCpEntry(i);
if (cpInfo != null)
{
// If this is an entry that references Descriptors and/or names, adjust them correspondingly
if (cpInfo instanceof MethodTypeCpInfo){
MethodTypeCpInfo mt = (MethodTypeCpInfo) cpInfo;
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(mt.getU2descriptorIndex());
String remapDesc = nm.mapDescriptor(descUtf.getString());
mt.setU2descriptorIndex(constantPool.remapUtf8To(remapDesc, mt.getU2descriptorIndex()));
} else
if (cpInfo instanceof RefCpInfo)
{
// Get the unmodified class name
ClassCpInfo classInfo = (ClassCpInfo)getCpEntry(((RefCpInfo)cpInfo).getClassIndex());
Utf8CpInfo classUtf = (Utf8CpInfo)getCpEntry(classInfo.getNameIndex());
String className = classUtf.getString();
// Get the current N&T reference and its 'name' and 'descriptor' utf's
int ntIndex = ((RefCpInfo)cpInfo).getNameAndTypeIndex();
NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo)getCpEntry(ntIndex);
Utf8CpInfo refUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getNameIndex());
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getDescriptorIndex());
// Get the remapped versions of 'name' and 'descriptor'
String remapRef;
if (cpInfo instanceof FieldrefCpInfo)
{
remapRef = nm.mapField(className, refUtf.getString());
// check if this is a compiler generated field
// supporting the JDK1.2-or-later '.class' construct
if (refUtf.getString().startsWith("class$"))
{
if (!replaceClassNameStrings){
String internalClassName = refUtf.getString().substring(6);
String realClassName = internalClassName.replace('$', '.');
internalClassName = internalClassName.replace('$','/');
String map = nm.mapClass(internalClassName);
if (map != null && !internalClassName.equals(map)){
String warning = realClassName +
" shouldn't be obfuscated: it is most likely referenced as " + realClassName + ".class from " +
Conversion.toJavaClass(thisClassName);
Logger.getInstance().warning(warning);
log.println("");
}
}
}
}
else
{
remapRef = nm.mapMethod(className, refUtf.getString(), descUtf.getString());
}
String remapDesc = nm.mapDescriptor(descUtf.getString());
((RefCpInfo)cpInfo).setNameAndTypeIndex(remapNT(refUtf, remapRef, descUtf, remapDesc, nameTypeInfo, ((RefCpInfo)cpInfo).getNameAndTypeIndex()));
}
}
}
// Finally, remap all class references to Utf
for (int i = 0; i < constantPool.length(); i++)
{
CpInfo cpInfo = getCpEntry(i);
if (cpInfo != null)
{
// If this is CONSTANT_Class, remap the class-name Utf8 entry
if (cpInfo instanceof ClassCpInfo)
{
ClassCpInfo classInfo = (ClassCpInfo)cpInfo;
Utf8CpInfo utf = (Utf8CpInfo)getCpEntry(classInfo.getNameIndex());
String remapClass = nm.mapClass(utf.getString());
int remapIndex = constantPool.remapUtf8To(remapClass, classInfo.getNameIndex());
classInfo.setNameIndex(remapIndex);
} else if (cpInfo instanceof PackageCpInfo) {
final PackageCpInfo info = (PackageCpInfo) cpInfo;
final int pnIdx = info.getNameIndex();
final CpInfo pnInfo = getCpEntry(pnIdx);
if (pnInfo instanceof Utf8CpInfo) {
final String oldName = ((Utf8CpInfo) pnInfo).getString();
final String newName = nm.mapPackage(oldName);
info.setNameIndex(constantPool.remapUtf8To(newName, pnIdx));
}
}
}
}
}
private BootstrapMethodsAttrInfo getBootstrapMethodAttribute() {
for (int i = 0; i < attributes.length; i++) {
AttrInfo attribute = attributes[i];
if (attribute instanceof BootstrapMethodsAttrInfo) {
return (BootstrapMethodsAttrInfo) attribute;
}
}
throw new RuntimeException("No BootstrapMethod attribute in class file");
}
private int getType(BootstrapMethod method) {
final String sig = getBootstrapMethodSignature(method);
if ("java/lang/invoke/StringConcatFactory#makeConcat(...)".equals(sig)) {
return BM_TYPE_SCF;
} else if ("java/lang/invoke/StringConcatFactory#makeConcatWithConstants(...)".equals(sig)) {
return BM_TYPE_SCF;
} else if ("java/lang/invoke/LambdaMetafactory#metafactory(...)".equals(sig)) {
return BM_TYPE_LMF;
} else if ("java/lang/invoke/LambdaMetafactory#altMetafactory(...)".equals(sig)) {
return BM_TYPE_LMF;
} else {
return BM_TYPE_UNKNOWN;
}
}
private BootstrapMethod getBootstrapMethod(InvokeDynamicCpInfo info) {
BootstrapMethodsAttrInfo bmInfo = getBootstrapMethodAttribute();
return bmInfo.getBootstrapMethods()[info.getBootstrapMethodAttrIndex()];
}
private String getBootstrapMethodSignature(BootstrapMethod method) {
final int mhIdx = method.getBootstrapMethodRef();
final MethodHandleCpInfo mhInfo = (MethodHandleCpInfo) getCpEntry(mhIdx);
final RefCpInfo mrInfo = (RefCpInfo) getCpEntry(mhInfo.getReferenceIndex());
final ClassCpInfo cpInfo = (ClassCpInfo) getCpEntry(mrInfo.getClassIndex());
final String className = getUtf8(cpInfo.getNameIndex());
final NameAndTypeCpInfo ntInfo = (NameAndTypeCpInfo) getCpEntry(mrInfo.getNameAndTypeIndex());
final String memberName = getUtf8(ntInfo.getNameIndex());
return className + '#' + memberName + getReferenceKindSuffix(mhInfo.getReferenceKind());
}
private static String getReferenceKindSuffix(int referenceKind) {
switch (referenceKind) {
case REF_getField:
case REF_getStatic:
case REF_putField:
case REF_putStatic:
return "";
case REF_invokeVirtual:
case REF_invokeStatic:
case REF_invokeSpecial:
case REF_newInvokeSpecial:
case REF_invokeInterface:
return "(...)";
default:
throw new IllegalArgumentException("Invalid reference kind: " + referenceKind);
}
}
private void remapAnnotationDefault(AnnotationDefaultAttrInfo annotationDefault, NameMapper nm){
remapElementValue(annotationDefault.elementValue, nm);
}
private void remapAnnotations(RuntimeVisibleAnnotationsAttrInfo annotation, NameMapper nm){
final AnnotationInfo[] annotations = annotation.getAnnotations();
if (annotations != null){
for (int i = 0; i < annotations.length; i++){
remapAnnotation(annotations[i], nm);
}
}
}
private void remapAnnotations(RuntimeVisibleParameterAnnotationsAttrInfo annotation, NameMapper nm){
final ParameterAnnotationInfo[] annotations = annotation.getParameterAnnotations();
if (annotations != null){
for (int i = 0; i < annotations.length; i++){
final ParameterAnnotationInfo info = annotations[i];
final AnnotationInfo[] a = info.getAnnotations();
if (a != null) {
for (int j = 0; j < a.length; j++){
remapAnnotation(a[j], nm);
}
}
}
}
}
private void remapAnnotation(AnnotationInfo annotation, NameMapper nm){
CpInfo info = getCpEntry(annotation.u2typeIndex);
if (info instanceof Utf8CpInfo){
Utf8CpInfo utf = (Utf8CpInfo) info;
String s = utf.getString();
if (s.length() > 2 && s.charAt(0) == 'L' && s.charAt(s.length() - 1) == ';'){
String fqn = s.substring(1, s.length() - 1);
String newFqn = nm.mapClass(fqn);
if (!fqn.equals(newFqn)){
annotation.u2typeIndex = constantPool.remapUtf8To('L' + newFqn + ';', annotation.u2typeIndex);
}
final ElementValuePairInfo[] evp = annotation.getElementValuePairs();
if (evp != null){
for (int i = 0; i < evp.length; i++){
final ElementValuePairInfo elementValuePair = evp[i];
utf = (Utf8CpInfo) getCpEntry(elementValuePair.u2ElementNameIndex);
String remapName = nm.mapAnnotationField(fqn, utf.getString());
if (!remapName.equals(utf.getString())){
elementValuePair.u2ElementNameIndex = constantPool.remapUtf8To(remapName, elementValuePair.u2ElementNameIndex);
}
final ElementValueInfo elementValue = elementValuePair.elementValue;
remapElementValue(elementValue, nm);
}
}
}
}
}
private void remapElementValue(ElementValueInfo elementValue, NameMapper nm){
switch (elementValue.u1Tag)
{
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 's':
// do nothing, this is a constant
break;
case 'e':
// remap the type...
{
Utf8CpInfo utf = (Utf8CpInfo) getCpEntry(elementValue.u2typeNameIndex);
String name = utf.getString();
String remapName = nm.mapDescriptor(name);
elementValue.u2typeNameIndex = constantPool.remapUtf8To(remapName, elementValue.u2typeNameIndex);
}
// leave the constant value in u2constNameIndex
break;
case 'c':
{
Utf8CpInfo utf = (Utf8CpInfo) getCpEntry(elementValue.u2cpIndex);
String name = utf.getString();
String remapName = nm.mapDescriptor(name);
elementValue.u2cpIndex = constantPool.remapUtf8To(remapName, elementValue.u2cpIndex);
}
break;
case '@':
remapAnnotation(elementValue.nestedAnnotation, nm);
break;
case '[':
for (int j = 0; j < elementValue.arrayValues.length; j++)
{
final ElementValueInfo evi = elementValue.arrayValues[j];
remapElementValue(evi, nm);
}
break;
default:
throw new RuntimeException("Unknown type tag in annotation!");
}
}
private void remapSignature(NameMapper nm, SignatureAttrInfo signature){
CpInfo cpInfo = getCpEntry(signature.getSignatureIndex());
if (cpInfo instanceof Utf8CpInfo){
Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
String sig = utf.getString();
String remapSignature = nm.mapSignature(sig);
if (!sig.equals(remapSignature)){
int remapIndex = constantPool.remapUtf8To(remapSignature, signature.getSignatureIndex());
signature.setSignatureIndex(remapIndex);
}
}
}
private int remapNT(Utf8CpInfo refUtf, String remapRef, Utf8CpInfo descUtf, String remapDesc, NameAndTypeCpInfo nameTypeInfo, int nameAndTypeIndex){
// If a remap is required, make a new N&T (increment ref count on 'name' and
// 'descriptor', decrement original N&T's ref count, set new N&T ref count to 1),
// remap new N&T's utf's
if (!remapRef.equals(refUtf.getString()) ||
!remapDesc.equals(descUtf.getString()))
{
// Get the new N&T guy
NameAndTypeCpInfo newNameTypeInfo;
if (nameTypeInfo.getRefCount() == 1)
{
newNameTypeInfo = nameTypeInfo;
}
else
{
// Create the new N&T info
newNameTypeInfo = (NameAndTypeCpInfo)nameTypeInfo.clone();
// Adjust its reference counts of its utf's
((CpInfo)getCpEntry(newNameTypeInfo.getNameIndex())).incRefCount();
((CpInfo)getCpEntry(newNameTypeInfo.getDescriptorIndex())).incRefCount();
// Append it to the Constant Pool, and
// point the RefCpInfo entry to the new N&T data
nameAndTypeIndex = constantPool.addEntry(newNameTypeInfo);
// Adjust reference counts from RefCpInfo
newNameTypeInfo.incRefCount();
nameTypeInfo.decRefCount();
}
// Remap the 'name' and 'descriptor' utf's in N&T
newNameTypeInfo.setNameIndex(constantPool.remapUtf8To(remapRef, newNameTypeInfo.getNameIndex()));
newNameTypeInfo.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, newNameTypeInfo.getDescriptorIndex()));
}
return nameAndTypeIndex;
}
/**
* goes through the constantpool, identifies classnamestrings and replaces
* them appropriately if necessary
*/
private void replaceConstantPoolStrings(ClassTree ct){
for (Enumeration enumeration = constantPool.elements(); enumeration.hasMoreElements();){
CpInfo cpi = (CpInfo) enumeration.nextElement();
if (cpi instanceof Utf8CpInfo){
Utf8CpInfo ui = (Utf8CpInfo) cpi;
String s = ui.getString();
boolean jikes = false;
if (s.length()>5 && s.startsWith("[L") && s.endsWith(";")){
s = s.substring(2, s.length()-1);
jikes = true;
}
if (s.length()>2 && Character.isJavaIdentifierPart(s.charAt(s.length()-1)) &&
s.indexOf(' ')<0 && s.indexOf('.')>0){
Cl cl = ct.findClassForName(s);
if (cl != null){
if (!cl.getFullInName().equals(cl.getFullOutName())){
if (jikes){
ui.setString("[L"+cl.getFullOutName().replace('/','.')+";");
} else {
ui.setString(cl.getFullOutName().replace('/','.'));
}
}
}
}
}
}
}
/** Export the representation to a DataOutput stream. */
public void write(DataOutput dout) throws java.io.IOException
{
if (dout == null) throw new NullPointerException("No output stream was provided.");
dout.writeInt(u4magic);
dout.writeShort(u2minorVersion);
dout.writeShort(u2majorVersion);
dout.writeShort(constantPool.length() + (writeIdString ? 1 : 0));
for (Enumeration enumeration = constantPool.elements(); enumeration.hasMoreElements(); )
{
CpInfo cpInfo = (CpInfo)enumeration.nextElement();
if (cpInfo != null)
{
cpInfo.write(dout);
}
}
if (writeIdString) {
cpIdString.write(dout);
}
dout.writeShort(u2accessFlags);
dout.writeShort(u2thisClass);
dout.writeShort(u2superClass);
dout.writeShort(u2interfacesCount);
for (int i = 0; i < u2interfacesCount; i++)
{
dout.writeShort(u2interfaces[i]);
}
dout.writeShort(u2fieldsCount);
for (int i = 0; i < u2fieldsCount; i++)
{
fields[i].write(dout);
}
dout.writeShort(u2methodsCount);
for (int i = 0; i < u2methodsCount; i++)
{
methods[i].write(dout);
}
dout.writeShort(u2attributesCount);
for (int i = 0; i < u2attributesCount; i++)
{
attributes[i].write(dout);
}
}
/** Dump the content of the class file to the specified file (used for debugging). */
public void dump(PrintWriter pw)
{
pw.println("_____________________________________________________________________");
pw.println("CLASS: " + getName());
pw.println("Magic: " + Integer.toHexString(u4magic));
pw.println("Minor version: " + Integer.toHexString(u2minorVersion));
pw.println("Major version: " + Integer.toHexString(u2majorVersion));
pw.println();
pw.println("CP length: " + Integer.toHexString(constantPool.length()));
for (int i = 0; i < constantPool.length(); i++)
{
CpInfo cpInfo = (CpInfo)constantPool.getCpEntry(i);
if (cpInfo != null)
{
cpInfo.dump(pw, this, i);
}
}
pw.println("Access: " + Integer.toHexString(u2accessFlags));
pw.println("This class: " + getName());
pw.println("Superclass: " + getSuper());
pw.println("Interfaces count: " + Integer.toHexString(u2interfacesCount));
for (int i = 0; i < u2interfacesCount; i++)
{
CpInfo info = getCpEntry(u2interfaces[i]);
if (info == null)
{
pw.println(" Interface " + Integer.toHexString(i) + ": (null)");
}
else
{
pw.println(" Interface " + Integer.toHexString(i) + ": " + ((Utf8CpInfo)getCpEntry(((ClassCpInfo)info).getNameIndex())).getString());
}
}
pw.println("Fields count: " + Integer.toHexString(u2fieldsCount));
for (int i = 0; i < u2fieldsCount; i++)
{
ClassItemInfo info = fields[i];
if (info == null)
{
pw.println(" Field " + Integer.toHexString(i) + ": (null)");
}
else
{
pw.println(" Field " + Integer.toHexString(i) + ": " + ((Utf8CpInfo)getCpEntry(info.getNameIndex())).getString() + " " + ((Utf8CpInfo)getCpEntry(info.getDescriptorIndex())).getString());
}
pw.println(" Attrs count: " + Integer.toHexString(info.u2attributesCount));
for (int j = 0; j < info.u2attributesCount; j++)
{
pw.println(info.attributes[j]);
}
}
pw.println("Methods count: " + Integer.toHexString(u2methodsCount));
for (int i = 0; i < u2methodsCount; i++)
{
ClassItemInfo info = methods[i];
if (info == null)
{
pw.println(" Method " + Integer.toHexString(i) + ": (null)");
}
else
{
pw.println(" Method " + Integer.toHexString(i) + ": " + ((Utf8CpInfo)getCpEntry(info.getNameIndex())).getString() + " " + ((Utf8CpInfo)getCpEntry(info.getDescriptorIndex())).getString() + " " + Integer.toHexString(info.getAccessFlags()));
}
pw.println(" Attrs count: " + Integer.toHexString(info.u2attributesCount));
for (int j = 0; j < info.u2attributesCount; j++)
{
if (info.attributes[j] instanceof CodeAttrInfo){
pw.println(info.attributes[j]);
CodeAttrInfo cai = (CodeAttrInfo) info.attributes[j];
for (int k = 0; k < cai.u2attributesCount; k++){
pw.println(cai.attributes[k]);
}
} else {
pw.println(info.attributes[j]);
}
}
}
pw.println("Attrs count: " + Integer.toHexString(u2attributesCount));
for (int i = 0; i < u2attributesCount; i++)
{
pw.println(attributes[i]);
}
}
public AttrInfo[] getAttributes() {
return attributes;
}
public int getU2attributesCount() {
return u2attributesCount;
}
/**
* Returns the module name if this class file represents a "module-info"
* class and the empty string otherwise.
*/
public String findModuleName() {
for (int i = 0; i < attributes.length; ++i) {
if (attributes[i] instanceof ModuleAttrInfo) {
final int mIdx = ((ModuleAttrInfo) attributes[i]).getModuleNameIndex();
final CpInfo mInfo = constantPool.getCpEntry(mIdx);
if (mInfo instanceof ModuleCpInfo) {
final int nIdx = ((ModuleCpInfo) mInfo).getNameIndex();
final CpInfo nInfo = constantPool.getCpEntry(nIdx);
if (nInfo instanceof Utf8CpInfo) {
return ((Utf8CpInfo) nInfo).getString();
}
}
}
}
return "";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy