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