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