View Javadoc
1   /*
2    * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   *
23   */
24  
25  package sun.jvm.hotspot.tools.jcore;
26  
27  import java.io.*;
28  import java.util.*;
29  import sun.jvm.hotspot.oops.*;
30  import sun.jvm.hotspot.runtime.*;
31  import sun.jvm.hotspot.utilities.*;
32  
33  public class ClassWriter implements /* imports */ ClassConstants
34  {
35      public static final boolean DEBUG = false;
36  
37      protected void debugMessage(String message) {
38          System.out.println(message);
39      }
40  
41      protected InstanceKlass     klass;
42      protected DataOutputStream  dos;
43      protected ConstantPool      cpool;
44  
45      // Map between class name to index of type CONSTANT_Class
46      protected Map<String, Short> classToIndex = new HashMap<String, Short>();
47  
48      // Map between any modified UTF-8 and it's constant pool index.
49      protected Map<String, Short> utf8ToIndex = new HashMap<String, Short>();
50  
51      // constant pool index for attribute names.
52  
53      protected short  _sourceFileIndex;
54      protected short  _innerClassesIndex;
55      protected short  _syntheticIndex;
56      protected short  _deprecatedIndex;
57      protected short  _constantValueIndex;
58      protected short  _codeIndex;
59      protected short  _exceptionsIndex;
60      protected short  _lineNumberTableIndex;
61      protected short  _localVariableTableIndex;
62      protected short  _signatureIndex;
63  
64      protected static int extractHighShortFromInt(int val) {
65          // must stay in sync with ConstantPool::name_and_type_at_put, method_at_put, etc.
66          return (val >> 16) & 0xFFFF;
67      }
68  
69      protected static int extractLowShortFromInt(int val) {
70          // must stay in sync with ConstantPool::name_and_type_at_put, method_at_put, etc.
71          return val & 0xFFFF;
72      }
73  
74      public ClassWriter(InstanceKlass kls, OutputStream os) {
75          klass = kls;
76          dos = new DataOutputStream(os);
77          cpool = klass.getConstants();
78      }
79  
80      public void write() throws IOException {
81          if (DEBUG) debugMessage("class name = " + klass.getName().asString());
82  
83          // write magic
84          dos.writeInt(0xCAFEBABE);
85  
86          writeVersion();
87          writeConstantPool();
88          writeClassAccessFlags();
89          writeThisClass();
90          writeSuperClass();
91          writeInterfaces();
92          writeFields();
93          writeMethods();
94          writeClassAttributes();
95  
96          // flush output
97          dos.flush();
98      }
99  
100     protected void writeVersion() throws IOException {
101         dos.writeShort((short)klass.minorVersion());
102         dos.writeShort((short)klass.majorVersion());
103     }
104 
105     protected void writeIndex(int index) throws IOException {
106         if (index == 0) throw new InternalError();
107         dos.writeShort(index);
108     }
109 
110     protected void writeConstantPool() throws IOException {
111         final U1Array tags = cpool.getTags();
112         final long len = tags.length();
113         dos.writeShort((short) len);
114 
115         if (DEBUG) debugMessage("constant pool length = " + len);
116 
117         int ci = 0; // constant pool index
118 
119         // collect all modified UTF-8 Strings from Constant Pool
120 
121         for (ci = 1; ci < len; ci++) {
122             int cpConstType = tags.at(ci);
123             if(cpConstType == JVM_CONSTANT_Utf8) {
124                 Symbol sym = cpool.getSymbolAt(ci);
125                 utf8ToIndex.put(sym.asString(), new Short((short) ci));
126             }
127             else if(cpConstType == JVM_CONSTANT_Long ||
128                       cpConstType == JVM_CONSTANT_Double) {
129                 ci++;
130             }
131         }
132 
133         // remember index of attribute name modified UTF-8 strings
134 
135         // class attributes
136         Short sourceFileIndex = (Short) utf8ToIndex.get("SourceFile");
137         _sourceFileIndex = (sourceFileIndex != null)? sourceFileIndex.shortValue() : 0;
138         if (DEBUG) debugMessage("SourceFile index = " + _sourceFileIndex);
139 
140         Short innerClassesIndex = (Short) utf8ToIndex.get("InnerClasses");
141         _innerClassesIndex = (innerClassesIndex != null)? innerClassesIndex.shortValue() : 0;
142         if (DEBUG) debugMessage("InnerClasses index = " + _innerClassesIndex);
143 
144         // field attributes
145         Short constantValueIndex = (Short) utf8ToIndex.get("ConstantValue");
146         _constantValueIndex = (constantValueIndex != null)?
147                                           constantValueIndex.shortValue() : 0;
148         if (DEBUG) debugMessage("ConstantValue index = " + _constantValueIndex);
149 
150         Short syntheticIndex = (Short) utf8ToIndex.get("Synthetic");
151         _syntheticIndex = (syntheticIndex != null)? syntheticIndex.shortValue() : 0;
152         if (DEBUG) debugMessage("Synthetic index = " + _syntheticIndex);
153 
154         Short deprecatedIndex = (Short) utf8ToIndex.get("Deprecated");
155         _deprecatedIndex = (deprecatedIndex != null)? deprecatedIndex.shortValue() : 0;
156         if (DEBUG) debugMessage("Deprecated index = " + _deprecatedIndex);
157 
158         // method attributes
159         Short codeIndex = (Short) utf8ToIndex.get("Code");
160         _codeIndex = (codeIndex != null)? codeIndex.shortValue() : 0;
161         if (DEBUG) debugMessage("Code index = " + _codeIndex);
162 
163         Short exceptionsIndex = (Short) utf8ToIndex.get("Exceptions");
164         _exceptionsIndex = (exceptionsIndex != null)? exceptionsIndex.shortValue() : 0;
165         if (DEBUG) debugMessage("Exceptions index = " + _exceptionsIndex);
166 
167         // Short syntheticIndex = (Short) utf8ToIndex.get("Synthetic");
168         // Short deprecatedIndex = (Short) utf8ToIndex.get("Deprecated");
169 
170         // Code attributes
171         Short lineNumberTableIndex = (Short) utf8ToIndex.get("LineNumberTable");
172         _lineNumberTableIndex = (lineNumberTableIndex != null)?
173                                        lineNumberTableIndex.shortValue() : 0;
174         if (DEBUG) debugMessage("LineNumberTable index = " + _lineNumberTableIndex);
175 
176         Short localVariableTableIndex = (Short) utf8ToIndex.get("LocalVariableTable");
177         _localVariableTableIndex = (localVariableTableIndex != null)?
178                                        localVariableTableIndex.shortValue() : 0;
179         if (DEBUG) debugMessage("LocalVariableTable index = " + _localVariableTableIndex);
180 
181         Short signatureIdx = (Short) utf8ToIndex.get("Signature");
182         _signatureIndex = (signatureIdx != null)? signatureIdx.shortValue() : 0;
183         if (DEBUG) debugMessage("Signature index = " + _signatureIndex);
184 
185         for(ci = 1; ci < len; ci++) {
186             int cpConstType = tags.at(ci);
187             // write cp_info
188             // write constant type
189             switch(cpConstType) {
190                 case JVM_CONSTANT_Utf8: {
191                      dos.writeByte(cpConstType);
192                      Symbol sym = cpool.getSymbolAt(ci);
193                      dos.writeShort((short)sym.getLength());
194                      dos.write(sym.asByteArray());
195                      if (DEBUG) debugMessage("CP[" + ci + "] = modified UTF-8 " + sym.asString());
196                      break;
197                 }
198 
199                 case JVM_CONSTANT_Unicode:
200                      throw new IllegalArgumentException("Unicode constant!");
201 
202                 case JVM_CONSTANT_Integer:
203                      dos.writeByte(cpConstType);
204                      dos.writeInt(cpool.getIntAt(ci));
205                      if (DEBUG) debugMessage("CP[" + ci + "] = int " + cpool.getIntAt(ci));
206                      break;
207 
208                 case JVM_CONSTANT_Float:
209                      dos.writeByte(cpConstType);
210                      dos.writeFloat(cpool.getFloatAt(ci));
211                      if (DEBUG) debugMessage("CP[" + ci + "] = float " + cpool.getFloatAt(ci));
212                      break;
213 
214                 case JVM_CONSTANT_Long: {
215                      dos.writeByte(cpConstType);
216                      long l = cpool.getLongAt(ci);
217                      // long entries occupy two pool entries
218                      ci++;
219                      dos.writeLong(l);
220                      break;
221                 }
222 
223                 case JVM_CONSTANT_Double:
224                      dos.writeByte(cpConstType);
225                      dos.writeDouble(cpool.getDoubleAt(ci));
226                      // double entries occupy two pool entries
227                      ci++;
228                      break;
229 
230                 case JVM_CONSTANT_Class:
231                 case JVM_CONSTANT_UnresolvedClass:
232                 case JVM_CONSTANT_UnresolvedClassInError: {
233                      dos.writeByte(JVM_CONSTANT_Class);
234                      String klassName = cpool.getKlassNameAt(ci).asString();
235                      Short s = (Short) utf8ToIndex.get(klassName);
236                      classToIndex.put(klassName, new Short((short)ci));
237                      dos.writeShort(s.shortValue());
238                      if (DEBUG) debugMessage("CP[" + ci  + "] = class " + s);
239                      break;
240                 }
241 
242                 case JVM_CONSTANT_String: {
243                      dos.writeByte(cpConstType);
244                      String str = cpool.getUnresolvedStringAt(ci).asString();
245                      Short s = (Short) utf8ToIndex.get(str);
246                      dos.writeShort(s.shortValue());
247                      if (DEBUG) debugMessage("CP[" + ci + "] = string " + s);
248                      break;
249                 }
250 
251                 // all external, internal method/field references
252                 case JVM_CONSTANT_Fieldref:
253                 case JVM_CONSTANT_Methodref:
254                 case JVM_CONSTANT_InterfaceMethodref: {
255                      dos.writeByte(cpConstType);
256                      int value = cpool.getIntAt(ci);
257                      short klassIndex = (short) extractLowShortFromInt(value);
258                      short nameAndTypeIndex = (short) extractHighShortFromInt(value);
259                      dos.writeShort(klassIndex);
260                      dos.writeShort(nameAndTypeIndex);
261                      if (DEBUG) debugMessage("CP[" + ci + "] = ref klass = " +
262                            klassIndex + ", N&T = " + nameAndTypeIndex);
263                      break;
264                 }
265 
266                 case JVM_CONSTANT_NameAndType: {
267                      dos.writeByte(cpConstType);
268                      int value = cpool.getIntAt(ci);
269                      short nameIndex = (short) extractLowShortFromInt(value);
270                      short signatureIndex = (short) extractHighShortFromInt(value);
271                      dos.writeShort(nameIndex);
272                      dos.writeShort(signatureIndex);
273                      if (DEBUG) debugMessage("CP[" + ci + "] = N&T name = " + nameIndex
274                                         + ", type = " + signatureIndex);
275                      break;
276                 }
277 
278                 case JVM_CONSTANT_MethodHandle: {
279                      dos.writeByte(cpConstType);
280                      int value = cpool.getIntAt(ci);
281                      byte refKind = (byte) extractLowShortFromInt(value);
282                      short memberIndex = (short) extractHighShortFromInt(value);
283                      dos.writeByte(refKind);
284                      dos.writeShort(memberIndex);
285                      if (DEBUG) debugMessage("CP[" + ci + "] = MH kind = " +
286                            refKind + ", mem = " + memberIndex);
287                      break;
288                 }
289 
290                 case JVM_CONSTANT_MethodType: {
291                      dos.writeByte(cpConstType);
292                      int value = cpool.getIntAt(ci);
293                      short refIndex = (short) value;
294                      dos.writeShort(refIndex);
295                      if (DEBUG) debugMessage("CP[" + ci + "] = MT index = " + refIndex);
296                      break;
297                 }
298 
299                 case JVM_CONSTANT_InvokeDynamic: {
300                      dos.writeByte(cpConstType);
301                      int value = cpool.getIntAt(ci);
302                      short bsmIndex = (short) extractLowShortFromInt(value);
303                      short nameAndTypeIndex = (short) extractHighShortFromInt(value);
304                      dos.writeShort(bsmIndex);
305                      dos.writeShort(nameAndTypeIndex);
306                      if (DEBUG) debugMessage("CP[" + ci + "] = INDY bsm = " +
307                            bsmIndex + ", N&T = " + nameAndTypeIndex);
308                      break;
309                 }
310 
311                 default:
312                   throw new InternalError("Unknown tag: " + cpConstType);
313             } // switch
314         }
315     }
316 
317     protected void writeClassAccessFlags() throws IOException {
318         int flags = (int)(klass.getAccessFlags() & JVM_RECOGNIZED_CLASS_MODIFIERS);
319         dos.writeShort((short)flags);
320     }
321 
322     protected void writeThisClass() throws IOException {
323         String klassName = klass.getName().asString();
324         Short index = (Short) classToIndex.get(klassName);
325         dos.writeShort(index.shortValue());
326         if (DEBUG) debugMessage("this class = " + index);
327     }
328 
329     protected void writeSuperClass() throws IOException {
330         Klass superKlass = klass.getSuper();
331         if (superKlass != null) { // is not java.lang.Object
332             String superName = superKlass.getName().asString();
333             Short index = (Short) classToIndex.get(superName);
334             if (DEBUG) debugMessage("super class = " + index);
335             dos.writeShort(index.shortValue());
336         } else {
337             dos.writeShort(0); // no super class
338         }
339     }
340     protected void writeInterfaces() throws IOException {
341         KlassArray interfaces = klass.getLocalInterfaces();
342         final int len = interfaces.length();
343 
344         if (DEBUG) debugMessage("number of interfaces = " + len);
345 
346         // write interfaces count
347         dos.writeShort((short) len);
348         for (int i = 0; i < len; i++) {
349            Klass k = interfaces.getAt(i);
350            Short index = (Short) classToIndex.get(k.getName().asString());
351            dos.writeShort(index.shortValue());
352            if (DEBUG) debugMessage("\t" + index);
353         }
354     }
355 
356     protected void writeFields() throws IOException {
357         final int javaFieldsCount = klass.getJavaFieldsCount();
358 
359         // write number of fields
360         dos.writeShort((short) javaFieldsCount);
361 
362         if (DEBUG) debugMessage("number of fields = " + javaFieldsCount);
363 
364         for (int index = 0; index < javaFieldsCount; index++) {
365             short accessFlags    = klass.getFieldAccessFlags(index);
366             dos.writeShort(accessFlags & (short) JVM_RECOGNIZED_FIELD_MODIFIERS);
367 
368             short nameIndex    = klass.getFieldNameIndex(index);
369             dos.writeShort(nameIndex);
370 
371             short signatureIndex = klass.getFieldSignatureIndex(index);
372             dos.writeShort(signatureIndex);
373             if (DEBUG) debugMessage("\tfield name = " + nameIndex + ", signature = " + signatureIndex);
374 
375             short fieldAttributeCount = 0;
376             boolean hasSyn = hasSyntheticAttribute(accessFlags);
377             if (hasSyn)
378                 fieldAttributeCount++;
379 
380             short initvalIndex = klass.getFieldInitialValueIndex(index);
381             if (initvalIndex != 0)
382                 fieldAttributeCount++;
383 
384             short genSigIndex = klass.getFieldGenericSignatureIndex(index);
385             if (genSigIndex != 0)
386                 fieldAttributeCount++;
387 
388             dos.writeShort(fieldAttributeCount);
389 
390             // write synthetic, if applicable
391             if (hasSyn)
392                 writeSynthetic();
393 
394             if (initvalIndex != 0) {
395                 writeIndex(_constantValueIndex);
396                 dos.writeInt(2);
397                 dos.writeShort(initvalIndex);
398                 if (DEBUG) debugMessage("\tfield init value = " + initvalIndex);
399             }
400 
401             if (genSigIndex != 0) {
402                 writeIndex(_signatureIndex);
403                 dos.writeInt(2);
404                 dos.writeShort(genSigIndex);
405                 if (DEBUG) debugMessage("\tfield generic signature index " + genSigIndex);
406             }
407         }
408     }
409 
410     protected boolean isSynthetic(short accessFlags) {
411         return (accessFlags & (short) JVM_ACC_SYNTHETIC) != 0;
412     }
413 
414     protected boolean hasSyntheticAttribute(short accessFlags) {
415         // Check if flags have the attribute and if the constant pool contains an entry for it.
416         return isSynthetic(accessFlags) && _syntheticIndex != 0;
417     }
418 
419     protected void writeSynthetic() throws IOException {
420         writeIndex(_syntheticIndex);
421         dos.writeInt(0);
422     }
423 
424     protected void writeMethods() throws IOException {
425         MethodArray methods = klass.getMethods();
426         final int len = methods.length();
427         // write number of methods
428         dos.writeShort((short) len);
429         if (DEBUG) debugMessage("number of methods = " + len);
430         for (int m = 0; m < len; m++) {
431             writeMethod(methods.at(m));
432         }
433     }
434 
435     protected void writeMethod(Method m) throws IOException {
436         long accessFlags = m.getAccessFlags();
437         dos.writeShort((short) (accessFlags & JVM_RECOGNIZED_METHOD_MODIFIERS));
438         dos.writeShort((short) m.getNameIndex());
439         dos.writeShort((short) m.getSignatureIndex());
440         if (DEBUG) debugMessage("\tmethod name = " + m.getNameIndex() + ", signature = "
441                         + m.getSignatureIndex());
442 
443         final boolean isNative = ((accessFlags & JVM_ACC_NATIVE) != 0);
444         final boolean isAbstract = ((accessFlags & JVM_ACC_ABSTRACT) != 0);
445 
446         short methodAttributeCount = 0;
447 
448         final boolean hasSyn = hasSyntheticAttribute((short)accessFlags);
449         if (hasSyn)
450             methodAttributeCount++;
451 
452         final boolean hasCheckedExceptions = m.hasCheckedExceptions();
453         if (hasCheckedExceptions)
454             methodAttributeCount++;
455 
456         final boolean isCodeAvailable = (!isNative) && (!isAbstract);
457         if (isCodeAvailable)
458             methodAttributeCount++;
459 
460         final boolean isGeneric = (m.getGenericSignature() != null);
461         if (isGeneric)
462             methodAttributeCount++;
463 
464         dos.writeShort(methodAttributeCount);
465         if (DEBUG) debugMessage("\tmethod attribute count = " + methodAttributeCount);
466 
467         if (hasSyn) {
468             if (DEBUG) debugMessage("\tmethod is synthetic");
469             writeSynthetic();
470         }
471 
472         if (isCodeAvailable) {
473             byte[] code = m.getByteCode();
474             short codeAttrCount = 0;
475             int codeSize  = 2           /* max_stack   */ +
476                             2           /* max_locals  */ +
477                             4           /* code_length */ +
478                             code.length /* code        */ +
479                             2           /* exp. table len.  */ +
480                             2           /* code attr. count */;
481 
482             boolean hasExceptionTable = m.hasExceptionTable();
483             ExceptionTableElement[] exceptionTable = null;
484             int exceptionTableLen = 0;
485             if (hasExceptionTable) {
486                 exceptionTable = m.getExceptionTable();
487                 exceptionTableLen = exceptionTable.length;
488                 if (DEBUG) debugMessage("\tmethod has exception table");
489                 codeSize += exceptionTableLen /* exception table is 4-tuple array */
490                                          * (2 /* start_pc     */ +
491                                             2 /* end_pc       */ +
492                                             2 /* handler_pc   */ +
493                                             2 /* catch_type   */);
494             }
495 
496             boolean hasLineNumberTable = m.hasLineNumberTable();
497             LineNumberTableElement[] lineNumberTable = null;
498             int lineNumberAttrLen = 0;
499 
500             if (hasLineNumberTable) {
501                 if (DEBUG) debugMessage("\tmethod has line number table");
502                 lineNumberTable = m.getLineNumberTable();
503                 if (DEBUG) debugMessage("\t\tline table length = " + lineNumberTable.length);
504 
505                 lineNumberAttrLen = 2 /* line number table length         */ +
506                            lineNumberTable.length * (2 /* start_pc */ + 2 /* line_number */);
507 
508                 codeSize += 2 /* line number table attr index     */ +
509                             4 /* line number table attr length    */ +
510                             lineNumberAttrLen;
511 
512                 if (DEBUG) debugMessage("\t\tline number table attr size = " +
513                                               lineNumberAttrLen);
514 
515                 codeAttrCount++;
516             }
517 
518             boolean hasLocalVariableTable = m.hasLocalVariableTable();
519             LocalVariableTableElement[] localVariableTable = null;
520             int localVarAttrLen = 0;
521 
522             if (hasLocalVariableTable) {
523                 if (DEBUG) debugMessage("\tmethod has local variable table");
524                 localVariableTable = m.getLocalVariableTable();
525                 if (DEBUG) debugMessage("\t\tlocal variable table length = "
526                               + localVariableTable.length);
527                 localVarAttrLen =
528                                2 /* local variable table length      */ +
529                                localVariableTable.length * ( 2 /* start_pc          */ +
530                                                           2 /* length            */ +
531                                                           2 /* name_index        */ +
532                                                           2 /* signature_index   */ +
533                                                           2 /* variable index    */ );
534 
535                 if (DEBUG) debugMessage("\t\tlocal variable attr size = " +
536                                               localVarAttrLen);
537 
538                 codeSize += 2 /* local variable table attr index  */ +
539                             4 /* local variable table attr length */ +
540                             localVarAttrLen;
541 
542                 codeAttrCount++;
543             }
544 
545             // fix ConstantPoolCache indices to ConstantPool indices.
546             rewriteByteCode(m, code);
547 
548             // start writing Code
549 
550             writeIndex(_codeIndex);
551 
552             dos.writeInt(codeSize);
553             if (DEBUG) debugMessage("\tcode attribute length = " + codeSize);
554 
555             dos.writeShort((short) m.getMaxStack());
556             if (DEBUG) debugMessage("\tmax stack = " + m.getMaxStack());
557 
558             dos.writeShort((short) m.getMaxLocals());
559             if (DEBUG) debugMessage("\tmax locals = " + m.getMaxLocals());
560 
561             dos.writeInt(code.length);
562             if (DEBUG) debugMessage("\tcode size = " + code.length);
563 
564             dos.write(code);
565 
566             // write exception table size
567             dos.writeShort((short) exceptionTableLen);
568             if (DEBUG) debugMessage("\texception table length = " + exceptionTableLen);
569 
570             if (exceptionTableLen != 0) {
571                 for (int e = 0; e < exceptionTableLen; e++) {
572                      dos.writeShort((short) exceptionTable[e].getStartPC());
573                      dos.writeShort((short) exceptionTable[e].getEndPC());
574                      dos.writeShort((short) exceptionTable[e].getHandlerPC());
575                      dos.writeShort((short) exceptionTable[e].getCatchTypeIndex());
576                 }
577             }
578 
579             dos.writeShort((short)codeAttrCount);
580             if (DEBUG) debugMessage("\tcode attribute count = " + codeAttrCount);
581 
582             // write LineNumberTable, if available.
583             if (hasLineNumberTable) {
584                 writeIndex(_lineNumberTableIndex);
585                 dos.writeInt(lineNumberAttrLen);
586                 dos.writeShort((short) lineNumberTable.length);
587                 for (int l = 0; l < lineNumberTable.length; l++) {
588                      dos.writeShort((short) lineNumberTable[l].getStartBCI());
589                      dos.writeShort((short) lineNumberTable[l].getLineNumber());
590                 }
591             }
592 
593             // write LocalVariableTable, if available.
594             if (hasLocalVariableTable) {
595                 writeIndex((short) _localVariableTableIndex);
596                 dos.writeInt(localVarAttrLen);
597                 dos.writeShort((short) localVariableTable.length);
598                 for (int l = 0; l < localVariableTable.length; l++) {
599                      dos.writeShort((short) localVariableTable[l].getStartBCI());
600                      dos.writeShort((short) localVariableTable[l].getLength());
601                      dos.writeShort((short) localVariableTable[l].getNameCPIndex());
602                      dos.writeShort((short) localVariableTable[l].getDescriptorCPIndex());
603                      dos.writeShort((short) localVariableTable[l].getSlot());
604                 }
605             }
606         }
607 
608         if (hasCheckedExceptions) {
609             CheckedExceptionElement[] exceptions = m.getCheckedExceptions();
610             writeIndex(_exceptionsIndex);
611 
612             int attrSize = 2 /* number_of_exceptions */ +
613                            exceptions.length * 2 /* exception_index */;
614             dos.writeInt(attrSize);
615             dos.writeShort(exceptions.length);
616             if (DEBUG) debugMessage("\tmethod has " + exceptions.length
617                                         +  " checked exception(s)");
618             for (int e = 0; e < exceptions.length; e++) {
619                  short cpIndex = (short) exceptions[e].getClassCPIndex();
620                  dos.writeShort(cpIndex);
621             }
622         }
623 
624         if (isGeneric) {
625            writeGenericSignature(m.getGenericSignature().asString());
626         }
627     }
628 
629     protected void rewriteByteCode(Method m, byte[] code) {
630         ByteCodeRewriter r = new ByteCodeRewriter(m, cpool, code);
631         r.rewrite();
632     }
633 
634     protected void writeGenericSignature(String signature) throws IOException {
635         writeIndex(_signatureIndex);
636         if (DEBUG) debugMessage("signature attribute = " + _signatureIndex);
637         dos.writeInt(2);
638         Short index = (Short) utf8ToIndex.get(signature);
639         dos.writeShort(index.shortValue());
640         if (DEBUG) debugMessage("generic signature = " + index);
641     }
642 
643     protected void writeClassAttributes() throws IOException {
644         final long flags = klass.getAccessFlags();
645         final boolean hasSyn = hasSyntheticAttribute((short) flags);
646 
647         // check for source file
648         short classAttributeCount = 0;
649 
650         if (hasSyn)
651             classAttributeCount++;
652 
653         Symbol sourceFileName = klass.getSourceFileName();
654         if (sourceFileName != null)
655             classAttributeCount++;
656 
657         Symbol genericSignature = klass.getGenericSignature();
658         if (genericSignature != null)
659             classAttributeCount++;
660 
661         U2Array innerClasses = klass.getInnerClasses();
662         final int numInnerClasses = (int) (innerClasses.length() / 4);
663         if (numInnerClasses != 0)
664             classAttributeCount++;
665 
666         dos.writeShort(classAttributeCount);
667         if (DEBUG) debugMessage("class attribute count = " + classAttributeCount);
668 
669         if (hasSyn)
670             writeSynthetic();
671 
672         // write SourceFile, if any
673         if (sourceFileName != null) {
674             writeIndex(_sourceFileIndex);
675             if (DEBUG) debugMessage("source file attribute = " + _sourceFileIndex);
676             dos.writeInt(2);
677             Short index = (Short) utf8ToIndex.get(sourceFileName.asString());
678             dos.writeShort(index.shortValue());
679             if (DEBUG) debugMessage("source file name = " + index);
680         }
681 
682         // write Signature, if any
683         if (genericSignature != null) {
684             writeGenericSignature(genericSignature.asString());
685         }
686 
687         // write inner classes, if any
688         if (numInnerClasses != 0) {
689             writeIndex(_innerClassesIndex);
690             final int innerAttrLen = 2 /* number_of_inner_classes */ +
691                                      numInnerClasses * (
692                                                  2 /* inner_class_info_index */ +
693                                                  2 /* outer_class_info_index */ +
694                                                  2 /* inner_class_name_index */ +
695                                                  2 /* inner_class_access_flags */);
696             dos.writeInt(innerAttrLen);
697 
698             dos.writeShort(numInnerClasses);
699             if (DEBUG) debugMessage("class has " + numInnerClasses + " inner class entries");
700 
701             for (int index = 0; index < numInnerClasses * 4; index++) {
702                 dos.writeShort(innerClasses.at(index));
703             }
704         }
705     }
706 }