KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jarg > ClassHandler


1 /* ====================================================================
2  * Copyright (c) 2002, Hidetoshi Ohuchi <hchacha@users.sourceforge.net>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the hchacha nor the names of its contributors
17  * may be used to endorse or promote products derived from this
18  * software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  * ====================================================================
33  */

34 package jarg;
35
36 import java.io.ByteArrayInputStream JavaDoc;
37 import java.io.IOException JavaDoc;
38 import java.util.*;
39
40 import org.apache.bcel.classfile.*;
41 import org.apache.bcel.generic.*;
42 import org.apache.bcel.Constants;
43 import org.apache.bcel.util.*;
44
45 /**
46  * The class for handling classes in the jar file.
47  *
48  * @version $Id: ClassHandler.java,v 1.21 2003/01/28 17:30:52 hchacha Exp $
49  * @author Hidetoshi Ohuchi &lt;hchacha@users.sourceforge.net&gt;
50  */

51 class ClassHandler {
52    private Jarg app;
53    String JavaDoc filename;
54    String JavaDoc packagename;
55    String JavaDoc classname_slash;
56    String JavaDoc classname;
57    String JavaDoc superclassname;
58    int orgsize;
59
60    private PackageHandler pkgh;
61
62    JavaClass jcls;
63    private JavaClass njcls;
64
65    NameCreater ncF = new NameCreater();
66    NameCreater ncM = new NameCreater();
67    boolean isNative = false;
68    boolean isPackageScope = false;
69    boolean isUsed = false;
70
71    ClassHandler(Jarg app, String JavaDoc filename, byte[] buf) throws IOException JavaDoc {
72       this.app = app;
73       this.filename = filename;
74
75       ByteArrayInputStream JavaDoc in = new ByteArrayInputStream JavaDoc(buf);
76       this.jcls = new ClassParser(in, filename).parse();
77
78       //
79
this.packagename = this.jcls.getPackageName();
80       this.classname = this.jcls.getClassName();
81       this.classname_slash = this.classname.replace('.','/');
82       this.superclassname = this.jcls.getSuperclassName();
83       this.orgsize = buf.length;
84
85       //
86
if (this.jcls.isClass()
87       && !this.jcls.isPrivate()
88       && !this.jcls.isProtected()
89       && !this.jcls.isPublic()) {
90          this.isPackageScope = true;
91       }
92
93       //
94
Field[] fs = this.jcls.getFields();
95       for (int i=0; i<fs.length; i++) {
96          this.ncF.addName(fs[i].getName());
97       }
98
99       //
100
Method[] ms = this.jcls.getMethods();
101       for (int i=0; i<ms.length; i++) {
102          Method m = ms[i];
103          this.ncM.addName(m.getName());
104          if (m.isNative()) {
105             this.isNative = true;
106          }
107       }
108
109       //
110
app.statistics.addOldClassFileSize(buf.length);
111       setupOldStatistics();
112    }
113
114    private void setupOldStatistics() {
115       app.statistics.incOldClassCount();
116
117       ConstantPool cp = this.jcls.getConstantPool();
118       app.statistics.addOldCpEntryCount(cp.getLength());
119       app.statistics.addOldCpEntrySize(calcConstantPoolSize(cp));
120
121       Field[] fs = this.jcls.getFields();
122       for (int i=0; i<fs.length; i++) {
123          app.statistics.incOldFieldCount();
124       }
125
126       Method[] ms = this.jcls.getMethods();
127       for (int i=0; i<ms.length; i++) {
128          app.statistics.incOldMethodCount();
129          Code code = ms[i].getCode();
130          if (code != null) {
131             byte[] bcode = code.getCode();
132             if (bcode != null) {
133                app.statistics.addOldByteCodeSize(bcode.length);
134                app.statistics.addOldByteCodeCount(calcOpecode(code));
135             }
136          }
137       }
138    }
139
140    void setupNewStatistics() {
141       app.statistics.incNewClassCount();
142
143       ConstantPool cp = this.njcls.getConstantPool();
144       app.statistics.addNewCpEntryCount(cp.getLength());
145       app.statistics.addNewCpEntrySize(calcConstantPoolSize(cp));
146
147       Field[] fs = this.njcls.getFields();
148       for (int i=0; i<fs.length; i++) {
149          app.statistics.incNewFieldCount();
150       }
151
152       Method[] ms = this.njcls.getMethods();
153       for (int i=0; i<ms.length; i++) {
154          app.statistics.incNewMethodCount();
155          Code code = ms[i].getCode();
156          if (code != null) {
157             byte[] bcode = code.getCode();
158             if (bcode != null) {
159                app.statistics.addNewByteCodeSize(bcode.length);
160                app.statistics.addNewByteCodeCount(calcOpecode(code));
161             }
162          }
163       }
164    }
165
166    private int calcConstantPoolSize(ConstantPool cp) {
167       java.io.ByteArrayOutputStream JavaDoc bout = new java.io.ByteArrayOutputStream JavaDoc();
168       try {
169          cp.dump(new java.io.DataOutputStream JavaDoc(bout));
170          bout.close();
171       } catch (java.io.IOException JavaDoc ex) {
172          ex.printStackTrace();
173       }
174       return bout.size();
175    }
176
177    private int calcOpecode(Code code) {
178       int cnt = -1;
179       cnt = OpecodeCounter.countOpcode(code.getCode());
180       return (cnt < 0) ? 0 : cnt;
181    }
182
183    void setPackageHandler(PackageHandler pkgh) {
184       this.pkgh = pkgh;
185    }
186
187    byte[] getBytes() {
188       return njcls.getBytes();
189    }
190
191    void doCheckUsedClass (PackageCollection pkgcol) {
192       ConstantPool cp = jcls.getConstantPool();
193       Constant[] css = cp.getConstantPool();
194       for (int i=0; i<css.length; i++) {
195          Constant cs = css[i];
196          if (cs instanceof ConstantClass) {
197             ConstantClass cc = (ConstantClass)cs;
198             int idx = cc.getNameIndex();
199             Constant csu = css[idx];
200             ConstantUtf8 utf8 = (ConstantUtf8)csu;
201             String JavaDoc s = utf8.getBytes();
202             if (!this.classname_slash.equals(s)) {
203                pkgcol.markUsedClass(s);
204             }
205          }
206       }
207    }
208
209    void doOptimize() {
210       this.njcls = optimizeJavaClass(jcls);
211       this.jcls = null;
212
213       if ("java.lang.Object".equals(njcls.getClassName())) {
214          njcls.setSuperclassNameIndex(0);
215       }
216    }
217
218    private JavaClass optimizeJavaClass(JavaClass jcls) {
219       // class name
220
String JavaDoc oldpkgnm = jcls.getPackageName();
221       String JavaDoc oldclsnm = jcls.getClassName();
222       String JavaDoc oldsupclsnm = jcls.getSuperclassName();
223       String JavaDoc newclsnm = pkgh.convClassName(oldclsnm);
224       String JavaDoc newsupclsnm = pkgh.convClassName(oldsupclsnm);
225
226       if (app.isVerboseAll) {
227          System.out.println("Optimizing class " + oldclsnm);
228       }
229
230       if (app.renameLog != null) {
231          app.renameLog.println(newclsnm + "\t <- " + oldclsnm);
232       }
233
234       // constant pool
235
ConstantPoolGen cpg = new ConstantPoolGen(jcls.getConstantPool());
236
237       boolean isOptClass = true;
238
239       // check for package
240
if (oldpkgnm != null && oldpkgnm.length() > 0) {
241          for (int i=0; i<app.excpPathes.length; i++) {
242             if (oldpkgnm.startsWith(app.excpPathes[i])) {
243                isOptClass = false;
244                break;
245             }
246          }
247       }
248       if (isOptClass && app.excpPackages.contains(oldpkgnm)) {
249          isOptClass = false;
250       }
251
252       // check for class
253
// the field name is used by native code
254
if (isOptClass && app.excpClasses.contains(oldclsnm)) {
255          isOptClass = false;
256       }
257
258       if (isOptClass) {
259          Field[] curfields = jcls.getFields();
260          Method[] curmethods = jcls.getMethods();
261
262          // remove unused fields and methods
263
if (!this.isNative) {
264             removeUnusedFieldAndMethod(oldclsnm, cpg, curfields, curmethods);
265          }
266
267          // fields
268
{
269             boolean isOptimize = true;
270
271             // check for persist
272
// private static final ObjectStreamField[] serialPersistentFields
273
// = {new ObjectStreamField("next", List.class)};
274
for (int i=0; i < curfields.length; i++) {
275                Field f = curfields[i];
276                if (f != null && "serialPersistentFields".equals(f.getName())) {
277                   isOptimize = false;
278                   break;
279                }
280             }
281
282             // check for native
283
if (this.isNative) {
284                isOptimize = false;
285             }
286
287             if (isOptimize) {
288                for (int i=0; i < curfields.length; i++) {
289                   Field oldf = curfields[i];
290                   if (oldf != null) {
291                      Field newf = optimizeFields(oldf, oldclsnm, cpg, newclsnm);
292                      curfields[i] = newf;
293                   }
294                }
295             }
296          }
297
298          // methods
299
{
300             boolean isOptimize = true;
301             // check for native
302
if (this.isNative) {
303                isOptimize = false;
304             }
305             if (isOptimize) {
306                for (int i=0; i < curmethods.length; i++) {
307                   Method oldm = curmethods[i];
308                   if (oldm != null) {
309                      Method newm = optimizeMethod(oldm, oldclsnm, cpg, newclsnm);
310                      curmethods[i] = newm;
311                   }
312                }
313             }
314          }
315       }
316
317       if (app.renameLog != null) {
318          app.renameLog.println();
319       }
320
321       //
322
// finish
323
jcls.setConstantPool(cpg.getFinalConstantPool());
324
325       // reshaping
326
JavaClassReshaper jcr = new JavaClassReshaper(this.app, this.pkgh);
327       return jcr.reshapeJavaClass(jcls, newclsnm, newsupclsnm);
328    }
329
330    private void removeUnusedFieldAndMethod(String JavaDoc oldclsnm, ConstantPoolGen cpg, Field[] curfields, Method[] curmethods) {
331       HashSet fset = new HashSet();
332       HashSet mset = new HashSet();
333
334       // setup methodgen
335
HashMap mthmap = new HashMap();
336       MethodGen[] mgs = new MethodGen[curmethods.length];
337       for (int i=0; i < curmethods.length; i++) {
338          Method m = curmethods[i];
339          if (m != null) {
340             MethodGen mg = new MethodGen(m, oldclsnm, cpg);
341             mgs[i] = mg;
342             String JavaDoc key = mg.getClassName() + '#' + m.getName() + m.getSignature();
343             mthmap.put(key, mg);
344          }
345       }
346
347       // scan used field & method
348
for (int i=0; i < mgs.length; i++) {
349          MethodGen mg = mgs[i];
350          if (!mg.isPrivate()) {
351             scanMethodForUsedCheck(mg, mthmap, fset, mset);
352          }
353       }
354
355       // remove unused private method
356
for (int i=0; i < curmethods.length; i++) {
357          Method m = curmethods[i];
358          if (m.isPrivate()) {
359             String JavaDoc clsnm = oldclsnm;
360             String JavaDoc mthnm = m.getName();
361             String JavaDoc sig = m.getSignature();
362             String JavaDoc mthsig = clsnm + '#' + mthnm + sig;
363             if (!mset.contains(mthsig)) {
364                curmethods[i] = null;
365                if (app.isVerboseUFM) {
366                   System.out.println("Removed method : " + mthsig);
367                }
368             }
369          }
370       }
371
372       // remove unused private field
373
for (int i=0; i < curfields.length; i++) {
374          Field f = curfields[i];
375          if (f.isPrivate()) {
376             String JavaDoc clsnm = oldclsnm;
377             String JavaDoc fldnm = f.getName();
378             String JavaDoc clsfld = clsnm + '!' + fldnm;
379             if (!fset.contains(clsfld)) {
380                curfields[i] = null;
381                if (app.isVerboseUFM) {
382                   System.out.println("Removed field : " + clsfld);
383                }
384             }
385          }
386       }
387    }
388
389    private void scanMethodForUsedCheck(MethodGen mg, HashMap mthmap, HashSet fset, HashSet mset) {
390       ConstantPoolGen cpg = mg.getConstantPool();
391       InstructionList il = mg.getInstructionList();
392       if (il == null) {
393          return;
394       }
395       for (InstructionHandle first = il.getStart(); first != null; first = first.getNext()) {
396          Instruction ins1 = first.getInstruction();
397          if (ins1 instanceof InvokeInstruction) {
398             InvokeInstruction inv = (InvokeInstruction)ins1;
399             String JavaDoc clsnm = inv.getClassName(cpg);
400             String JavaDoc mthnm = inv.getMethodName(cpg);
401             String JavaDoc sig = inv.getSignature(cpg);
402             String JavaDoc mthsig = clsnm + '#' + mthnm + sig;
403             if (!mset.contains(mthsig)) {
404                mset.add(mthsig);
405                MethodGen mg2 = (MethodGen)mthmap.get(mthsig);
406                if (mg2 != null) {
407                   scanMethodForUsedCheck(mg2, mthmap, fset, mset);
408                }
409             }
410 // System.out.println(mthsig);
411
} else if (ins1 instanceof FieldInstruction) {
412             FieldInstruction fins = (FieldInstruction)ins1;
413             String JavaDoc clsnm = fins.getClassName(cpg);
414             String JavaDoc fldnm = fins.getFieldName(cpg);
415             String JavaDoc fld = clsnm + '!' + fldnm;
416             fset.add(fld);
417 // System.out.println(fld);
418
}
419       }
420    }
421
422    private Field optimizeFields(Field f, String JavaDoc oldclsnm, ConstantPoolGen cpg, String JavaDoc newclsnm) {
423       int flags = f.getAccessFlags();
424       String JavaDoc oldfldnm = f.getName();
425       String JavaDoc oldsignm = f.getSignature();
426
427       // rename private field
428
boolean flg = false;
429
430       if (app.isRenameField && f.isPrivate()) {
431          if (!"this".equals(oldfldnm)
432          && !"serialVersionUID".equals(oldfldnm)
433          && !app.excpFields.contains(oldfldnm)
434 // && !name.startsWith("this$")
435
// && !(name.indexOf('$')>=0)
436
) {
437             // private static final long serialVersionUID = 3487495895819393L;
438
flg = true;
439          }
440       }
441
442       String JavaDoc newnm = this.ncF.createNext();
443       if (oldfldnm.length() <= newnm.length()) {
444          flg = false;
445       }
446       if (flg) {
447          String JavaDoc newfldnm = newnm;
448
449          // rewrite new field name
450
int idx = cpg.addUtf8(newfldnm);
451          f.setNameIndex(idx);
452
453          // refresh fieldref with new field name
454
idx = cpg.lookupFieldref(oldclsnm, oldfldnm, oldsignm);
455          if (idx > 0) {
456             ConstantFieldref fr = (ConstantFieldref)cpg.getConstant(idx);
457             int idx2 = cpg.addNameAndType(newfldnm, oldsignm);
458             fr.setNameAndTypeIndex(idx2);
459          }
460
461          if (app.isVerboseRN) {
462             System.out.println("rename field " + oldclsnm + "!" + oldfldnm + " -> " + newfldnm);
463          }
464          if (app.renameLog != null) {
465             app.renameLog.println(newclsnm + "!" + newfldnm + "\t <- " + oldclsnm + "!" + oldfldnm);
466          }
467       } else {
468          if (app.renameLog != null) {
469             String JavaDoc newfldnm = pkgh.convFieldName(oldclsnm, oldfldnm);
470             app.renameLog.println(newclsnm + "!" + newfldnm + "\t <- " + oldclsnm + "!" + oldfldnm);
471          }
472       }
473       return f;
474    }
475
476    private Method optimizeMethod(Method m, String JavaDoc oldclsnm, ConstantPoolGen cpg, String JavaDoc newclsnm) {
477       Code code = m.getCode();
478       int flags = m.getAccessFlags();
479       String JavaDoc oldmthnm = m.getName();
480       String JavaDoc oldsignm = m.getSignature();
481
482       // sanity check
483
if(m.isNative() || m.isAbstract() || (code == null)) {
484          return m;
485       }
486       MethodGen mg = new MethodGen(m, oldclsnm, cpg);
487
488       // rename private method
489
boolean flg = false;
490
491       if (app.isRenameMethod && m.isPrivate()) {
492          if (!"<init>".equals(oldmthnm)
493          && !"<clinit>".equals(oldmthnm)
494          && !app.excpMethods.contains(oldmthnm)
495          ) {
496             flg = true;
497          }
498       }
499
500       String JavaDoc newnm = this.ncM.createNext();
501       if (oldmthnm.length() <= newnm.length()) {
502          flg = false;
503       }
504       if (flg) {
505          String JavaDoc newmthnm = newnm;
506          int idx;
507
508          // rewrite new field name
509
mg.setName(newmthnm);
510
511          // refresh methodref with new method name
512
idx = cpg.lookupMethodref(oldclsnm, oldmthnm, oldsignm);
513          if (idx > 0) {
514             ConstantMethodref mr = (ConstantMethodref)cpg.getConstant(idx);
515             int idx2 = cpg.addNameAndType(newmthnm, oldsignm);
516             mr.setNameAndTypeIndex(idx2);
517          }
518
519          if (app.isVerboseRN) {
520             System.out.println("rename method " + oldclsnm + "#" + oldmthnm + oldsignm + " -> " + newmthnm);
521          }
522          if (app.renameLog != null) {
523             String JavaDoc newsignm = pkgh.convSignatureM(oldsignm);
524             app.renameLog.println(newclsnm + "#" + newmthnm + newsignm + "\t <- " + oldclsnm + "#" + oldmthnm + oldsignm);
525          }
526       } else {
527          if (app.renameLog != null) {
528             String JavaDoc newmthnm = pkgh.convMethodName(oldclsnm, oldmthnm, oldsignm);
529             String JavaDoc newsignm = pkgh.convSignatureM(oldsignm);
530             app.renameLog.println(newclsnm + "#" + newmthnm + newsignm + "\t <- " + oldclsnm + "#" + oldmthnm + oldsignm);
531          }
532       }
533
534       //
535
return mg.getMethod();
536    }
537 }
538
Popular Tags