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