KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > javaguard > classfile > ClassFile


1 /**
2  * JavaGuard -- an obfuscation package for Java classfiles.
3  *
4  * Copyright (c) 1999 Mark Welsh (markw@retrologic.com)
5  * Copyright (c) 2002 Thorsten Heit (theit@gmx.de)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  * The author may be contacted at theit@gmx.de.
22  *
23  *
24  * $Id: ClassFile.java,v 1.9 2002/06/04 09:56:47 glurk Exp $
25  */

26 package net.sf.javaguard.classfile;
27
28 import java.io.*;
29 import java.util.*;
30 import net.sf.javaguard.Cl;
31 import net.sf.javaguard.KeyValue;
32 import net.sf.javaguard.ClassTree;
33 import net.sf.javaguard.log.FileLogger;
34 import net.sf.javaguard.Tools;
35
36 /** This is a representation of the data in a Java class file (*.class).
37  * A ClassFile instance representing a *.class file can be generated
38  * using the static create(DataInput) method, manipulated using various
39  * operators, and persisted back using the write(DataOutput) method.
40  *
41  * @author <a HREF="mailto:theit@gmx.de">Thorsten Heit</a>
42  * @author <a HREF="mailto:markw@retrologic.com">Mark Welsh</a>
43  */

44 public class ClassFile implements ClassConstants {
45   // Constants -------------------------------------------------------------
46
private static final String JavaDoc[] DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY = {
47     "forName(Ljava/lang/String;)Ljava/lang/Class;",
48     "getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
49     "getField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
50     "getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
51     "getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"
52   };
53   private static final String JavaDoc LOG_DANGER_CLASS_PRE = " Your class ";
54   private static final String JavaDoc LOG_DANGER_CLASS_MID = " calls the java/lang/Class method ";
55   private static final String JavaDoc[] DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY = {
56     "defineClass(Ljava/lang/String;[BII)Ljava/lang/Class;",
57     "findLoadedClass(Ljava/lang/String;)Ljava/lang/Class;",
58     "findSystemClass(Ljava/lang/String;)Ljava/lang/Class;",
59     "loadClass(Ljava/lang/String;)Ljava/lang/Class;",
60     "loadClass(Ljava/lang/String;Z)Ljava/lang/Class;"
61   };
62   private static final String JavaDoc LOG_DANGER_CLASSLOADER_PRE = " Your class ";
63   private static final String JavaDoc LOG_DANGER_CLASSLOADER_MID = " calls the java/lang/ClassLoader method ";
64   
65   
66   /** Holds the class tree the class file is assigned to. */
67   private ClassTree classTree;
68   /** Holds the magic number stored in the class file (must be 0xcafebabe). */
69   private int magic;
70   /** Holds the minor version number. */
71   private int minorVersion;
72   /** Holds the major version number. */
73   private int majorVersion;
74   /** Holds the constant pool of the class file. */
75   private ConstantPool constantPool;
76   /** Holds the access flags for the class. */
77   private int accessFlags;
78   /** Index into the constant pool. The referenced element holds an index that
79    * references the class name. */

80   private int thisClassIndex;
81   /** Index into the constant pool. The referenced element holds an index that
82    * references the name of the super class. */

83   private int superClassIndex;
84   
85   /** Holds the number of direct implemented interfaces. */
86   private int interfacesCount;
87   /** Index array of interface names in the constant pool. */
88   private int interfaces[];
89   /** Holds the number of direct declared fields. */
90   private int fieldsCount;
91   /** Holds information about the declared fields. */
92   private FieldInfo fieldInfo[];
93   /** Holds the number of declared methods. */
94   private int methodsCount;
95   /** Holds information about the declared methods. */
96   private MethodInfo methodInfo[];
97   /** Holds the number of attributes for the class file. */
98   private int attributesCount;
99   /** Holds information about the attributes for the class file. */
100   private AttrInfo attributesInfo[];
101   
102   
103   
104   
105   /** Create a new ClassFile from the class file format data in the DataInput
106    * stream.
107    * @param classTree the class tree the class file is assigned to
108    * @param din the input stream
109    * @return the class file created from the input stream
110    * @throws IOException if class file is corrupt or incomplete
111    */

112   public static ClassFile create(ClassTree classTree, DataInput din)
113   throws IOException {
114     if (null == din) throw new IOException("No input stream was provided.");
115     ClassFile cf = new ClassFile(classTree);
116     cf.read(din);
117     return cf;
118   }
119   
120   
121   
122   
123   /** Private constructor.
124    * @param classTree the class tree the class file is assigned to
125    */

126   private ClassFile(ClassTree classTree) {
127     this.classTree = classTree;
128   }
129   
130   
131   
132   
133   /** Returns the class tree the class file is assigned to.
134    * @return the class tree
135    */

136   private ClassTree getClassTree() {
137     return classTree;
138   }
139   
140   
141   
142   
143   /** Import the class data to internal representation.
144    * @param din the input stream
145    * @throws IOException if an I/O error occurs
146    */

147   private void read(DataInput din)
148   throws IOException {
149     // Read the class file
150
setMagic(din.readInt());
151     setMinorVersion(din.readUnsignedShort());
152     setMajorVersion(din.readUnsignedShort());
153     
154     // Check this is a valid classfile that we can handle
155
if (getMagic() != MAGIC) {
156       throw new IOException("Invalid magic number in class file.");
157     }
158     if (getMajorVersion() > MAJOR_VERSION ||
159     (MAJOR_VERSION == getMajorVersion() && getMinorVersion() > MINOR_VERSION_MAX)) {
160       throw new IOException("Incompatible version number for class file format: "
161       + getMajorVersion() + " / " + getMinorVersion());
162     }
163     
164     
165     int u2constantPoolCount = din.readUnsignedShort();
166     CpInfo[] cpInfo = new CpInfo[u2constantPoolCount];
167     // Fill the constant pool, recalling the zero entry
168
// is not persisted, nor are the entries following a Long or Double
169
for (int i = 1; i < u2constantPoolCount; i++) {
170       cpInfo[i] = CpInfo.create(din);
171       if ((cpInfo[i] instanceof LongCpInfo) ||
172       (cpInfo[i] instanceof DoubleCpInfo)) {
173         i++;
174       }
175     }
176     constantPool = new ConstantPool(this, cpInfo);
177     
178     setAccessFlags(din.readUnsignedShort());
179     setClassIndex(din.readUnsignedShort());
180     setSuperClassIndex(din.readUnsignedShort());
181     // read the information about the implemented interfaces
182
setInterfacesCount(din.readUnsignedShort());
183     int[] u2interfaces = new int[getInterfacesCount()];
184     for (int i = 0; i < getInterfacesCount(); i++) {
185       u2interfaces[i] = din.readUnsignedShort();
186     }
187     setInterfaces(u2interfaces);
188     // read the direct declared fields
189
setFieldsCount(din.readUnsignedShort());
190     FieldInfo[] fields = new FieldInfo[getFieldsCount()];
191     for (int i = 0; i < getFieldsCount(); i++) {
192       fields[i] = FieldInfo.create(din, this);
193     }
194     setFieldInfo(fields);
195     // read the direct declared methods
196
setMethodsCount(din.readUnsignedShort());
197     MethodInfo[] methods = new MethodInfo[getMethodsCount()];
198     for (int i = 0; i < getMethodsCount(); i++) {
199       methods[i] = MethodInfo.create(din, this);
200     }
201     setMethodInfo(methods);
202     // read the attributes for the class
203
setAttributesCount(din.readUnsignedShort());
204     AttrInfo[] attrs = new AttrInfo[getAttributesCount()];
205     for (int i = 0; i < getAttributesCount(); i++) {
206       attrs[i] = AttrInfo.create(din, this);
207     }
208     setAttributeInfo(attrs);
209   }
210   
211   
212   
213   
214   /** Sets the magic number of the class file (normally 0xcafebabe).
215    * @param num the magic number
216    * @see #getMagic
217    */

218   private void setMagic(int num) {
219     this.magic = num;
220   }
221   
222   
223   /** Returns the magic number of the class file (normally 0xcafebabe).
224    * @return magic number
225    * @see #setMagic
226    */

227   private int getMagic() {
228     return magic;
229   }
230   
231   
232   
233   
234   /** Sets the major version of the class file.
235    * @param ver major class file version
236    * @see #getMajorVersion
237    */

238   private void setMajorVersion(int ver) {
239     majorVersion = ver;
240   }
241   
242   
243   /** Returns the major version of the class file.
244    * @return major class file version
245    * @see #setMajorVersion
246    */

247   private int getMajorVersion() {
248     return majorVersion;
249   }
250   
251   
252   
253   
254   /** Sets the minor version of the class file.
255    * @param ver minor class file version
256    * @see #setMinorVersion
257    */

258   private void setMinorVersion(int ver) {
259     minorVersion = ver;
260   }
261   
262   
263   /** Returns the minor version of the class file.
264    * @return minor class file version
265    * @see #setMinorVersion
266    */

267   private int getMinorVersion() {
268     return minorVersion;
269   }
270   
271   
272   
273   
274   /** Sets the access flags for the class.
275    * @param flags the access flags for the class
276    * @see #getAccessFlags
277    */

278   private void setAccessFlags(int flags) {
279     accessFlags = flags;
280   }
281   
282   
283   /** Returns the access flags for the class.
284    * @return access flags for the class
285    * @see #setAccessFlags
286    */

287   private int getAccessFlags() {
288     return accessFlags;
289   }
290   
291   
292   
293   
294   /** Sets the index for the element in the constant pool that knows the class
295    * name.
296    * @param index index into the constant pool
297    * @see #getClassIndex
298    */

299   private void setClassIndex(int index) {
300     thisClassIndex = index;
301   }
302   
303   
304   /** Returns the index for the element in the constant pool that knows the
305    * class name.
306    * @return index into the constant pool
307    * @see #setClassIndex
308    */

309   private int getClassIndex() {
310     return thisClassIndex;
311   }
312   
313   
314   
315   
316   /** Sets the index for the element in the constant pool that knows the name
317    * of the super class.
318    * @param index index into the constant pool
319    * @see #getSuperClassIndex
320    */

321   private void setSuperClassIndex(int index) {
322     superClassIndex = index;
323   }
324   
325   
326   /** Returns the index for the element in the constant pool that knows the
327    * name of the super class.
328    * @return index into the constant pool
329    */

330   private int getSuperClassIndex() {
331     return superClassIndex;
332   }
333   
334   
335   
336   
337   /** Sets the number of direct implemented interfaces.
338    * @param num the number of direct implemented interfaces
339    * @see #getInterfacesCount
340    */

341   private void setInterfacesCount(int num) {
342     interfacesCount = num;
343   }
344   
345   
346   /** Returns the number of direct implemented interfaces.
347    * @return number of direct implemented interfaces
348    * @see #setInterfacesCount
349    */

350   private int getInterfacesCount() {
351     return interfacesCount;
352   }
353   
354   
355   
356   
357   /** Sets the index array of interface names in the constant pool.
358    * @param interfaces index array of interface names in the constant pool
359    * @see #getInterfaces
360    */

361   private void setInterfaces(int[] interfaces) {
362     this.interfaces = interfaces;
363   }
364   
365   
366   /** Returns the index array of interface names in the constant pool.
367    * @return index array of interface names in the constant pool
368    * @see #setInterfaces
369    * @see #getInterface
370    */

371   private int[] getInterfaces() {
372     return interfaces;
373   }
374   
375   
376   /** Return the index of the specified interface from the index array.
377    * @param pos the position of the interface in the interface name index array
378    * @return index into the constant pool
379    * @see #getInterfaces
380    */

381   private int getInterface(int pos) {
382     return interfaces[pos];
383   }
384   
385   
386   /** Returns an array with the names of the direct implemented interfaces.
387    * @return array with interface names
388    */

389   public String JavaDoc[] getInterfaceNames() {
390     String JavaDoc[] names = new String JavaDoc[getInterfacesCount()];
391     for (int i=0; i<getInterfacesCount(); i++) {
392       names[i] = toName(getInterface(i));
393     }
394     return names;
395   }
396   
397   
398   
399   
400   /** Sets the number of declared fields.
401    * @param num number of declared fields
402    * @see #getFieldsCount
403    */

404   private void setFieldsCount(int num) {
405     fieldsCount = num;
406   }
407   
408   
409   /** Returns the number of declared fields.
410    * @return number of declared fields
411    */

412   public int getFieldsCount() {
413     return fieldsCount;
414   }
415   
416   
417   
418   
419   /** Sets the information about the declared fields.
420    * @param fields array with information about the declared fields
421    * @see #getFieldInfo
422    */

423   private void setFieldInfo(FieldInfo[] fields) {
424     this.fieldInfo = fields;
425   }
426   
427   
428   /** Returns an array with information about the declared fields.
429    * @return array with information about the declared fields
430    * @see #setFieldInfo
431    */

432   private FieldInfo[] getFieldInfo() {
433     return fieldInfo;
434   }
435   
436   
437   /** Return the information about a declared field.
438    * @param pos the position in the array of declared fields
439    * @return information about the declared fieldd
440    * @see #getFieldInfo
441    */

442   public FieldInfo getFieldInfo(int pos) {
443     return fieldInfo[pos];
444   }
445   
446   
447   
448   
449   /** Sets the number of declared methods.
450    * @param num the number of declared methods
451    * @see #getMethodsCount
452    */

453   private void setMethodsCount(int num) {
454     methodsCount = num;
455   }
456   
457   
458   /** Returns the number of declared methods.
459    * @return the number of declared methods
460    */

461   public int getMethodsCount() {
462     return methodsCount;
463   }
464   
465   
466   
467   
468   /** Sets the information about the declared methods.
469    * @param methods array with information about the declared methods
470    * @see #getMethodInfo
471    */

472   private void setMethodInfo(MethodInfo[] methods) {
473     this.methodInfo = methods;
474   }
475   
476   
477   /** Returns an array with information about the declared methods.
478    * @return array with information about the declared methods
479    * @see #setMethodInfo
480    */

481   private MethodInfo[] getMethodInfo() {
482     return methodInfo;
483   }
484   
485   
486   /** Return the information about a declared method.
487    * @param pos the position in the array of declared methods
488    * @return information about the declared method
489    * @see #getMethodInfo
490    */

491   public MethodInfo getMethodInfo(int pos) {
492     return methodInfo[pos];
493   }
494   
495   
496   
497   
498   /** Sets the number of attributes for the class file.
499    * @param num the number of attributes for the class file
500    * @see #getAttributesCount
501    */

502   private void setAttributesCount(int num) {
503     attributesCount = num;
504   }
505   
506   
507   /** Returns the number of attributes for the class file.
508    * @return the number of attributes for the class file
509    * @see #setAttributesCount
510    */

511   private int getAttributesCount() {
512     return attributesCount;
513   }
514   
515   
516   
517   
518   /** Sets the information about the attributes for the class file.
519    * @param attrs array with information about the attributes for the class file
520    * @see #getAttributeInfo
521    */

522   private void setAttributeInfo(AttrInfo[] attrs) {
523     this.attributesInfo = attrs;
524   }
525   
526   
527   /** Returns an array with information about the attributes for the class file.
528    * @return array with information about the attributes for the class file
529    * @see #setAttributeInfo
530    */

531   private AttrInfo[] getAttributeInfo() {
532     return attributesInfo;
533   }
534   
535   
536   /** Return the information about an attribute for the class file.
537    * @param pos the position in the array of attributes
538    * @return information about the attribute
539    * @see #getAttributeInfo
540    * @see #setAttributeInfo(int, AttrInfo)
541    */

542   private AttrInfo getAttributeInfo(int pos) {
543     return attributesInfo[pos];
544   }
545   
546   
547   /** Sets the information about a certain attribute.
548    * @param pos the position in the array of attributes
549    * @param attrInfo the new attribute information
550    * @see #getAttributeInfo(int)
551    * @see #setAttributeInfo
552    */

553   private void setAttributeInfo(int pos, AttrInfo attrInfo) {
554     attributesInfo[pos] = attrInfo;
555   }
556   
557   
558   
559   
560   /** Return the name of the class file.
561    * @return the name of the class file
562    */

563   public String JavaDoc getName() {
564     return toName(getClassIndex());
565   }
566   
567   
568   
569   
570   /** Return the name of this class's super class.
571    * @return the name of the super class
572    */

573   public String JavaDoc getSuper() {
574     // This may be java/lang/Object, in which case there is no super
575
return (0 == getSuperClassIndex()) ? null : toName(getSuperClassIndex());
576   }
577   
578   
579   
580   
581   /** Convert a constant pool index to a class name.
582    * @param index the index into the constant pool
583    * @return the name of the specified class
584    * @throws IllegalStateException if an error occurs
585    */

586   protected String JavaDoc toName(int index)
587   throws IllegalStateException JavaDoc {
588     CpInfo classEntry = getCpEntry(index);
589     if (classEntry instanceof ClassCpInfo) {
590       CpInfo nameEntry = getCpEntry( ((ClassCpInfo) classEntry).getClassNameIndex());
591       if (nameEntry instanceof Utf8CpInfo) {
592         return ((Utf8CpInfo) nameEntry).getString();
593       }
594     }
595     throw new IllegalStateException JavaDoc("Inconsistent Constant Pool in class file.");
596   }
597   
598   
599   
600   
601   /** Lookup the entry in the constant pool and return the object.
602    * @param index the index into the constant pool
603    * @return the entry at the specified index in the constant pool
604    */

605   protected CpInfo getCpEntry(int index) {
606     return constantPool.getCpEntry(index);
607   }
608   
609   
610   
611   
612   /** Check for methods which can break the obfuscated code, and log them to
613    * a string array.
614    * @return string array with methods which can break the obfuscated code
615    */

616   public String JavaDoc[] dangerousMethods() {
617     Vector warningVec = new Vector();
618     
619     // Need only check CONSTANT_Methodref entries of constant pool since
620
// dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
621
for (Iterator iter = constantPool.iterator(); iter.hasNext(); ) {
622       Object JavaDoc obj = iter.next();
623       if (obj instanceof MethodrefCpInfo) {
624         // Get the method class name, simple name and descriptor
625
MethodrefCpInfo entry = (MethodrefCpInfo) obj;
626         ClassCpInfo classEntry = (ClassCpInfo) getCpEntry(entry.getClassIndex());
627         String JavaDoc className = ((Utf8CpInfo) getCpEntry(classEntry.getClassNameIndex())).getString();
628         NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo) getCpEntry(entry.getNameAndTypeIndex());
629         String JavaDoc name = ((Utf8CpInfo) getCpEntry(ntEntry.getNameIndex())).getString();
630         String JavaDoc descriptor = ((Utf8CpInfo) getCpEntry(ntEntry.getDescriptorIndex())).getString();
631         
632         // Check if this is on the proscribed list
633
if (className.equals("java/lang/Class") &&
634         Tools.isInArray(name + descriptor, DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY)) {
635           warningVec.addElement(LOG_DANGER_CLASS_PRE + getName() + LOG_DANGER_CLASS_MID + name + descriptor);
636         }
637         else if (Tools.isInArray(name + descriptor, DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY)) {
638           warningVec.addElement(LOG_DANGER_CLASSLOADER_PRE + getName() + LOG_DANGER_CLASSLOADER_MID + name + descriptor);
639         }
640       }
641     }
642     
643     // Copy any warnings to a String[]
644
String JavaDoc[] warnings = new String JavaDoc[warningVec.size()];
645     for (int i = 0; i < warnings.length; i++) {
646       warnings[i] = (String JavaDoc)warningVec.elementAt(i);
647     }
648     return warnings;
649   }
650   
651   
652   
653   /** Check for methods which can break the obfuscated code, and log them. */
654   private static boolean hasHeader = false;
655   
656   public static void resetDangerHeader() {
657     hasHeader = false;
658   }
659   
660   public void logDangerousMethods() {
661     // Get any warnings and print them to the logfile
662
String JavaDoc[] warnings = dangerousMethods();
663     if (warnings != null && warnings.length > 0) {
664       FileLogger logfile = FileLogger.getInstance();
665       if (!hasHeader) {
666         logfile.addMethodWarning("#");
667         logfile.addMethodWarning("# WARNING - Methods are called which may unavoidably break in obfuscated version at runtime.");
668         logfile.addMethodWarning("# Please review your source code to ensure that the dangerous methods are not intended");
669         logfile.addMethodWarning("# to act on classes which are within the obfuscated Jar file.");
670         logfile.addMethodWarning("#");
671         hasHeader = true;
672       }
673       for (int i = 0; i < warnings.length; i++) {
674         logfile.addMethodWarning("# " + warnings[i]);
675       }
676     }
677   }
678   
679   
680   /** Check for direct references to Utf8 constant pool entries.
681    * @param pool the constant pool
682    * @throws IllegalStateException if a reference is invalid
683    */

684   public void markUtf8Refs(ConstantPool pool)
685   throws IllegalStateException JavaDoc {
686     try {
687       // Check for references to Utf8 from outside the constant pool
688
for (int i = 0; i < getFieldsCount(); i++) {
689         getFieldInfo(i).markUtf8Refs(pool);
690       }
691       for (int i = 0; i < getMethodsCount(); i++) {
692         getMethodInfo(i).markUtf8Refs(pool); // also checks Code/LVT attrs here
693
}
694       for (int i = 0; i < getAttributesCount(); i++) {
695         getAttributeInfo(i).markUtf8Refs(pool); // checks InnerClasses, SourceFile and all attr names
696
}
697       
698       // Now check for references from other CP entries
699
for (Iterator iter = pool.iterator(); iter.hasNext(); ) {
700         Object JavaDoc obj = iter.next();
701         if (obj instanceof NameAndTypeCpInfo ||
702         obj instanceof ClassCpInfo ||
703         obj instanceof StringCpInfo) {
704           ((CpInfo) obj).markUtf8Refs(pool);
705         }
706       }
707     } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
708       throw new IllegalStateException JavaDoc("Inconsistent reference to constant pool.");
709     }
710   }
711   
712   
713   
714   
715   /** Check for direct references to NameAndType constant pool entries.
716    * @param pool the constant pool
717    * @throws IllegalStateException if a reference is invalid
718    */

719   public void markNTRefs(ConstantPool pool)
720   throws IllegalStateException JavaDoc {
721     try {
722       // Now check the method and field CP entries
723
for (Iterator iter = pool.iterator(); iter.hasNext(); ) {
724         Object JavaDoc obj = iter.next();
725         if (obj instanceof RefCpInfo) {
726           ((CpInfo) obj).markNTRefs(pool);
727         }
728       }
729     } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
730       throw new IllegalStateException JavaDoc("Inconsistent reference to constant pool.");
731     }
732   }
733   
734   
735   
736   
737   /** Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
738    * are preserved, all others except the list in the String[] are killed).
739    * @param extraAttrs array with additional attributes to keep
740    */

741   public void trimAttrsExcept(String JavaDoc[] extraAttrs) {
742     // Merge additional attributes with required list
743
String JavaDoc[] keepAttrs = REQUIRED_ATTRS;
744     if (extraAttrs != null && extraAttrs.length > 0) {
745       String JavaDoc[] tmp = new String JavaDoc[keepAttrs.length + extraAttrs.length];
746       System.arraycopy(keepAttrs, 0, tmp, 0, keepAttrs.length);
747       System.arraycopy(extraAttrs, 0, tmp, keepAttrs.length, extraAttrs.length);
748       keepAttrs = tmp;
749     }
750     
751     // Traverse all attributes, removing all except those on 'keep' list
752
for (int i = 0; i < getFieldsCount(); i++) {
753       getFieldInfo(i).trimAttrsExcept(keepAttrs);
754     }
755     for (int i = 0; i < getMethodsCount(); i++) {
756       getMethodInfo(i).trimAttrsExcept(keepAttrs);
757     }
758     for (int i = 0; i < getAttributesCount(); i++) {
759       if (Tools.isInArray(getAttributeInfo(i).getAttrName(), keepAttrs)) {
760         getAttributeInfo(i).trimAttrsExcept(keepAttrs);
761       } else {
762         setAttributeInfo(i, null);
763       }
764     }
765     
766     // Delete the marked attributes
767
AttrInfo[] left = new AttrInfo[getAttributesCount()];
768     int j = 0;
769     for (int i = 0; i < getAttributesCount(); i++) {
770       if (null != getAttributeInfo(i)) {
771         left[j++] = getAttributeInfo(i);
772       }
773     }
774     AttrInfo[] attributes = new AttrInfo[j];
775     System.arraycopy(left, 0, attributes, 0, j);
776     setAttributesCount(j);
777     setAttributeInfo(attributes);
778     
779     // Update the constant pool reference counts
780
constantPool.updateRefCount();
781   }
782   
783   
784   
785   
786   /** Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
787    * are preserved, all others are killed).
788    */

789   public void trimAttrs() {
790     trimAttrsExcept(null);
791   }
792   
793   
794   /** Remap the entities in the current class file.
795    * @param nm the name mapper for the class file
796    */

797   public void remap(NameMapper nm) {
798     // Remove unnecessary attributes from the class
799
String JavaDoc[] attrs = nm.getAttrsToKeep();
800     if (attrs.length > 0) {
801       trimAttrsExcept(attrs);
802     } else {
803       trimAttrs();
804     }
805     
806     // Remap all the package/interface/class/method/field names
807
//
808
String JavaDoc thisClassName = ((Utf8CpInfo)getCpEntry(((ClassCpInfo)getCpEntry(getClassIndex())).getClassNameIndex())).getString();
809     
810     // Remap the 'inner name' reference of the 'InnerClasses' attribute
811
for (int i = 0; i < getAttributesCount(); i++) {
812       AttrInfo attrInfo = getAttributeInfo(i);
813       if (attrInfo instanceof InnerClassesAttrInfo) {
814         // For each inner class referemce,
815
InnerClassesInfo[] info = ((InnerClassesAttrInfo) attrInfo).getInnerClasses();
816         for (int j = 0; j < info.length; j++) {
817           // Get the 'inner name' (it is a CONSTANT_Utf8)
818
CpInfo cpInfo = getCpEntry(info[j].getInnerNameIndex());
819           if (cpInfo instanceof Utf8CpInfo) {
820             // Get the remapped class name
821
Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
822             String JavaDoc origClass = utf.getString();
823             
824             // Only remap non-anonymous classes (anon are "")
825
if (!origClass.equals("")) {
826               // Get the full inner class name
827
ClassCpInfo innerClassInfo = (ClassCpInfo) getCpEntry(info[j].getInnerClassInfoIndex());
828               String JavaDoc innerClassName = ((Utf8CpInfo) getCpEntry(innerClassInfo.getClassNameIndex())).getString();
829               
830               // It is the remapped simple name that must be stored, so truncate it
831
String JavaDoc remapClass = nm.mapClass(innerClassName);
832               remapClass = remapClass.substring(remapClass.lastIndexOf('$') + 1);
833               int remapIndex = constantPool.remapUtf8To(remapClass, info[j].getInnerNameIndex());
834               info[j].setInnerNameIndex(remapIndex);
835             }
836           }
837         }
838       }
839     }
840     
841     // Remap the 'name' and 'descriptor' references of the 'LocalVariableTable'
842
// attribute, in the 'Code' attribute of method structures.
843
for (int i = 0; i < getMethodsCount(); i++) {
844       for (int j = 0; j < getMethodInfo(i).getAttributesLength(); j++) {
845         AttrInfo attrInfo = getMethodInfo(i).getAttribute(j);
846         if (attrInfo instanceof CodeAttrInfo) {
847           CodeAttrInfo codeAttrInfo = (CodeAttrInfo)attrInfo;
848           for (int k = 0; k < codeAttrInfo.getAttributesLength(); k++) {
849             AttrInfo innerAttrInfo = codeAttrInfo.getAttributes()[k];
850             if (innerAttrInfo instanceof LocalVariableTableAttrInfo) {
851               LocalVariableTableAttrInfo lvtAttrInfo = (LocalVariableTableAttrInfo)innerAttrInfo;
852               LocalVariableInfo[] lvts = lvtAttrInfo.getLocalVariableTable();
853               for (int m = 0; m < lvts.length; m++) {
854                 // Remap name
855
Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(lvts[m].getNameIndex());
856                 String JavaDoc remapName = nm.mapField(thisClassName, nameUtf.getString());
857                 // if no mapping is found use the original name
858
if (null == remapName) {
859                   remapName = nameUtf.getString();
860                 }
861                 lvts[m].setNameIndex(constantPool.remapUtf8To(remapName, lvts[m].getNameIndex()));
862                 
863                 // Remap descriptor
864
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(lvts[m].getDescriptorIndex());
865                 String JavaDoc remapDesc = nm.mapDescriptor(descUtf.getString());
866                 lvts[m].setDescriptorIndex(constantPool.remapUtf8To(remapDesc, lvts[m].getDescriptorIndex()));
867               }
868             }
869           }
870         }
871       }
872     }
873     
874     // Go through all of class's fields and methods mapping 'name' and 'descriptor' references
875
for (int i = 0; i < getFieldsCount(); i++) {
876       // Remap field 'name', unless it is 'Synthetic'
877
FieldInfo field = getFieldInfo(i);
878       if (!field.isSynthetic()) {
879         Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(field.getNameIndex());
880         String JavaDoc remapName = nm.mapField(thisClassName, nameUtf.getString());
881         // if no mapping is found use the original name
882
if (null == remapName) {
883           remapName = nameUtf.getString();
884         }
885         field.setNameIndex(constantPool.remapUtf8To(remapName, field.getNameIndex()));
886       }
887       
888       // Remap field 'descriptor'
889
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(field.getDescriptorIndex());
890       String JavaDoc remapDesc = nm.mapDescriptor(descUtf.getString());
891       field.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, field.getDescriptorIndex()));
892     }
893     for (int i = 0; i < getMethodsCount(); i++) {
894       // Remap method 'name', unless it is 'Synthetic'
895
MethodInfo method = getMethodInfo(i);
896       Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(method.getDescriptorIndex());
897       if (!method.isSynthetic()) {
898         Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(method.getNameIndex());
899         String JavaDoc remapName = nm.mapMethod(thisClassName, nameUtf.getString(), descUtf.getString());
900         // if no mapping is found use the original name
901
if (null == remapName) {
902           remapName = nameUtf.getString();
903         }
904         method.setNameIndex(constantPool.remapUtf8To(remapName, method.getNameIndex()));
905       }
906       
907       // Remap method 'descriptor'
908
String JavaDoc remapDesc = nm.mapDescriptor(descUtf.getString());
909       method.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, method.getDescriptorIndex()));
910     }
911     
912     // Remap all field/method names and descriptors in the constant pool (depends on class names)
913
int currentCpLength = constantPool.length(); // constant pool can be extended (never contracted) during loop
914
for (int i = 0; i < currentCpLength; i++) {
915       CpInfo cpInfo = getCpEntry(i);
916       if (null != cpInfo) {
917         // If this is a CONSTANT_Fieldref, CONSTANT_Methodref or CONSTANT_InterfaceMethodref
918
// get the CONSTANT_NameAndType and remap the name and the components of the
919
// descriptor string.
920
if (cpInfo instanceof RefCpInfo) {
921           // Get the unmodified class name
922
ClassCpInfo classInfo = (ClassCpInfo)getCpEntry(((RefCpInfo)cpInfo).getClassIndex());
923           Utf8CpInfo classUtf = (Utf8CpInfo)getCpEntry(classInfo.getClassNameIndex());
924           String JavaDoc className = classUtf.getString();
925           
926           // Get the current N&T reference and its 'name' and 'descriptor' utf's
927
int ntIndex = ((RefCpInfo)cpInfo).getNameAndTypeIndex();
928           NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo)getCpEntry(ntIndex);
929           Utf8CpInfo refUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getNameIndex());
930           Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getDescriptorIndex());
931           
932           // Get the remapped versions of 'name' and 'descriptor'
933
String JavaDoc remapRef;
934           if (cpInfo instanceof FieldrefCpInfo) {
935             remapRef = nm.mapField(className, refUtf.getString());
936             // if no mapping is found use the original name
937
if (null == remapRef) {
938               remapRef = refUtf.getString();
939             }
940             
941             // cloder - check if this is a compiler generated field
942
// supporting the JDK1.2-or-later '.class' construct
943
if (refUtf.getString().startsWith("class$")) {
944               String JavaDoc realClassName = refUtf.getString().substring(6).replace('$', '.');
945               // check whether the hardcoded class name is not a class from the
946
// Java SDK, i.e. doesn't start with "java." or "javax."
947
if (!realClassName.startsWith("java.") && !realClassName.startsWith("javax.")
948               && !realClassName.startsWith("sun.") && !realClassName.startsWith("com.sun.")) {
949                 // check whether the hardcoded class name exists in the main tree
950
// whether it is be obfuscated
951
Cl cl = getClassTree().findClass(realClassName);
952                 if (null != cl) {
953                   if (cl.isBeingObfuscated()) {
954                     FileLogger.getInstance().addWarning("# WARNING: " + realClassName +
955                     " shouldn't be obfuscated: it is referenced as "
956                     + realClassName + ".class from " + thisClassName);
957                   }
958                 }
959               }
960             }
961             // end cloder
962
} else {
963             // instance of method mapping
964
remapRef = nm.mapMethod(className, refUtf.getString(), descUtf.getString());
965             // if no mapping is found use the original name
966
if (null == remapRef) {
967               remapRef = refUtf.getString();
968             }
969           }
970           String JavaDoc remapDesc = nm.mapDescriptor(descUtf.getString());
971           
972           // If a remap is required, make a new N&T (increment ref count on 'name' and
973
// 'descriptor', decrement original N&T's ref count, set new N&T ref count to 1),
974
// remap new N&T's utf's
975
if (!remapRef.equals(refUtf.getString()) ||
976           !remapDesc.equals(descUtf.getString())) {
977             // Get the new N&T guy
978
NameAndTypeCpInfo newNameTypeInfo;
979             if (nameTypeInfo.getRefCount() == 1) {
980               newNameTypeInfo = nameTypeInfo;
981             }
982             else {
983               // Create the new N&T info
984
newNameTypeInfo = (NameAndTypeCpInfo)nameTypeInfo.clone();
985               
986               // Adjust its reference counts of its utf's
987
((CpInfo)getCpEntry(newNameTypeInfo.getNameIndex())).incRefCount();
988               ((CpInfo)getCpEntry(newNameTypeInfo.getDescriptorIndex())).incRefCount();
989               
990               // Append it to the Constant Pool, and
991
// point the RefCpInfo entry to the new N&T data
992
((RefCpInfo)cpInfo).setNameAndTypeIndex(
993               constantPool.addEntry(newNameTypeInfo));
994               
995               // Adjust reference counts from RefCpInfo
996
newNameTypeInfo.incRefCount();
997               nameTypeInfo.decRefCount();
998             }
999             
1000            // Remap the 'name' and 'descriptor' utf's in N&T
1001
newNameTypeInfo.setNameIndex(constantPool.remapUtf8To(remapRef, newNameTypeInfo.getNameIndex()));
1002            newNameTypeInfo.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, newNameTypeInfo.getDescriptorIndex()));
1003          }
1004        }
1005      }
1006    }
1007    
1008    // Finally, remap all class references to Utf
1009
for (int i = 0; i < constantPool.length(); i++) {
1010      CpInfo cpInfo = getCpEntry(i);
1011      if (cpInfo != null) {
1012        // If this is CONSTANT_Class, remap the class-name Utf8 entry
1013
if (cpInfo instanceof ClassCpInfo) {
1014          ClassCpInfo classInfo = (ClassCpInfo)cpInfo;
1015          Utf8CpInfo utf = (Utf8CpInfo)getCpEntry(classInfo.getClassNameIndex());
1016          String JavaDoc remapClass = nm.mapClass(utf.getString());
1017          int remapIndex = constantPool.remapUtf8To(remapClass, classInfo.getClassNameIndex());
1018          classInfo.setClassNameIndex(remapIndex);
1019        }
1020      }
1021    }
1022  }
1023  
1024  
1025  
1026  
1027  /** Export the representation of the class file to a DataOutput stream.
1028   * @param dout the output stream
1029   * @throws IOException if an I/O error occurs
1030   */

1031  public void write(DataOutput dout)
1032  throws IOException {
1033    if (dout == null) throw new IOException("No output stream was provided.");
1034    dout.writeInt(getMagic());
1035    dout.writeShort(getMinorVersion());
1036    dout.writeShort(getMajorVersion());
1037    dout.writeShort(constantPool.length());
1038    for (Iterator iter = constantPool.iterator(); iter.hasNext(); ) {
1039      CpInfo cpInfo = (CpInfo) iter.next();
1040      if (cpInfo != null) {
1041        cpInfo.write(dout);
1042      }
1043    }
1044    dout.writeShort(getAccessFlags());
1045    dout.writeShort(getClassIndex());
1046    dout.writeShort(getSuperClassIndex());
1047    dout.writeShort(getInterfacesCount());
1048    for (int i = 0; i < getInterfacesCount(); i++) {
1049      dout.writeShort(getInterface(i));
1050    }
1051    dout.writeShort(getFieldsCount());
1052    for (int i = 0; i < getFieldsCount(); i++) {
1053      getFieldInfo(i).write(dout);
1054    }
1055    dout.writeShort(getMethodsCount());
1056    for (int i = 0; i < getMethodsCount(); i++) {
1057      getMethodInfo(i).write(dout);
1058    }
1059    dout.writeShort(getAttributesCount());
1060    for (int i = 0; i < getAttributesCount(); i++) {
1061      getAttributeInfo(i).write(dout);
1062    }
1063  }
1064  
1065  
1066  
1067  
1068  /** Dump the content of the class file to the specified file (used for
1069   * debugging).
1070   * @param pw the print writer
1071   */

1072  public void dump(PrintWriter pw) {
1073    pw.println("_____________________________________________________________________");
1074    pw.println("CLASS: " + getName());
1075    pw.println("Magic: " + Integer.toHexString(getMagic()) + " (" + getMagic() + ")");
1076    pw.println("Minor version: " + Integer.toHexString(getMinorVersion()) + " (" + getMinorVersion() + ")");
1077    pw.println("Major version: " + Integer.toHexString(getMajorVersion()) + " (" + getMajorVersion() + ")");
1078    pw.println();
1079    pw.println("Access: " + getAccessFlags());
1080    pw.println("This class: " + getClassIndex() + " / " + getName());
1081    pw.println("Superclass: " + getSuperClassIndex() + " / " + getSuper());
1082    pw.println();
1083    pw.println("CP length: " + constantPool.length());
1084    for (int i = 0; i < constantPool.length(); i++) {
1085      CpInfo cpInfo = (CpInfo) constantPool.getCpEntry(i);
1086      if (cpInfo != null) {
1087        cpInfo.dump(pw, this, i);
1088      }
1089    }
1090    pw.println("Attributes: " + getAttributesCount());
1091    for (int i = 0; i < getAttributesCount(); i++) {
1092      pw.println();
1093      pw.print("attribute[" + i + "]: ");
1094      getAttributeInfo(i).dump(pw, this);
1095    }
1096    pw.println();
1097    pw.println("Direct implemented interfaces: " + getInterfacesCount());
1098    for (int i = 0; i < getInterfacesCount(); i++) {
1099      CpInfo info = getCpEntry(getInterface(i));
1100      pw.print(" interface[" + i + "]: ");
1101      if (null == info) {
1102        pw.println("(null)");
1103      } else {
1104        pw.println( ((Utf8CpInfo) getCpEntry( ((ClassCpInfo) info).getClassNameIndex())).getString() );
1105      }
1106    }
1107    pw.println();
1108    pw.println("Declared fields: " + getFieldsCount());
1109    for (int i = 0; i < getFieldsCount(); i++) {
1110      ClassItemInfo info = getFieldInfo(i);
1111      pw.print(" field[" + i + "]: ");
1112      if (null == info) {
1113        pw.println("(null)");
1114      } else {
1115        pw.println(" name: " + ((Utf8CpInfo) getCpEntry(info.getNameIndex())).getString());
1116        pw.println(" descriptor: " + ((Utf8CpInfo) getCpEntry(info.getDescriptorIndex())).getString());
1117      }
1118      pw.println(" attributes: " + info.getAttributesLength());
1119      for (int j = 0; j < info.getAttributesLength(); j++) {
1120        pw.print(" attribute[" + j + "]: ");
1121        info.getAttribute(j).dump(pw, this);
1122        pw.println();
1123      }
1124    }
1125    pw.println();
1126    pw.println("Declared methods: " + getMethodsCount());
1127    for (int i = 0; i < getMethodsCount(); i++) {
1128      ClassItemInfo info = getMethodInfo(i);
1129      pw.print(" method[" + i + "]: ");
1130      if (info == null) {
1131        pw.println("(null)");
1132      } else {
1133        pw.println();
1134        pw.println(" name: " + ((Utf8CpInfo) getCpEntry(info.getNameIndex())).getString());
1135        pw.println(" descriptor: " + ((Utf8CpInfo) getCpEntry(info.getDescriptorIndex())).getString());
1136        pw.println(" access flags: " + info.getAccessFlags());
1137      }
1138      pw.println(" attributes: " + info.getAttributesLength());
1139      for (int j = 0; j < info.getAttributesLength(); j++) {
1140        pw.print(" attribute[" + j + "]: ");
1141        info.getAttribute(j).dump(pw, this);
1142        pw.println();
1143      }
1144    }
1145  }
1146  
1147  
1148  
1149  /** Retrieve a list of classes that are used in hardcoded references.
1150   * @return a vector containing strings with class names
1151   * @see #remap
1152   */

1153  public Set getHardcodedClassNames() {
1154    HashSet result = new HashSet();
1155    
1156    int currentCpLength = constantPool.length();
1157    for (int i = 0; i < currentCpLength; i++) {
1158      CpInfo cpInfo = getCpEntry(i);
1159      if (cpInfo != null && cpInfo instanceof RefCpInfo) {
1160        // Get the current name and type reference and its 'name' utf's
1161
int ntIndex = ((RefCpInfo) cpInfo).getNameAndTypeIndex();
1162        NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo) getCpEntry(ntIndex);
1163        Utf8CpInfo refUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo.getNameIndex());
1164        
1165        if (cpInfo instanceof FieldrefCpInfo) {
1166          // cloder - check if this is a compiler generated field
1167
// supporting the JDK1.2-or-later '.class' construct
1168
if (refUtf.getString().startsWith("class$")) {
1169            String JavaDoc realClassName = refUtf.getString().substring(6).replace('$', '.');
1170            if (!realClassName.startsWith("java.") && !realClassName.startsWith("javax.")
1171            && !realClassName.startsWith("sun.") && !realClassName.startsWith("com.sun.")) {
1172              result.add(realClassName);
1173            }
1174          }
1175          // end cloder
1176
}
1177      }
1178    }
1179    return result;
1180  }
1181}
1182
Popular Tags