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