KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > PatchByteCode


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans;
21
22 import java.io.UnsupportedEncodingException JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.List JavaDoc;
26 import org.openide.util.NbCollections;
27
28 /** Class that can enhance bytecode with information about alternative
29  * superclass and access modifiers. It can also extract this information
30  * later when the class is about to be loaded into the VM.
31  * <P>
32  * The additional information is added to attributes of the classfile (global attributes
33  * and also member attributes) and as such the class remain compatible and
34  * understandable for any VM. But if loaded by classloader that before defining
35  * the class invokes:
36  * <pre>
37  * byte[] arr = ...;
38  * arr = PatchByteCode.patch (arr);
39  * </pre>
40  * The class is altered in its superclass and/or access modifiers.
41  * <P>
42  * The patching mechanism uses two attributes. ATTR_SUPERCLASS can be just
43  * in global attributes pool (and only once), is of length 2 and contains index
44  * into constant pool that contains definition of a Class that should become
45  * the alternate superclass. Attribute ATTR_MEMBER can appear in global
46  * attribute set and also in set of each member (field or method). It is of
47  * length 2 and contains alternate value for access flags of the class or of
48  * the field.
49  * <P>
50  * For purposes for speed, each patched class file has to end with bytes "nb".
51  * This is achieved by finishing the patching process by adding third attribute
52  * "org.netbeans.enhanced" with value "nb". As such the <code>PatchByteCode.patch</code>
53  * can quickly check the byte array and process just those that need processing.
54  *
55  * @author Jaroslav Tulach
56  */

57 public final class PatchByteCode {
58     private static final String JavaDoc ATTR_SUPERCLASS = "org.netbeans.superclass"; // NOI18N
59
private static final byte[] BYTE_SUPERCLASS;
60     static {
61         try {
62             BYTE_SUPERCLASS = ATTR_SUPERCLASS.getBytes("utf-8"); // NOI18N
63
} catch (java.io.UnsupportedEncodingException JavaDoc ex) {
64             throw new IllegalStateException JavaDoc (ex.getMessage());
65         }
66     }
67     private static final String JavaDoc ATTR_INTERFACES = "org.netbeans.interfaces"; // NOI18N
68
private static final byte[] BYTE_INTERFACES;
69     static {
70         try {
71             BYTE_INTERFACES = ATTR_INTERFACES.getBytes("utf-8"); // NOI18N
72
} catch (java.io.UnsupportedEncodingException JavaDoc ex) {
73             throw new IllegalStateException JavaDoc (ex.getMessage());
74         }
75     }
76     private static final String JavaDoc ATTR_MEMBER = "org.netbeans.member"; // NOI18N
77
private static final byte[] BYTE_MEMBER;
78     static {
79         try {
80             BYTE_MEMBER = ATTR_MEMBER.getBytes("utf-8"); // NOI18N
81
} catch (java.io.UnsupportedEncodingException JavaDoc ex) {
82             throw new IllegalStateException JavaDoc (ex.getMessage());
83         }
84     }
85     private static final String JavaDoc ATTR_NAME = "org.netbeans.name"; // NOI18N
86
private static final byte[] BYTE_NAME;
87     static {
88         try {
89             BYTE_NAME = ATTR_NAME.getBytes("utf-8"); // NOI18N
90
} catch (java.io.UnsupportedEncodingException JavaDoc ex) {
91             throw new IllegalStateException JavaDoc (ex.getMessage());
92         }
93     }
94
95     private static final String JavaDoc ATTR_INIT = "<init>"; // NOI18N
96
private static final byte[] BYTE_INIT;
97     static {
98         try {
99             BYTE_INIT = ATTR_INIT.getBytes("utf-8"); // NOI18N
100
} catch (java.io.UnsupportedEncodingException JavaDoc ex) {
101             throw new IllegalStateException JavaDoc (ex.getMessage());
102         }
103     }
104
105     private static final String JavaDoc ATTR_INIT_TYPE = "()V"; // NOI18N
106
private static final byte[] BYTE_INIT_TYPE;
107     static {
108         try {
109             BYTE_INIT_TYPE = ATTR_INIT_TYPE.getBytes("utf-8"); // NOI18N
110
} catch (java.io.UnsupportedEncodingException JavaDoc ex) {
111             throw new IllegalStateException JavaDoc (ex.getMessage());
112         }
113     }
114     
115     private byte[] arr;
116     
117     private int cpCount;
118     private int cpEnd;
119     private int atCount;
120     private int atEnd;
121     
122     /** the index of a string that matches the searched attribute */
123     private int superClassNameIndex;
124     /** the possiton of found attribute */
125     private int superClassNameAttr;
126     /** the index of a string that matches the searched attribute */
127     private int interfacesNameIndex;
128     /** the possiton of found attribute */
129     private int interfacesNameAttr;
130     /** the index of a string to patch members of a field */
131     private int memberNameIndex = -1;
132     /** position of attribute the change the access rights of the class */
133     private int memberClassAttr = -1;
134     /** index of <init> UTF8 in constant pool*/
135     private int initIndex = -1;
136     /** index of ()V UTF8 in constant pool */
137     private int initIndexType = -1;
138     /** index of CONSTANT_NameAndType index for <init> and ()V in pool */
139     private int initNameTypeIndex = -1;
140     /** position of the <init> method */
141     private int initAttr = -1;
142     /** index of string that identifies the rename of a member */
143     private int renameNameIndex = -1;
144     
145     /** map that maps names of fields to their position in constant pool (String, int[1]) */
146     private HashMap JavaDoc<String JavaDoc,int[]> nameIndexes;
147     
148     /** Creates a new instance of PatchByteCode
149      *
150      * @param nameIndexes hashmap from (String -> int[1])
151      */

152     private PatchByteCode(byte[] arr, HashMap JavaDoc<String JavaDoc,int[]> nameIndexes) {
153         this.arr = arr;
154         this.nameIndexes = nameIndexes;
155         
156         // scan twice because of back references
157
scan ();
158         scan ();
159     }
160     
161     /** Generates patch attribute into the classfile to
162      * allow method <code>patch</code> to modify the superclass of this
163      * class.
164      *
165      * @param arr the bytecode to change
166      * @param args map with arguments.
167      * @return new version of the bytecode if changed, otherwise null to signal that
168      * no change has been made
169      */

170     public static byte[] enhanceClass(byte[] arr, java.util.Map JavaDoc<String JavaDoc,Object JavaDoc> args) {
171         if (isPatched (arr)) {
172             // already patched
173
return null;
174         }
175         
176         String JavaDoc superClass = (String JavaDoc)args.get ("netbeans.superclass");
177         String JavaDoc interfaces = (String JavaDoc)args.get ("netbeans.interfaces");
178         List JavaDoc _methods = (List JavaDoc) args.get("netbeans.public");
179         List JavaDoc<String JavaDoc> methods = _methods != null ? NbCollections.checkedListByCopy(_methods, String JavaDoc.class, true) : null;
180         List JavaDoc _rename = (List JavaDoc) args.get ("netbeans.rename");
181         List JavaDoc<String JavaDoc> rename = _rename != null ? NbCollections.checkedListByCopy(_rename, String JavaDoc.class, true) : null;
182         
183
184         HashMap JavaDoc<String JavaDoc,int[]> m;
185         if (methods != null || rename != null) {
186             m = new HashMap JavaDoc<String JavaDoc,int[]> ();
187             
188             if (methods != null) {
189         for (String JavaDoc s: methods) {
190                     m.put(s, new int[1]);
191                 }
192             }
193             
194             if (rename != null) {
195         for (String JavaDoc s: rename) {
196                     m.put(s, new int[1]);
197                 }
198             }
199         } else {
200             m = null;
201         }
202         
203         
204         PatchByteCode pc = new PatchByteCode (arr, m);
205         boolean patched = false;
206         
207         if (superClass != null) {
208             int x = pc.addClass (superClass);
209
210             byte[] sup = new byte[2];
211             writeU2 (sup, 0, x);
212             pc.addAttribute (ATTR_SUPERCLASS, sup);
213             
214             patched = true;
215         }
216         
217         if (interfaces != null) {
218             java.util.ArrayList JavaDoc<String JavaDoc> tokens = new java.util.ArrayList JavaDoc<String JavaDoc> ();
219             java.util.StringTokenizer JavaDoc tok = new java.util.StringTokenizer JavaDoc (interfaces, ",");
220             while (tok.hasMoreTokens()) {
221                 tokens.add (tok.nextToken());
222             }
223             String JavaDoc[] ifaces = tokens.toArray (new String JavaDoc[0]);
224             byte[] sup = new byte[2 + ifaces.length * 2];
225             writeU2 (sup, 0, ifaces.length);
226             
227             for (int i = 0; i < ifaces.length; i++) {
228                 int x = pc.addClass (ifaces[i]);
229
230                 writeU2 (sup, 2 + i * 2, x);
231             }
232             pc.addAttribute (ATTR_INTERFACES, sup);
233             
234             patched = true;
235         }
236         
237         if (!pc.isPublic ()) {
238             // will need patching
239
pc.markPublic ();
240             patched = true;
241         }
242         
243         if (methods != null) {
244             for (String JavaDoc s : methods) {
245                 patched |= pc.markMemberPublic(s);
246             }
247         }
248         
249         if (rename != null) {
250             Iterator JavaDoc<String JavaDoc> it = rename.iterator();
251             while (it.hasNext()) {
252                 patched |= pc.renameMember(it.next(), it.next());
253             }
254         }
255         
256         if (patched) {
257             byte[] patch = {
258                 'n', 'b' // identification at the end of class file
259
};
260
261             pc.addAttribute ("org.netbeans.enhanced", patch);
262         } else {
263             return null;
264         }
265         
266         
267         
268         // otherwise do the patching
269
return pc.getClassFile ();
270     }
271     
272     /** Checks if the class has previously been enhanced by the
273      * change of superclass attribute and if so, changes the bytecode
274      * to reflect the change.
275      *
276      * @param arr the bytecode
277      * @param name the class name
278      * @return the enhanced bytecode
279      */

280     public static byte[] patch (byte[] arr, String JavaDoc name) {
281         if (!isPatched (arr)) return arr;
282         
283         /*
284         if (System.getProperty("test.class") != null) { // NOI18N
285             // Running in XTest (ide-mode executor). Provide a little debug info.
286             System.err.println("Patching: " + name); // NOI18N
287         }
288          */

289
290         PatchByteCode pc = new PatchByteCode (arr, null);
291         if (pc.superClassNameAttr > 0) {
292             // let's patch
293
int classindex = pc.readU2 (pc.superClassNameAttr + 6);
294             
295             writeU2 (pc.getClassFile(), pc.cpEnd + 4, classindex);
296             
297             if (pc.initAttr != -1) {
298                 // patch also CONSTANT_Methodref to superclass's <init>
299
writeU2 (pc.getClassFile (), pc.initAttr + 1, classindex);
300             }
301         }
302
303         if (pc.memberClassAttr > 0) {
304             // change the access rights of the class itself
305
if (pc.readU4 (pc.memberClassAttr + 2) != 2) {
306                 throw new IllegalArgumentException JavaDoc ("Size of a attribute " + ATTR_MEMBER + " should be 2"); // NOI18N
307
}
308
309             // alternate access rights
310
int access = pc.readU2 (pc.memberClassAttr + 6);
311             
312             /*int now = */pc.readU2(pc.cpEnd);
313             
314             writeU2 (pc.getClassFile (), pc.cpEnd, access);
315             
316         }
317
318         if (pc.memberNameIndex > 0 || pc.renameNameIndex > 0) {
319             // change access rights of fields
320
pc.applyMemberAccessAndNameChanges ();
321         }
322         
323         byte[] result = pc.getClassFile ();
324         if (pc.interfacesNameAttr > 0) {
325             // let's patch interfaces if necessary
326
int numberOfIfaces = pc.readU2 (pc.interfacesNameAttr + 6);
327             int currentIfaces = pc.readU2 (pc.cpEnd + 6);
328             
329             byte[] insert = new byte[result.length + numberOfIfaces * 2];
330             System.arraycopy(result, 0, insert, 0, pc.cpEnd + 6);
331             System.arraycopy(result, pc.interfacesNameAttr + 8, insert, pc.cpEnd + 8, numberOfIfaces * 2);
332             System.arraycopy(result, pc.cpEnd + 8, insert, pc.cpEnd + 8 + numberOfIfaces * 2, result.length - pc.cpEnd - 8);
333             writeU2 (insert, pc.cpEnd + 6, numberOfIfaces + currentIfaces);
334             result = insert;
335         }
336         
337         return result;
338     }
339     
340     
341     /** Check if the byte code is patched.
342      * @param arr the bytecode
343      * @return true if patched
344      */

345     private static boolean isPatched (byte[] arr) {
346         if (arr == null || arr.length < 2) return false;
347         
348         int base = arr.length - 2;
349         if (arr[base + 1] != 'b') return false;
350         if (arr[base + 0] != 'n') return false;
351         
352         //
353
// ok, looks like enhanced byte code
354
//
355
return true;
356     }
357     
358     
359     
360     
361     
362     
363     
364     /** Gets the current byte array of the actual class file.
365      * @return bytes of the class file
366      */

367     private byte[] getClassFile () {
368         return arr;
369     }
370     
371     /** Creates new contant pool entry representing given class.
372      * @param c name of the class
373      * @return index of the entry
374      */

375     private int addClass (String JavaDoc s) {
376         int x = addConstant (s);
377         
378         byte[] t = { 7, 0, 0 };
379         writeU2 (t, 1, x);
380         
381         return addPool (t);
382     }
383     
384     /** Adds a new string constant to the constant pool.
385      * @param s the string to add
386      * @return index of the constant
387      */

388     private int addConstant (String JavaDoc s) {
389         byte[] t;
390         
391         try {
392             t = s.getBytes("utf-8"); // NOI18N
393
} catch (java.io.UnsupportedEncodingException JavaDoc ex) {
394             throw new IllegalStateException JavaDoc ("UTF-8 shall be always supported"); // NOI18N
395
}
396         
397         byte[] add = new byte[t.length + 3];
398         System.arraycopy (t, 0, add, 3, t.length);
399         add[0] = 1; // UTF8 contant
400
writeU2 (add, 1, t.length);
401         
402         return addPool (add);
403     }
404         
405     /** Adds this array of bytes as another entry into the constant pool */
406     private int addPool (byte[] add) {
407         byte[] res = new byte[arr.length + add.length];
408      
409         System.arraycopy (arr, 0, res, 0, cpEnd);
410         // increments number of objects in contant pool
411
int index = readU2 (cpCount);
412         writeU2 (res, cpCount, index + 1);
413         
414         // adds the content
415
System.arraycopy (add, 0, res, cpEnd, add.length);
416         
417         // and now add the rest of the original array
418
System.arraycopy (arr, cpEnd, res, cpEnd + add.length, arr.length - cpEnd);
419         
420         arr = res;
421         
422         cpEnd += add.length;
423         atCount += add.length;
424         atEnd += add.length;
425         
426         // the index
427
return index;
428     }
429     
430     /** Checks whether the code is public.
431      */

432     private boolean isPublic () {
433         int x = readU2 (cpEnd);
434         
435         if ((x & 0x0001) != 0) {
436             return true;
437         } else {
438             return false;
439         }
440     }
441     
442     /** Ensures that the class is public
443      * @return true if really patched, false if not
444      */

445     private boolean markPublic () {
446         if (isPublic ()) {
447             return false;
448         }
449         
450         // make sure ATTR_MEMBER is in constant pool
451
if (memberNameIndex == -1) {
452             memberNameIndex = addConstant (ATTR_MEMBER);
453         }
454         
455         int x = readU2 (cpEnd) | 0x0001; // make it public
456

457         byte[] sup = new byte[2];
458         writeU2 (sup, 0, x);
459         addAttribute (ATTR_MEMBER, sup);
460         
461         return true;
462     }
463     
464     /** Makes method of field public and non final.
465      * @param name name of the method to make public
466      * @return true if really changed, false if it already was public
467      */

468     private boolean markMemberPublic (String JavaDoc name) {
469         int constantPoolIndex = nameIndexes.get(name)[0];
470         int patchCount = 0;
471         boolean modified = false;
472
473         // make sure ATTR_MEMBER is in constant pool
474
if (memberNameIndex == -1) {
475             memberNameIndex = addConstant (ATTR_MEMBER);
476         }
477         
478         int pos = cpEnd;
479         
480         pos += 6;
481         // now add interfaces
482
pos += 2 * readU2 (pos);
483         // to add also the integer with interfaces
484
pos += 2;
485         
486         for (int fieldsAndMethods = 0; fieldsAndMethods < 2; fieldsAndMethods++) {
487             // fields and then methods
488
int fieldsOrMethods = readU2 (pos);
489             pos += 2;
490             
491             while (fieldsOrMethods-- > 0) {
492                 // check the name
493
int nameIndex = readU2 (pos + 2);
494                 if (nameIndex == constantPoolIndex) {
495                     // let's patch
496
int access = readU2 (pos);
497                     if ((access & 0x0001) == 0 || (access & 0x0010) != 0) {
498                         // is not public or is final
499
access = (access | 0x0001) & ~(0x0010 | 0x0002 | 0x0004);
500
501
502                         // increment the attributes count
503
int cnt = readU2 (pos + 6) + 1;
504
505                         //
506
byte[] res = new byte[arr.length + 2 + 6];
507
508                         // copy the array before
509
System.arraycopy(arr, 0, res, 0, pos + 6);
510                         // write the new count of attributes
511
writeU2 (res, pos + 6, cnt);
512                         
513                         // write the attribute itself
514
writeU2 (res, pos + 8, memberNameIndex); // name of attribute
515
writeU4 (res, pos + 10, 2); // length
516
writeU2 (res, pos + 14, access); // data - the "NetBeans" member modifier
517

518                         // copy the rest
519
System.arraycopy(arr, pos + 8, res, pos + 8 + 6 + 2, arr.length - pos - 8);
520
521                         atEnd += 2 + 6;
522                         atCount += 2 + 6;
523
524
525                         arr = res;
526                         
527                         modified = true;
528                     }
529                         
530                     patchCount++;
531                 }
532                 
533                 pos += memberSize (pos, null);
534             }
535         }
536         
537         if (patchCount == 0) {
538             throw new IllegalArgumentException JavaDoc ("Member " + name + " not found!");
539         }
540         
541         return modified;
542     }
543     
544     /** Marks a field or method as one that should be renamed.
545      * @param name name of the member
546      * @param rename new name of the member
547      * @return true if really changed, false if it already was renamed
548      */

549     private boolean renameMember (String JavaDoc name, String JavaDoc rename) {
550         int constantPoolIndex = nameIndexes.get (name)[0];
551         int newPoolIndex;
552         {
553             int[] arr = nameIndexes.get(rename);
554             if (arr != null && arr[0] > 0) {
555                 newPoolIndex = arr[0];
556             } else {
557                 newPoolIndex = addConstant (rename);
558                 nameIndexes.put (rename, new int[] { newPoolIndex });
559             }
560         }
561         int patchCount = 0;
562         boolean modified = false;
563
564         // make sure ATTR_MEMBER is in constant pool
565
if (renameNameIndex == -1) {
566             renameNameIndex = addConstant (ATTR_NAME);
567         }
568         
569         int pos = cpEnd;
570         
571         pos += 6;
572         // now add interfaces
573
pos += 2 * readU2 (pos);
574         // to add also the integer with interfaces
575
pos += 2;
576         
577         for (int fieldsAndMethods = 0; fieldsAndMethods < 2; fieldsAndMethods++) {
578             // fields and then methods
579
int fieldsOrMethods = readU2 (pos);
580             pos += 2;
581             
582             while (fieldsOrMethods-- > 0) {
583                 // check the name
584
int nameIndex = readU2 (pos + 2);
585                 if (nameIndex == constantPoolIndex) {
586                     // check whether the rename attribute is not there yet
587
int[] attributes = { -1, -1 };
588                     
589                     memberSize (pos, attributes);
590                     if (attributes[1] == -1) {
591                         // let's patch attribute is not there yet
592

593                         // increment the attributes count
594
int cnt = readU2 (pos + 6) + 1;
595
596                         //
597
byte[] res = new byte[arr.length + 2 + 6];
598
599                         // copy the array before
600
System.arraycopy(arr, 0, res, 0, pos + 6);
601                         // write the new count of attributes
602
writeU2 (res, pos + 6, cnt);
603                         
604                         // write the attribute itself
605
writeU2 (res, pos + 8, renameNameIndex); // name of attribute
606
writeU4 (res, pos + 10, 2); // length
607
writeU2 (res, pos + 14, newPoolIndex); // index to the new name
608

609                         // copy the rest
610
System.arraycopy(arr, pos + 8, res, pos + 8 + 6 + 2, arr.length - pos - 8);
611
612                         atEnd += 2 + 6;
613                         atCount += 2 + 6;
614
615
616                         arr = res;
617                         
618                         modified = true;
619                     }
620                         
621                     patchCount++;
622                 }
623                 
624                 pos += memberSize (pos, null);
625             }
626         }
627         
628         if (patchCount == 0) {
629             throw new IllegalArgumentException JavaDoc ("Member " + name + " not found!");
630         }
631         
632         return modified;
633     }
634     
635     /** Checks all members of the class to find out whether they need patching
636      * of access rights. If so, patches them.
637      */

638     private void applyMemberAccessAndNameChanges () {
639         int[] result = new int[2];
640         
641         int pos = cpEnd;
642         
643         pos += 6;
644         // now add interfaces
645
pos += 2 * readU2 (pos);
646         // to add also the integer with interfaces
647
pos += 2;
648         
649         for (int fieldsAndMethods = 0; fieldsAndMethods < 2; fieldsAndMethods++) {
650             // fields and then methods
651
int fieldsOrMethods = readU2 (pos);
652             pos += 2;
653             
654             while (fieldsOrMethods-- > 0) {
655                 result[0] = -1;
656                 result[1] = -1;
657                 int size = memberSize(pos, result);
658                 if (result[0] != -1) {
659                     // we will do patching
660

661                     if (readU4 (result[0] + 2) != 2) {
662                         throw new IllegalArgumentException JavaDoc ("Size of a attribute " + ATTR_MEMBER + " should be 2"); // NOI18N
663
}
664                     
665                     // alternate access rights
666
int access = readU2 (result[0] + 6);
667                     writeU2 (arr, pos, access);
668                 }
669                 
670                 if (result[1] != -1) {
671                     // we will do patching
672

673                     if (readU4 (result[1] + 2) != 2) {
674                         throw new IllegalArgumentException JavaDoc ("Size of a attribute " + ATTR_NAME + " should be 2"); // NOI18N
675
}
676                     
677                     // alternate name of the member
678
int newName = readU2 (result[1] + 6);
679                     writeU2 (arr, pos + 2, newName);
680                 }
681                 
682                 pos += size;
683             }
684         }
685     }
686     
687     /** Adds an attribute to the class file.
688      * @param name name of the attribute to add
689      * @param b the bytes representing the attribute
690      */

691     private void addAttribute (String JavaDoc name, byte[] b) {
692         int index = -1;
693         if (ATTR_SUPERCLASS.equals (name) && superClassNameIndex > 0) {
694             index = superClassNameIndex;
695         }
696         
697         if (ATTR_MEMBER.equals (name) && memberNameIndex > 0) {
698             index = memberNameIndex;
699         }
700         
701         if (ATTR_INTERFACES.equals (name) && interfacesNameIndex > 0) {
702             index = interfacesNameIndex;
703         }
704         
705         if (index == -1) {
706             // register new attribute
707
index = addConstant (name);
708         }
709         
710         byte[] res = new byte[arr.length + b.length + 6];
711         
712         System.arraycopy(arr, 0, res, 0, arr.length);
713         
714         writeU2 (res, arr.length, index);
715         writeU4 (res, arr.length + 2, b.length);
716         
717         int begin = arr.length + 6;
718         System.arraycopy(b, 0, res, begin, b.length);
719         
720         atEnd += b.length + 6;
721         
722         writeU2 (res, atCount, readU2 (atCount) + 1);
723         
724         arr = res;
725     }
726     
727     
728     /** Gets i-th element from the array.
729      */

730     private int get (int pos) {
731         if (pos >= arr.length) {
732             throw new ArrayIndexOutOfBoundsException JavaDoc ("Size: " + arr.length + " index: " + pos);
733         }
734         
735         int x = arr[pos];
736         return x >= 0 ? x : 256 + x;
737     }
738
739     /** Scans the file to find out important possitions
740      * @return size of the bytecode
741      */

742     private void scan () {
743         if (get (0) != 0xCA && get (1) != 0xFE && get (2) != 0xBA && get (3) != 0xBE) {
744             throw new IllegalStateException JavaDoc ("Not a class file!"); // NOI18N
745
}
746         
747         int pos = 10;
748         // count of items in CP is here
749
cpCount = 8;
750         
751         int cp = readU2 (8);
752         for (int[] i = { 1 }; i[0] < cp; i[0]++) {
753             // i[0] can be incremented in constantPoolSize
754
int len = constantPoolSize (pos, i);
755             pos += len;
756         }
757         
758         // list item in constant pool
759
cpEnd = pos;
760         
761         pos += 6;
762         // now add interfaces
763
pos += 2 * readU2 (pos);
764         // to add also the integer with interfaces
765
pos += 2;
766         
767         // fields
768
int fields = readU2 (pos);
769         pos += 2;
770         while (fields-- > 0) {
771             pos += memberSize (pos, null);
772         }
773         
774         int methods = readU2 (pos);
775         pos += 2;
776         while (methods-- > 0) {
777             pos += memberSize (pos, null);
778         }
779
780         // count of items in Attributes is here
781

782         int[] memberAccess = { -1, -1 };
783         
784         atCount = pos;
785         int attrs = readU2 (pos);
786         pos += 2;
787         while (attrs-- > 0) {
788             pos += attributeSize (pos, memberAccess);
789         }
790         
791         if (memberAccess[0] != -1) {
792             // we need to update the name of class
793
memberClassAttr = memberAccess[0];
794         }
795
796         // end of attributes
797
atEnd = pos;
798     }
799     
800     private int readU2 (int pos) {
801         int b1 = get (pos);
802         int b2 = get (pos + 1);
803         
804         return b1 * 256 + b2;
805     }
806     
807     private int readU4 (int pos) {
808         return readU2 (pos) * 256 * 256 + readU2 (pos + 2);
809     }
810
811     private static void writeU2 (byte[] arr, int pos, int value) {
812         int v1 = (value & 0xff00) >> 8;
813         int v2 = value & 0xff;
814         
815         if (v1 < 0) v1 += 256;
816         if (v2 < 0) v2 += 256;
817         
818         arr[pos] = (byte)v1;
819         arr[pos + 1] = (byte)v2;
820     }
821     
822     private static void writeU4 (byte[] arr, int pos, int value) {
823         writeU2 (arr, pos, (value & 0xff00) >> 16);
824         writeU2 (arr, pos + 2, value & 0x00ff);
825     }
826     
827     /** @param pos position to read from
828      * @param cnt[0] index in the pool that we are now reading
829      */

830     private int constantPoolSize (int pos, int[] cnt) {
831         switch (get (pos)) {
832             case 7: // CONSTANT_Class
833
case 8: // CONSTANT_String
834
return 3;
835                 
836             case 12: // CONSTANT_NameAndType
837
// check for <init> and ()V invocation
838
int nameIndex = readU2 (pos + 1);
839                 if (nameIndex == initIndex) {
840                     int descriptorIndex = readU2 (pos + 3);
841                     if (descriptorIndex == initIndexType) {
842                         if (initNameTypeIndex > 0 && initNameTypeIndex != cnt[0]) {
843                             throw new IllegalArgumentException JavaDoc ("Second initialization of name type index"); // NOI18N
844
}
845                         initNameTypeIndex = cnt[0];
846                     }
847                 }
848                 return 5;
849                 
850             case 10: // CONSTANT_Methodref
851
// special check for <init> invocation
852
int classname = readU2 (pos + 1);
853                 int nameAndType = readU2 (pos + 3);
854                 if (nameAndType == initNameTypeIndex) {
855                     // found call to <init>
856
int superclass = readU2 (cpEnd + 4);
857                     
858                     if (superclass == classname) {
859                         // it is call to our superclass
860
if (initAttr > 0 && initAttr != pos) {
861                             throw new IllegalStateException JavaDoc ("Second initialization of position of <init> invocation"); // NOI18N
862
}
863                         initAttr = pos;
864                     }
865                 }
866                 return 5;
867                 
868             case 9: // CONSTANT_Fieldref
869
case 11: // CONSTANT_InterfaceMethodref
870
case 3: // CONSTANT_Integer
871
case 4: // CONSTANT_Float
872
return 5;
873                 
874             case 5: // CONSTANT_Long
875
case 6: // CONSTANT_Double
876
// after long and double the next entry in CP is unusable
877
cnt[0]++;
878                 return 9;
879             case 1: // CONSTANT_Utf8
880
int len = readU2 (pos + 1);
881
882                 if (compareUtfEntry (BYTE_INIT, pos)) {
883                     if (initIndex > 0 && initIndex != cnt[0]) {
884                         throw new IllegalArgumentException JavaDoc ("Second initialization of " + ATTR_INIT); // NOI18N
885
}
886                     initIndex = cnt[0];
887                 }
888
889                 if (compareUtfEntry (BYTE_INIT_TYPE, pos)) {
890                     if (initIndexType > 0 && initIndexType != cnt[0]) {
891                         throw new IllegalArgumentException JavaDoc ("Second initialization of " + ATTR_INIT_TYPE); // NOI18N
892
}
893                     initIndexType = cnt[0];
894                 }
895                 
896                 if (compareUtfEntry (BYTE_SUPERCLASS, pos)) {
897                     // we have found the attribute
898
if (superClassNameIndex > 0 && superClassNameIndex != cnt[0]) {
899                         throw new IllegalStateException JavaDoc (ATTR_SUPERCLASS + " registered for the second time!"); // NOI18N
900
}
901
902                     superClassNameIndex = cnt[0];
903                 }
904
905                 if (compareUtfEntry (BYTE_INTERFACES, pos)) {
906                     // we have found the attribute
907
if (interfacesNameIndex > 0 && interfacesNameIndex != cnt[0]) {
908                         throw new IllegalStateException JavaDoc (ATTR_INTERFACES + " registered for the second time!"); // NOI18N
909
}
910
911                     interfacesNameIndex = cnt[0];
912                 }
913                 
914                 if (compareUtfEntry (BYTE_MEMBER, pos)) {
915                     // we have found the attribute
916
if (memberNameIndex > 0 && memberNameIndex != cnt[0]) {
917                         throw new IllegalStateException JavaDoc (ATTR_MEMBER + " registered for the second time!"); // NOI18N
918
}
919
920                     memberNameIndex = cnt[0];
921                 }
922                 if (compareUtfEntry (BYTE_NAME, pos)) {
923                     // we have found the attribute
924
if (renameNameIndex > 0 && renameNameIndex != cnt[0]) {
925                         throw new IllegalStateException JavaDoc (ATTR_NAME + " registered for the second time!"); // NOI18N
926
}
927
928                     renameNameIndex = cnt[0];
929                 }
930                     
931                 if (nameIndexes != null) {
932                     // check the name in the table
933
String JavaDoc s;
934                     try {
935                         s = new String JavaDoc (arr, pos + 3, len, "utf-8"); // NOI18N
936
} catch (UnsupportedEncodingException JavaDoc ex) {
937                         throw new IllegalStateException JavaDoc ("utf-8 is always supported"); // NOI18N
938
}
939                     
940                     int[] index = nameIndexes.get(s);
941                     if (index != null) {
942                         index[0] = cnt[0];
943                     }
944                 }
945
946                 // ok, exit
947
return len + 3;
948             default:
949                 throw new IllegalStateException JavaDoc ("Unknown pool type: " + get (pos)); // NOI18N
950
}
951     }
952     
953     private int memberSize (int pos, int[] containsPatchAttributeAndRename) {
954         int s = 8;
955         /*int name = */readU2(pos + 2);
956         
957         int cnt = readU2 (pos + 6);
958         
959         while (cnt-- > 0) {
960             s += attributeSize (pos + s, containsPatchAttributeAndRename);
961         }
962         return s;
963     }
964     
965     /** Into the containsPatchAttribute (if not null) it adds the
966      * index of structure of attribute ATTR_MEMBER.
967      */

968     private int attributeSize (int pos, int[] containsPatchAttributeAndRename) {
969         // index to the name attr
970
int name = readU2 (pos);
971         
972         if (name == superClassNameIndex) {
973             if (superClassNameAttr > 0 && superClassNameAttr != pos) {
974                 throw new IllegalStateException JavaDoc ("Two attributes with name " + ATTR_SUPERCLASS); // NOI18N
975
}
976
977             // we found the attribute
978
superClassNameAttr = pos;
979         }
980
981         if (name == interfacesNameIndex) {
982             if (interfacesNameAttr > 0 && interfacesNameAttr != pos) {
983                 throw new IllegalStateException JavaDoc ("Two attributes with name " + ATTR_INTERFACES); // NOI18N
984
}
985
986             // we found the attribute
987
interfacesNameAttr = pos;
988         }
989         
990         if (name == memberNameIndex && containsPatchAttributeAndRename != null) {
991             if (containsPatchAttributeAndRename[0] != -1) {
992                 throw new IllegalStateException JavaDoc ("Second attribute " + ATTR_MEMBER); // NOI18N
993
}
994             containsPatchAttributeAndRename[0] = pos;
995         }
996
997         if (name == renameNameIndex && containsPatchAttributeAndRename != null) {
998             if (containsPatchAttributeAndRename[1] != -1) {
999                 throw new IllegalStateException JavaDoc ("Second attribute " + ATTR_NAME); // NOI18N
1000
}
1001            containsPatchAttributeAndRename[1] = pos;
1002        }
1003        
1004        int len = readU4 (pos + 2);
1005        return len + 6;
1006    }
1007    
1008    
1009    /** Compares arrays.
1010     */

1011    private boolean compareUtfEntry (byte[] pattern, int pos) {
1012        int len = readU2 (pos + 1);
1013        
1014        if (pattern.length != len) {
1015            return false;
1016        }
1017        
1018        int base = pos + 3;
1019        // we are searching for an attribute with given name
1020
for (int i = 0; i < len; i++) {
1021            if (pattern[i] != arr[base + i]) {
1022                // regular exit
1023
return false;
1024            }
1025        }
1026        
1027        return true;
1028    }
1029}
1030
Popular Tags