1 /*
  2  * Copyright (c) 2002, 2022, 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(), (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 = utf8ToIndex.get("SourceFile");
137         _sourceFileIndex = (sourceFileIndex != null)? sourceFileIndex.shortValue() : 0;
138         if (DEBUG) debugMessage("SourceFile index = " + _sourceFileIndex);
139 
140         Short innerClassesIndex = utf8ToIndex.get("InnerClasses");
141         _innerClassesIndex = (innerClassesIndex != null)? innerClassesIndex.shortValue() : 0;
142         if (DEBUG) debugMessage("InnerClasses index = " + _innerClassesIndex);
143 
144         // field attributes
145         Short constantValueIndex = utf8ToIndex.get("ConstantValue");
146         _constantValueIndex = (constantValueIndex != null)?
147                                           constantValueIndex.shortValue() : 0;
148         if (DEBUG) debugMessage("ConstantValue index = " + _constantValueIndex);
149 
150         Short syntheticIndex = utf8ToIndex.get("Synthetic");
151         _syntheticIndex = (syntheticIndex != null)? syntheticIndex.shortValue() : 0;
152         if (DEBUG) debugMessage("Synthetic index = " + _syntheticIndex);
153 
154         Short deprecatedIndex = utf8ToIndex.get("Deprecated");
155         _deprecatedIndex = (deprecatedIndex != null)? deprecatedIndex.shortValue() : 0;
156         if (DEBUG) debugMessage("Deprecated index = " + _deprecatedIndex);
157 
158         // method attributes
159         Short codeIndex = utf8ToIndex.get("Code");
160         _codeIndex = (codeIndex != null)? codeIndex.shortValue() : 0;
161         if (DEBUG) debugMessage("Code index = " + _codeIndex);
162 
163         Short exceptionsIndex = 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 = utf8ToIndex.get("LineNumberTable");
172         _lineNumberTableIndex = (lineNumberTableIndex != null)?
173                                        lineNumberTableIndex.shortValue() : 0;
174         if (DEBUG) debugMessage("LineNumberTable index = " + _lineNumberTableIndex);
175 
176         Short localVariableTableIndex = utf8ToIndex.get("LocalVariableTable");
177         _localVariableTableIndex = (localVariableTableIndex != null)?
178                                        localVariableTableIndex.shortValue() : 0;
179         if (DEBUG) debugMessage("LocalVariableTable index = " + _localVariableTableIndex);
180 
181         Short signatureIdx = 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 = utf8ToIndex.get(klassName);
236                      classToIndex.put(klassName, (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 = 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_Dynamic: {
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 + "] = CONDY bsm = " +
307                                             bsmIndex + ", N&T = " + nameAndTypeIndex);
308                     break;
309                 }
310 
311                 case JVM_CONSTANT_InvokeDynamic: {
312                      dos.writeByte(cpConstType);
313                      int value = cpool.getIntAt(ci);
314                      short bsmIndex = (short) extractLowShortFromInt(value);
315                      short nameAndTypeIndex = (short) extractHighShortFromInt(value);
316                      dos.writeShort(bsmIndex);
317                      dos.writeShort(nameAndTypeIndex);
318                      if (DEBUG) debugMessage("CP[" + ci + "] = INDY bsm = " +
319                            bsmIndex + ", N&T = " + nameAndTypeIndex);
320                      break;
321                 }
322 
323                 default:
324                   throw new InternalError("Unknown tag: " + cpConstType);
325             } // switch
326         }
327     }
328 
329     protected void writeClassAccessFlags() throws IOException {
330         int flags = (int)(klass.getAccessFlags() & JVM_RECOGNIZED_CLASS_MODIFIERS);
331         dos.writeShort((short)flags);
332     }
333 
334     protected void writeThisClass() throws IOException {
335         String klassName = klass.getName().asString();
336         Short index = classToIndex.get(klassName);
337         dos.writeShort(index.shortValue());
338         if (DEBUG) debugMessage("this class = " + index);
339     }
340 
341     protected void writeSuperClass() throws IOException {
342         Klass superKlass = klass.getSuper();
343         if (superKlass != null) { // is not java.lang.Object
344             String superName = superKlass.getName().asString();
345             Short index = classToIndex.get(superName);
346             if (DEBUG) debugMessage("super class = " + index);
347             dos.writeShort(index.shortValue());
348         } else {
349             dos.writeShort(0); // no super class
350         }
351     }
352     protected void writeInterfaces() throws IOException {
353         KlassArray interfaces = klass.getLocalInterfaces();
354         final int len = interfaces.length();
355         int nb_interfaces = len;
356 
357         if (DEBUG) debugMessage("number of interfaces = " + nb_interfaces);
358 
359         // write interfaces count
360         dos.writeShort((short) nb_interfaces);
361         for (int i = 0; i < len; i++) {
362            Klass k = interfaces.getAt(i);
363            Short index = classToIndex.get(k.getName().asString());
364            dos.writeShort(index.shortValue());
365            if (DEBUG) debugMessage("\t" + index);
366         }
367     }
368 
369     protected void writeFields() throws IOException {
370         final int javaFieldsCount = klass.getJavaFieldsCount();
371 
372         // write number of fields
373         dos.writeShort((short) javaFieldsCount);
374 
375         if (DEBUG) debugMessage("number of fields = " + javaFieldsCount);
376 
377         for (int index = 0; index < javaFieldsCount; index++) {
378             short accessFlags    = klass.getFieldAccessFlags(index);
379             dos.writeShort(accessFlags & (short) JVM_RECOGNIZED_FIELD_MODIFIERS);
380 
381             short nameIndex    = klass.getFieldNameIndex(index);
382             dos.writeShort(nameIndex);
383 
384             short signatureIndex = klass.getFieldSignatureIndex(index);
385             dos.writeShort(signatureIndex);
386             if (DEBUG) debugMessage("\tfield name = " + nameIndex + ", signature = " + signatureIndex);
387 
388             short fieldAttributeCount = 0;
389             boolean hasSyn = hasSyntheticAttribute(accessFlags);
390             if (hasSyn)
391                 fieldAttributeCount++;
392 
393             short initvalIndex = klass.getFieldInitialValueIndex(index);
394             if (initvalIndex != 0)
395                 fieldAttributeCount++;
396 
397             short genSigIndex = klass.getFieldGenericSignatureIndex(index);
398             if (genSigIndex != 0)
399                 fieldAttributeCount++;
400 
401             dos.writeShort(fieldAttributeCount);
402 
403             // write synthetic, if applicable
404             if (hasSyn)
405                 writeSynthetic();
406 
407             if (initvalIndex != 0) {
408                 writeIndex(_constantValueIndex);
409                 dos.writeInt(2);
410                 dos.writeShort(initvalIndex);
411                 if (DEBUG) debugMessage("\tfield init value = " + initvalIndex);
412             }
413 
414             if (genSigIndex != 0) {
415                 writeIndex(_signatureIndex);
416                 dos.writeInt(2);
417                 dos.writeShort(genSigIndex);
418                 if (DEBUG) debugMessage("\tfield generic signature index " + genSigIndex);
419             }
420         }
421     }
422 
423     protected boolean isSynthetic(short accessFlags) {
424         return (accessFlags & (short) JVM_ACC_SYNTHETIC) != 0;
425     }
426 
427     protected boolean hasSyntheticAttribute(short accessFlags) {
428         // Check if flags have the attribute and if the constant pool contains an entry for it.
429         return isSynthetic(accessFlags) && _syntheticIndex != 0;
430     }
431 
432     protected void writeSynthetic() throws IOException {
433         writeIndex(_syntheticIndex);
434         dos.writeInt(0);
435     }
436 
437     protected void writeMethods() throws IOException {
438         MethodArray methods = klass.getMethods();
439         ArrayList<Method> valid_methods = new ArrayList<Method>();
440         for (int i = 0; i < methods.length(); i++) {
441             Method m = methods.at(i);
442             long accessFlags = m.getAccessFlags();
443             // overpass method
444             if (accessFlags == (JVM_ACC_PUBLIC | JVM_ACC_SYNTHETIC | JVM_ACC_BRIDGE)) {
445                 continue;
446             }
447             valid_methods.add(m);
448         }
449         final int len = valid_methods.size();
450         // write number of methods
451         dos.writeShort((short) len);
452         if (DEBUG) debugMessage("number of methods = " + len);
453         for (int m = 0; m < len; m++) {
454             writeMethod(valid_methods.get(m));
455         }
456     }
457 
458     protected void writeMethod(Method m) throws IOException {
459         long accessFlags = m.getAccessFlags();
460         dos.writeShort((short) (accessFlags & JVM_RECOGNIZED_METHOD_MODIFIERS));
461         dos.writeShort((short) m.getNameIndex());
462         dos.writeShort((short) m.getSignatureIndex());
463         if (DEBUG) debugMessage("\tmethod name = " + m.getNameIndex() + ", signature = "
464                         + m.getSignatureIndex());
465 
466         final boolean isNative = ((accessFlags & JVM_ACC_NATIVE) != 0);
467         final boolean isAbstract = ((accessFlags & JVM_ACC_ABSTRACT) != 0);
468 
469         short methodAttributeCount = 0;
470 
471         final boolean hasSyn = hasSyntheticAttribute((short)accessFlags);
472         if (hasSyn)
473             methodAttributeCount++;
474 
475         final boolean hasCheckedExceptions = m.hasCheckedExceptions();
476         if (hasCheckedExceptions)
477             methodAttributeCount++;
478 
479         final boolean isCodeAvailable = (!isNative) && (!isAbstract);
480         if (isCodeAvailable)
481             methodAttributeCount++;
482 
483         final boolean isGeneric = (m.getGenericSignature() != null);
484         if (isGeneric)
485             methodAttributeCount++;
486 
487         dos.writeShort(methodAttributeCount);
488         if (DEBUG) debugMessage("\tmethod attribute count = " + methodAttributeCount);
489 
490         if (hasSyn) {
491             if (DEBUG) debugMessage("\tmethod is synthetic");
492             writeSynthetic();
493         }
494 
495         if (isCodeAvailable) {
496             byte[] code = m.getByteCode();
497             short codeAttrCount = 0;
498             int codeSize  = 2           /* max_stack   */ +
499                             2           /* max_locals  */ +
500                             4           /* code_length */ +
501                             code.length /* code        */ +
502                             2           /* exp. table len.  */ +
503                             2           /* code attr. count */;
504 
505             boolean hasExceptionTable = m.hasExceptionTable();
506             ExceptionTableElement[] exceptionTable = null;
507             int exceptionTableLen = 0;
508             if (hasExceptionTable) {
509                 exceptionTable = m.getExceptionTable();
510                 exceptionTableLen = exceptionTable.length;
511                 if (DEBUG) debugMessage("\tmethod has exception table");
512                 codeSize += exceptionTableLen /* exception table is 4-tuple array */
513                                          * (2 /* start_pc     */ +
514                                             2 /* end_pc       */ +
515                                             2 /* handler_pc   */ +
516                                             2 /* catch_type   */);
517             }
518 
519             boolean hasLineNumberTable = m.hasLineNumberTable();
520             LineNumberTableElement[] lineNumberTable = null;
521             int lineNumberAttrLen = 0;
522 
523             if (hasLineNumberTable) {
524                 if (DEBUG) debugMessage("\tmethod has line number table");
525                 lineNumberTable = m.getLineNumberTable();
526                 if (DEBUG) debugMessage("\t\tline table length = " + lineNumberTable.length);
527 
528                 lineNumberAttrLen = 2 /* line number table length         */ +
529                            lineNumberTable.length * (2 /* start_pc */ + 2 /* line_number */);
530 
531                 codeSize += 2 /* line number table attr index     */ +
532                             4 /* line number table attr length    */ +
533                             lineNumberAttrLen;
534 
535                 if (DEBUG) debugMessage("\t\tline number table attr size = " +
536                                               lineNumberAttrLen);
537 
538                 codeAttrCount++;
539             }
540 
541             boolean hasLocalVariableTable = m.hasLocalVariableTable();
542             LocalVariableTableElement[] localVariableTable = null;
543             int localVarAttrLen = 0;
544 
545             if (hasLocalVariableTable) {
546                 if (DEBUG) debugMessage("\tmethod has local variable table");
547                 localVariableTable = m.getLocalVariableTable();
548                 if (DEBUG) debugMessage("\t\tlocal variable table length = "
549                               + localVariableTable.length);
550                 localVarAttrLen =
551                                2 /* local variable table length      */ +
552                                localVariableTable.length * ( 2 /* start_pc          */ +
553                                                           2 /* length            */ +
554                                                           2 /* name_index        */ +
555                                                           2 /* signature_index   */ +
556                                                           2 /* variable index    */ );
557 
558                 if (DEBUG) debugMessage("\t\tlocal variable attr size = " +
559                                               localVarAttrLen);
560 
561                 codeSize += 2 /* local variable table attr index  */ +
562                             4 /* local variable table attr length */ +
563                             localVarAttrLen;
564 
565                 codeAttrCount++;
566             }
567 
568             // fix ConstantPoolCache indices to ConstantPool indices.
569             rewriteByteCode(m, code);
570 
571             // start writing Code
572 
573             writeIndex(_codeIndex);
574 
575             dos.writeInt(codeSize);
576             if (DEBUG) debugMessage("\tcode attribute length = " + codeSize);
577 
578             dos.writeShort((short) m.getMaxStack());
579             if (DEBUG) debugMessage("\tmax stack = " + m.getMaxStack());
580 
581             dos.writeShort((short) m.getMaxLocals());
582             if (DEBUG) debugMessage("\tmax locals = " + m.getMaxLocals());
583 
584             dos.writeInt(code.length);
585             if (DEBUG) debugMessage("\tcode size = " + code.length);
586 
587             dos.write(code);
588 
589             // write exception table size
590             dos.writeShort((short) exceptionTableLen);
591             if (DEBUG) debugMessage("\texception table length = " + exceptionTableLen);
592 
593             if (exceptionTableLen != 0) {
594                 for (int e = 0; e < exceptionTableLen; e++) {
595                      dos.writeShort((short) exceptionTable[e].getStartPC());
596                      dos.writeShort((short) exceptionTable[e].getEndPC());
597                      dos.writeShort((short) exceptionTable[e].getHandlerPC());
598                      dos.writeShort((short) exceptionTable[e].getCatchTypeIndex());
599                 }
600             }
601 
602             dos.writeShort(codeAttrCount);
603             if (DEBUG) debugMessage("\tcode attribute count = " + codeAttrCount);
604 
605             // write LineNumberTable, if available.
606             if (hasLineNumberTable) {
607                 writeIndex(_lineNumberTableIndex);
608                 dos.writeInt(lineNumberAttrLen);
609                 dos.writeShort((short) lineNumberTable.length);
610                 for (int l = 0; l < lineNumberTable.length; l++) {
611                      dos.writeShort((short) lineNumberTable[l].getStartBCI());
612                      dos.writeShort((short) lineNumberTable[l].getLineNumber());
613                 }
614             }
615 
616             // write LocalVariableTable, if available.
617             if (hasLocalVariableTable) {
618                 writeIndex(_localVariableTableIndex);
619                 dos.writeInt(localVarAttrLen);
620                 dos.writeShort((short) localVariableTable.length);
621                 for (int l = 0; l < localVariableTable.length; l++) {
622                      dos.writeShort((short) localVariableTable[l].getStartBCI());
623                      dos.writeShort((short) localVariableTable[l].getLength());
624                      dos.writeShort((short) localVariableTable[l].getNameCPIndex());
625                      dos.writeShort((short) localVariableTable[l].getDescriptorCPIndex());
626                      dos.writeShort((short) localVariableTable[l].getSlot());
627                 }
628             }
629         }
630 
631         if (hasCheckedExceptions) {
632             CheckedExceptionElement[] exceptions = m.getCheckedExceptions();
633             writeIndex(_exceptionsIndex);
634 
635             int attrSize = 2 /* number_of_exceptions */ +
636                            exceptions.length * 2 /* exception_index */;
637             dos.writeInt(attrSize);
638             dos.writeShort(exceptions.length);
639             if (DEBUG) debugMessage("\tmethod has " + exceptions.length
640                                         +  " checked exception(s)");
641             for (int e = 0; e < exceptions.length; e++) {
642                  short cpIndex = (short) exceptions[e].getClassCPIndex();
643                  dos.writeShort(cpIndex);
644             }
645         }
646 
647         if (isGeneric) {
648            writeGenericSignature(m.getGenericSignature().asString());
649         }
650     }
651 
652     protected void rewriteByteCode(Method m, byte[] code) {
653         ByteCodeRewriter r = new ByteCodeRewriter(m, cpool, code);
654         r.rewrite();
655     }
656 
657     protected void writeGenericSignature(String signature) throws IOException {
658         writeIndex(_signatureIndex);
659         if (DEBUG) debugMessage("signature attribute = " + _signatureIndex);
660         dos.writeInt(2);
661         Short index = utf8ToIndex.get(signature);
662         dos.writeShort(index.shortValue());
663         if (DEBUG) debugMessage("generic signature = " + index);
664     }
665 
666     protected void writeClassAttributes() throws IOException {
667         final long flags = klass.getAccessFlags();
668         final boolean hasSyn = hasSyntheticAttribute((short) flags);
669 
670         // check for source file
671         short classAttributeCount = 0;
672 
673         if (hasSyn)
674             classAttributeCount++;
675 
676         Symbol sourceFileName = klass.getSourceFileName();
677         if (sourceFileName != null)
678             classAttributeCount++;
679 
680         Symbol genericSignature = klass.getGenericSignature();
681         if (genericSignature != null)
682             classAttributeCount++;
683 
684         U2Array innerClasses = klass.getInnerClasses();
685         final int numInnerClasses = innerClasses.length() / 4;
686         if (numInnerClasses != 0)
687             classAttributeCount++;
688 
689         dos.writeShort(classAttributeCount);
690         if (DEBUG) debugMessage("class attribute count = " + classAttributeCount);
691 
692         if (hasSyn)
693             writeSynthetic();
694 
695         // write SourceFile, if any
696         if (sourceFileName != null) {
697             writeIndex(_sourceFileIndex);
698             if (DEBUG) debugMessage("source file attribute = " + _sourceFileIndex);
699             dos.writeInt(2);
700             Short index = utf8ToIndex.get(sourceFileName.asString());
701             dos.writeShort(index.shortValue());
702             if (DEBUG) debugMessage("source file name = " + index);
703         }
704 
705         // write Signature, if any
706         if (genericSignature != null) {
707             writeGenericSignature(genericSignature.asString());
708         }
709 
710         // write inner classes, if any
711         if (numInnerClasses != 0) {
712             writeIndex(_innerClassesIndex);
713             final int innerAttrLen = 2 /* number_of_inner_classes */ +
714                                      numInnerClasses * (
715                                                  2 /* inner_class_info_index */ +
716                                                  2 /* outer_class_info_index */ +
717                                                  2 /* inner_class_name_index */ +
718                                                  2 /* inner_class_access_flags */);
719             dos.writeInt(innerAttrLen);
720 
721             dos.writeShort(numInnerClasses);
722             if (DEBUG) debugMessage("class has " + numInnerClasses + " inner class entries");
723 
724             for (int index = 0; index < numInnerClasses * 4; index++) {
725                 dos.writeShort(innerClasses.at(index));
726             }
727         }
728     }
729 }