1 /*
  2  * Copyright (c) 2002, 2023, 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  _nestHostIndex;
 56     protected short  _nestMembersIndex;
 57     protected short  _syntheticIndex;
 58     protected short  _deprecatedIndex;
 59     protected short  _constantValueIndex;
 60     protected short  _codeIndex;
 61     protected short  _exceptionsIndex;
 62     protected short  _stackMapTableIndex;
 63     protected short  _lineNumberTableIndex;
 64     protected short  _localVariableTableIndex;
 65     protected short  _signatureIndex;
 66     protected short  _bootstrapMethodsIndex;
 67 
 68     protected static int extractHighShortFromInt(int val) {
 69         // must stay in sync with ConstantPool::name_and_type_at_put, method_at_put, etc.
 70         return (val >> 16) & 0xFFFF;
 71     }
 72 
 73     protected static int extractLowShortFromInt(int val) {
 74         // must stay in sync with ConstantPool::name_and_type_at_put, method_at_put, etc.
 75         return val & 0xFFFF;
 76     }
 77 
 78     public ClassWriter(InstanceKlass kls, OutputStream os) {
 79         klass = kls;
 80         dos = new DataOutputStream(os);
 81         cpool = klass.getConstants();
 82     }
 83 
 84     public void write() throws IOException {
 85         if (DEBUG) debugMessage("class name = " + klass.getName().asString());
 86 
 87         // write magic
 88         dos.writeInt(0xCAFEBABE);
 89 
 90         writeVersion();
 91         writeConstantPool();
 92         writeClassAccessFlags();
 93         writeThisClass();
 94         writeSuperClass();
 95         writeInterfaces();
 96         writeFields();
 97         writeMethods();
 98         writeClassAttributes();
 99 
100         // flush output
101         dos.flush();
102     }
103 
104     protected void writeVersion() throws IOException {
105         dos.writeShort((short)klass.minorVersion());
106         dos.writeShort((short)klass.majorVersion());
107     }
108 
109     protected void writeIndex(int index) throws IOException {
110         if (index == 0) throw new InternalError();
111         dos.writeShort(index);
112     }
113 
114     protected void writeConstantPool() throws IOException {
115         final U1Array tags = cpool.getTags();
116         final long len = tags.length();
117         dos.writeShort((short) len);
118 
119         if (DEBUG) debugMessage("constant pool length = " + len);
120 
121         int ci = 0; // constant pool index
122 
123         // collect all modified UTF-8 Strings from Constant Pool
124 
125         for (ci = 1; ci < len; ci++) {
126             int cpConstType = tags.at(ci);
127             if(cpConstType == JVM_CONSTANT_Utf8) {
128                 Symbol sym = cpool.getSymbolAt(ci);
129                 utf8ToIndex.put(sym.asString(), (short) ci);
130             }
131             else if(cpConstType == JVM_CONSTANT_Long ||
132                       cpConstType == JVM_CONSTANT_Double) {
133                 ci++;
134             }
135         }
136 
137         // remember index of attribute name modified UTF-8 strings
138 
139         // class attributes
140         Short sourceFileIndex = utf8ToIndex.get("SourceFile");
141         _sourceFileIndex = (sourceFileIndex != null)? sourceFileIndex.shortValue() : 0;
142         if (DEBUG) debugMessage("SourceFile index = " + _sourceFileIndex);
143 
144         Short innerClassesIndex = utf8ToIndex.get("InnerClasses");
145         _innerClassesIndex = (innerClassesIndex != null)? innerClassesIndex.shortValue() : 0;
146         if (DEBUG) debugMessage("InnerClasses index = " + _innerClassesIndex);
147 
148         Short nestHostIndex = utf8ToIndex.get("NestHost");
149         _nestHostIndex = (nestHostIndex != null)? nestHostIndex.shortValue() : 0;
150         if (DEBUG) debugMessage("NestHost index = " + _nestHostIndex);
151 
152         Short nestMembersIndex = utf8ToIndex.get("NestMembers");
153         _nestMembersIndex = (nestMembersIndex != null)? nestMembersIndex.shortValue() : 0;
154         if (DEBUG) debugMessage("NestMembers index = " + _nestMembersIndex);
155 
156         Short bootstrapMethodsIndex = utf8ToIndex.get("BootstrapMethods");
157         _bootstrapMethodsIndex = (bootstrapMethodsIndex != null) ? bootstrapMethodsIndex.shortValue() : 0;
158         // field attributes
159         Short constantValueIndex = utf8ToIndex.get("ConstantValue");
160         _constantValueIndex = (constantValueIndex != null)?
161                                           constantValueIndex.shortValue() : 0;
162         if (DEBUG) debugMessage("ConstantValue index = " + _constantValueIndex);
163 
164         Short syntheticIndex = utf8ToIndex.get("Synthetic");
165         _syntheticIndex = (syntheticIndex != null)? syntheticIndex.shortValue() : 0;
166         if (DEBUG) debugMessage("Synthetic index = " + _syntheticIndex);
167 
168         Short deprecatedIndex = utf8ToIndex.get("Deprecated");
169         _deprecatedIndex = (deprecatedIndex != null)? deprecatedIndex.shortValue() : 0;
170         if (DEBUG) debugMessage("Deprecated index = " + _deprecatedIndex);
171 
172         // method attributes
173         Short codeIndex = utf8ToIndex.get("Code");
174         _codeIndex = (codeIndex != null)? codeIndex.shortValue() : 0;
175         if (DEBUG) debugMessage("Code index = " + _codeIndex);
176 
177         Short exceptionsIndex = utf8ToIndex.get("Exceptions");
178         _exceptionsIndex = (exceptionsIndex != null)? exceptionsIndex.shortValue() : 0;
179         if (DEBUG) debugMessage("Exceptions index = " + _exceptionsIndex);
180 
181         // Short syntheticIndex = (Short) utf8ToIndex.get("Synthetic");
182         // Short deprecatedIndex = (Short) utf8ToIndex.get("Deprecated");
183 
184         // Code attributes
185         Short stackMapTableIndex = utf8ToIndex.get("StackMapTable");
186         _stackMapTableIndex = (stackMapTableIndex != null) ?
187                               stackMapTableIndex.shortValue() : 0;
188         if (DEBUG) debugMessage("StackMapTable index = " + _stackMapTableIndex);
189 
190         Short lineNumberTableIndex = utf8ToIndex.get("LineNumberTable");
191         _lineNumberTableIndex = (lineNumberTableIndex != null)?
192                                        lineNumberTableIndex.shortValue() : 0;
193         if (DEBUG) debugMessage("LineNumberTable index = " + _lineNumberTableIndex);
194 
195         Short localVariableTableIndex = utf8ToIndex.get("LocalVariableTable");
196         _localVariableTableIndex = (localVariableTableIndex != null)?
197                                        localVariableTableIndex.shortValue() : 0;
198         if (DEBUG) debugMessage("LocalVariableTable index = " + _localVariableTableIndex);
199 
200         Short signatureIdx = utf8ToIndex.get("Signature");
201         _signatureIndex = (signatureIdx != null)? signatureIdx.shortValue() : 0;
202         if (DEBUG) debugMessage("Signature index = " + _signatureIndex);
203 
204         for(ci = 1; ci < len; ci++) {
205             int cpConstType = tags.at(ci);
206             // write cp_info
207             // write constant type
208             switch(cpConstType) {
209                 case JVM_CONSTANT_Utf8: {
210                      dos.writeByte(cpConstType);
211                      Symbol sym = cpool.getSymbolAt(ci);
212                      dos.writeShort((short)sym.getLength());
213                      dos.write(sym.asByteArray());
214                      if (DEBUG) debugMessage("CP[" + ci + "] = modified UTF-8 " + sym.asString());
215                      break;
216                 }
217 
218                 case JVM_CONSTANT_Unicode:
219                      throw new IllegalArgumentException("Unicode constant!");
220 
221                 case JVM_CONSTANT_Integer:
222                      dos.writeByte(cpConstType);
223                      dos.writeInt(cpool.getIntAt(ci));
224                      if (DEBUG) debugMessage("CP[" + ci + "] = int " + cpool.getIntAt(ci));
225                      break;
226 
227                 case JVM_CONSTANT_Float:
228                      dos.writeByte(cpConstType);
229                      dos.writeFloat(cpool.getFloatAt(ci));
230                      if (DEBUG) debugMessage("CP[" + ci + "] = float " + cpool.getFloatAt(ci));
231                      break;
232 
233                 case JVM_CONSTANT_Long: {
234                      dos.writeByte(cpConstType);
235                      long l = cpool.getLongAt(ci);
236                      // long entries occupy two pool entries
237                      ci++;
238                      dos.writeLong(l);
239                      break;
240                 }
241 
242                 case JVM_CONSTANT_Double:
243                      dos.writeByte(cpConstType);
244                      dos.writeDouble(cpool.getDoubleAt(ci));
245                      // double entries occupy two pool entries
246                      ci++;
247                      break;
248 
249                 case JVM_CONSTANT_Class:
250                 case JVM_CONSTANT_UnresolvedClass:
251                 case JVM_CONSTANT_UnresolvedClassInError: {
252                      dos.writeByte(JVM_CONSTANT_Class);
253                      String klassName = cpool.getKlassNameAt(ci).asString();
254                      Short s = utf8ToIndex.get(klassName);
255                      classToIndex.put(klassName, (short) ci);
256                      dos.writeShort(s.shortValue());
257                      if (DEBUG) debugMessage("CP[" + ci  + "] = class " + s);
258                      break;
259                 }
260 
261                 case JVM_CONSTANT_String: {
262                      dos.writeByte(cpConstType);
263                      String str = cpool.getUnresolvedStringAt(ci).asString();
264                      Short s = utf8ToIndex.get(str);
265                      dos.writeShort(s.shortValue());
266                      if (DEBUG) debugMessage("CP[" + ci + "] = string " + s);
267                      break;
268                 }
269 
270                 // all external, internal method/field references
271                 case JVM_CONSTANT_Fieldref:
272                 case JVM_CONSTANT_Methodref:
273                 case JVM_CONSTANT_InterfaceMethodref: {
274                      dos.writeByte(cpConstType);
275                      int value = cpool.getIntAt(ci);
276                      short klassIndex = (short) extractLowShortFromInt(value);
277                      short nameAndTypeIndex = (short) extractHighShortFromInt(value);
278                      dos.writeShort(klassIndex);
279                      dos.writeShort(nameAndTypeIndex);
280                      if (DEBUG) debugMessage("CP[" + ci + "] = ref klass = " +
281                            klassIndex + ", N&T = " + nameAndTypeIndex);
282                      break;
283                 }
284 
285                 case JVM_CONSTANT_NameAndType: {
286                      dos.writeByte(cpConstType);
287                      int value = cpool.getIntAt(ci);
288                      short nameIndex = (short) extractLowShortFromInt(value);
289                      short signatureIndex = (short) extractHighShortFromInt(value);
290                      dos.writeShort(nameIndex);
291                      dos.writeShort(signatureIndex);
292                      if (DEBUG) debugMessage("CP[" + ci + "] = N&T name = " + nameIndex
293                                         + ", type = " + signatureIndex);
294                      break;
295                 }
296 
297                 case JVM_CONSTANT_MethodHandle: {
298                      dos.writeByte(cpConstType);
299                      int value = cpool.getIntAt(ci);
300                      byte refKind = (byte) extractLowShortFromInt(value);
301                      short memberIndex = (short) extractHighShortFromInt(value);
302                      dos.writeByte(refKind);
303                      dos.writeShort(memberIndex);
304                      if (DEBUG) debugMessage("CP[" + ci + "] = MH kind = " +
305                            refKind + ", mem = " + memberIndex);
306                      break;
307                 }
308 
309                 case JVM_CONSTANT_MethodType: {
310                      dos.writeByte(cpConstType);
311                      int value = cpool.getIntAt(ci);
312                      short refIndex = (short) value;
313                      dos.writeShort(refIndex);
314                      if (DEBUG) debugMessage("CP[" + ci + "] = MT index = " + refIndex);
315                      break;
316                 }
317 
318                 case JVM_CONSTANT_Dynamic: {
319                     dos.writeByte(cpConstType);
320                     int value = cpool.getIntAt(ci);
321                     short bsmIndex = (short) extractLowShortFromInt(value);
322                     short nameAndTypeIndex = (short) extractHighShortFromInt(value);
323                     dos.writeShort(bsmIndex);
324                     dos.writeShort(nameAndTypeIndex);
325                     if (DEBUG) debugMessage("CP[" + ci + "] = CONDY bsm = " +
326                                             bsmIndex + ", N&T = " + nameAndTypeIndex);
327                     break;
328                 }
329 
330                 case JVM_CONSTANT_InvokeDynamic: {
331                      dos.writeByte(cpConstType);
332                      int value = cpool.getIntAt(ci);
333                      short bsmIndex = (short) extractLowShortFromInt(value);
334                      short nameAndTypeIndex = (short) extractHighShortFromInt(value);
335                      dos.writeShort(bsmIndex);
336                      dos.writeShort(nameAndTypeIndex);
337                      if (DEBUG) debugMessage("CP[" + ci + "] = INDY bsm = " +
338                            bsmIndex + ", N&T = " + nameAndTypeIndex);
339                      break;
340                 }
341 
342                 default:
343                   throw new InternalError("Unknown tag: " + cpConstType);
344             } // switch
345         }
346     }
347 
348     protected void writeClassAccessFlags() throws IOException {
349         int flags = (int)(klass.getAccessFlags() & JVM_RECOGNIZED_CLASS_MODIFIERS);
350         dos.writeShort((short)flags);
351     }
352 
353     protected void writeThisClass() throws IOException {
354         String klassName = klass.getName().asString();
355         Short index = classToIndex.get(klassName);
356         dos.writeShort(index.shortValue());
357         if (DEBUG) debugMessage("this class = " + index);
358     }
359 
360     protected void writeSuperClass() throws IOException {
361         Klass superKlass = klass.getSuper();
362         if (superKlass != null) { // is not java.lang.Object
363             String superName = superKlass.getName().asString();
364             Short index = classToIndex.get(superName);
365             if (DEBUG) debugMessage("super class = " + index);
366             dos.writeShort(index.shortValue());
367         } else {
368             dos.writeShort(0); // no super class
369         }
370     }
371     protected void writeInterfaces() throws IOException {
372         KlassArray interfaces = klass.getLocalInterfaces();
373         final int len = interfaces.length();
374         int nb_interfaces = len;
375 
376         if (DEBUG) debugMessage("number of interfaces = " + nb_interfaces);
377 
378         // write interfaces count
379         dos.writeShort((short) nb_interfaces);
380         for (int i = 0; i < len; i++) {
381            Klass k = interfaces.getAt(i);
382            Short index = classToIndex.get(k.getName().asString());
383            dos.writeShort(index.shortValue());
384            if (DEBUG) debugMessage("\t" + index);
385         }
386     }
387 
388     protected void writeFields() throws IOException {
389         final int javaFieldsCount = klass.getJavaFieldsCount();
390 
391         // write number of fields
392         dos.writeShort((short) javaFieldsCount);
393 
394         if (DEBUG) debugMessage("number of fields = " + javaFieldsCount);
395 
396         for (int index = 0; index < javaFieldsCount; index++) {
397             short accessFlags    = klass.getFieldAccessFlags(index);
398             dos.writeShort(accessFlags & (short) JVM_RECOGNIZED_FIELD_MODIFIERS);
399 
400             int nameIndex = klass.getFieldNameIndex(index);
401             dos.writeShort(nameIndex);
402 
403             int signatureIndex = klass.getFieldSignatureIndex(index);
404             dos.writeShort(signatureIndex);
405             if (DEBUG) debugMessage("\tfield name = " + nameIndex + ", signature = " + signatureIndex);
406 
407             short fieldAttributeCount = 0;
408             boolean hasSyn = hasSyntheticAttribute(accessFlags);
409             if (hasSyn)
410                 fieldAttributeCount++;
411 
412             int initvalIndex = klass.getFieldInitialValueIndex(index);
413             if (initvalIndex != 0)
414                 fieldAttributeCount++;
415 
416             int genSigIndex = klass.getFieldGenericSignatureIndex(index);
417             if (genSigIndex != 0)
418                 fieldAttributeCount++;
419 
420             U1Array fieldAnnotations = klass.getFieldAnnotations(index);
421             if (fieldAnnotations != null) {
422                 fieldAttributeCount++;
423             }
424 
425             U1Array fieldTypeAnnotations = klass.getFieldTypeAnnotations(index);
426             if (fieldTypeAnnotations != null) {
427                 fieldAttributeCount++;
428             }
429 
430             dos.writeShort(fieldAttributeCount);
431 
432             // write synthetic, if applicable
433             if (hasSyn)
434                 writeSynthetic();
435 
436             if (initvalIndex != 0) {
437                 writeIndex(_constantValueIndex);
438                 dos.writeInt(2);
439                 dos.writeShort(initvalIndex);
440                 if (DEBUG) debugMessage("\tfield init value = " + initvalIndex);
441             }
442 
443             if (genSigIndex != 0) {
444                 writeIndex(_signatureIndex);
445                 dos.writeInt(2);
446                 dos.writeShort(genSigIndex);
447                 if (DEBUG) debugMessage("\tfield generic signature index " + genSigIndex);
448             }
449 
450             if (fieldAnnotations != null) {
451                 writeAnnotationAttribute("RuntimeVisibleAnnotations", fieldAnnotations);
452             }
453 
454             if (fieldTypeAnnotations != null) {
455                 writeAnnotationAttribute("RuntimeVisibleTypeAnnotations", fieldTypeAnnotations);
456             }
457         }
458     }
459 
460     protected boolean isSynthetic(short accessFlags) {
461         return (accessFlags & (short) JVM_ACC_SYNTHETIC) != 0;
462     }
463 
464     protected boolean hasSyntheticAttribute(short accessFlags) {
465         // Check if flags have the attribute and if the constant pool contains an entry for it.
466         return isSynthetic(accessFlags) && _syntheticIndex != 0;
467     }
468 
469     protected void writeSynthetic() throws IOException {
470         writeIndex(_syntheticIndex);
471         dos.writeInt(0);
472     }
473 
474     protected void writeMethods() throws IOException {
475         MethodArray methods = klass.getMethods();
476         ArrayList<Method> valid_methods = new ArrayList<Method>();
477         for (int i = 0; i < methods.length(); i++) {
478             Method m = methods.at(i);
479             long accessFlags = m.getAccessFlags() & JVM_RECOGNIZED_METHOD_MODIFIERS;
480             // skip overpass methods
481             if (accessFlags == (JVM_ACC_PUBLIC | JVM_ACC_SYNTHETIC | JVM_ACC_BRIDGE)) {
482                 continue;
483             }
484             valid_methods.add(m);
485         }
486         final int len = valid_methods.size();
487         // write number of methods
488         dos.writeShort((short) len);
489         if (DEBUG) debugMessage("number of methods = " + len);
490         for (int m = 0; m < len; m++) {
491             writeMethod(valid_methods.get(m));
492         }
493     }
494 
495     protected void writeMethod(Method m) throws IOException {
496         long accessFlags = m.getAccessFlags();
497         dos.writeShort((short) (accessFlags & JVM_RECOGNIZED_METHOD_MODIFIERS));
498         dos.writeShort((short) m.getNameIndex());
499         dos.writeShort((short) m.getSignatureIndex());
500         if (DEBUG) debugMessage("\tmethod name = " + m.getNameIndex() + ", signature = "
501                         + m.getSignatureIndex());
502 
503         final boolean isNative = ((accessFlags & JVM_ACC_NATIVE) != 0);
504         final boolean isAbstract = ((accessFlags & JVM_ACC_ABSTRACT) != 0);
505 
506         short methodAttributeCount = 0;
507 
508         final boolean hasSyn = hasSyntheticAttribute((short)accessFlags);
509         if (hasSyn)
510             methodAttributeCount++;
511 
512         final boolean hasCheckedExceptions = m.hasCheckedExceptions();
513         if (hasCheckedExceptions)
514             methodAttributeCount++;
515 
516         final boolean isCodeAvailable = (!isNative) && (!isAbstract);
517         if (isCodeAvailable)
518             methodAttributeCount++;
519 
520         final boolean isGeneric = (m.getGenericSignature() != null);
521         if (isGeneric)
522             methodAttributeCount++;
523 
524         final U1Array annotations = m.getAnnotations();
525         if (annotations != null) {
526             methodAttributeCount++;
527         }
528 
529         final U1Array parameterAnnotations = m.getParameterAnnotations();
530         if (parameterAnnotations != null) {
531             methodAttributeCount++;
532         }
533 
534         final U1Array typeAnnotations = m.getTypeAnnotations();
535         if (typeAnnotations != null) {
536             methodAttributeCount++;
537         }
538 
539         final U1Array annotationDefault = m.getAnnotationDefault();
540         if (annotationDefault != null) {
541             methodAttributeCount++;
542         }
543 
544         dos.writeShort(methodAttributeCount);
545         if (DEBUG) debugMessage("\tmethod attribute count = " + methodAttributeCount);
546 
547         if (hasSyn) {
548             if (DEBUG) debugMessage("\tmethod is synthetic");
549             writeSynthetic();
550         }
551 
552         if (isCodeAvailable) {
553             byte[] code = m.getByteCode();
554             short codeAttrCount = 0;
555             int codeSize  = 2           /* max_stack   */ +
556                             2           /* max_locals  */ +
557                             4           /* code_length */ +
558                             code.length /* code        */ +
559                             2           /* exp. table len.  */ +
560                             2           /* code attr. count */;
561 
562             boolean hasExceptionTable = m.hasExceptionTable();
563             ExceptionTableElement[] exceptionTable = null;
564             int exceptionTableLen = 0;
565             if (hasExceptionTable) {
566                 exceptionTable = m.getExceptionTable();
567                 exceptionTableLen = exceptionTable.length;
568                 if (DEBUG) debugMessage("\tmethod has exception table");
569                 codeSize += exceptionTableLen /* exception table is 4-tuple array */
570                                          * (2 /* start_pc     */ +
571                                             2 /* end_pc       */ +
572                                             2 /* handler_pc   */ +
573                                             2 /* catch_type   */);
574             }
575 
576             boolean hasStackMapTable = m.hasStackMapTable();
577             U1Array stackMapData = null;
578             int stackMapAttrLen = 0;
579 
580             if (hasStackMapTable) {
581                 if (DEBUG) debugMessage("\tmethod has stack map table");
582                 stackMapData = m.getStackMapData();
583                 if (DEBUG) debugMessage("\t\tstack map table length = " + stackMapData.length());
584 
585                 stackMapAttrLen = stackMapData.length();
586 
587                 codeSize += 2 /* stack map table attr index */ +
588                             4 /* stack map table attr length */ +
589                             stackMapAttrLen;
590 
591                 if (DEBUG) debugMessage("\t\tstack map table attr size = " +
592                                         stackMapAttrLen);
593 
594                 codeAttrCount++;
595             }
596 
597             boolean hasLineNumberTable = m.hasLineNumberTable();
598             LineNumberTableElement[] lineNumberTable = null;
599             int lineNumberAttrLen = 0;
600 
601             if (hasLineNumberTable) {
602                 if (DEBUG) debugMessage("\tmethod has line number table");
603                 lineNumberTable = m.getLineNumberTable();
604                 if (DEBUG) debugMessage("\t\tline table length = " + lineNumberTable.length);
605 
606                 lineNumberAttrLen = 2 /* line number table length         */ +
607                            lineNumberTable.length * (2 /* start_pc */ + 2 /* line_number */);
608 
609                 codeSize += 2 /* line number table attr index     */ +
610                             4 /* line number table attr length    */ +
611                             lineNumberAttrLen;
612 
613                 if (DEBUG) debugMessage("\t\tline number table attr size = " +
614                                               lineNumberAttrLen);
615 
616                 codeAttrCount++;
617             }
618 
619             boolean hasLocalVariableTable = m.hasLocalVariableTable();
620             LocalVariableTableElement[] localVariableTable = null;
621             int localVarAttrLen = 0;
622 
623             if (hasLocalVariableTable) {
624                 if (DEBUG) debugMessage("\tmethod has local variable table");
625                 localVariableTable = m.getLocalVariableTable();
626                 if (DEBUG) debugMessage("\t\tlocal variable table length = "
627                               + localVariableTable.length);
628                 localVarAttrLen =
629                                2 /* local variable table length      */ +
630                                localVariableTable.length * ( 2 /* start_pc          */ +
631                                                           2 /* length            */ +
632                                                           2 /* name_index        */ +
633                                                           2 /* signature_index   */ +
634                                                           2 /* variable index    */ );
635 
636                 if (DEBUG) debugMessage("\t\tlocal variable attr size = " +
637                                               localVarAttrLen);
638 
639                 codeSize += 2 /* local variable table attr index  */ +
640                             4 /* local variable table attr length */ +
641                             localVarAttrLen;
642 
643                 codeAttrCount++;
644             }
645 
646             // fix ConstantPoolCache indices to ConstantPool indices.
647             rewriteByteCode(m, code);
648 
649             // start writing Code
650 
651             writeIndex(_codeIndex);
652 
653             dos.writeInt(codeSize);
654             if (DEBUG) debugMessage("\tcode attribute length = " + codeSize);
655 
656             dos.writeShort((short) m.getMaxStack());
657             if (DEBUG) debugMessage("\tmax stack = " + m.getMaxStack());
658 
659             dos.writeShort((short) m.getMaxLocals());
660             if (DEBUG) debugMessage("\tmax locals = " + m.getMaxLocals());
661 
662             dos.writeInt(code.length);
663             if (DEBUG) debugMessage("\tcode size = " + code.length);
664 
665             dos.write(code);
666 
667             // write exception table size
668             dos.writeShort((short) exceptionTableLen);
669             if (DEBUG) debugMessage("\texception table length = " + exceptionTableLen);
670 
671             if (exceptionTableLen != 0) {
672                 for (int e = 0; e < exceptionTableLen; e++) {
673                      dos.writeShort((short) exceptionTable[e].getStartPC());
674                      dos.writeShort((short) exceptionTable[e].getEndPC());
675                      dos.writeShort((short) exceptionTable[e].getHandlerPC());
676                      dos.writeShort((short) exceptionTable[e].getCatchTypeIndex());
677                 }
678             }
679 
680             dos.writeShort(codeAttrCount);
681             if (DEBUG) debugMessage("\tcode attribute count = " + codeAttrCount);
682 
683             // write StackMapTable, if available
684             if (hasStackMapTable) {
685                 writeIndex(_stackMapTableIndex);
686                 dos.writeInt(stackMapAttrLen);
687                 // We write bytes directly as stackMapData is
688                 // raw data (#entries + entries)
689                 for (int i = 0; i < stackMapData.length(); i++) {
690                     dos.writeByte(stackMapData.at(i));
691                 }
692             }
693 
694             // write LineNumberTable, if available.
695             if (hasLineNumberTable) {
696                 writeIndex(_lineNumberTableIndex);
697                 dos.writeInt(lineNumberAttrLen);
698                 dos.writeShort((short) lineNumberTable.length);
699                 for (int l = 0; l < lineNumberTable.length; l++) {
700                      dos.writeShort((short) lineNumberTable[l].getStartBCI());
701                      dos.writeShort((short) lineNumberTable[l].getLineNumber());
702                 }
703             }
704 
705             // write LocalVariableTable, if available.
706             if (hasLocalVariableTable) {
707                 writeIndex(_localVariableTableIndex);
708                 dos.writeInt(localVarAttrLen);
709                 dos.writeShort((short) localVariableTable.length);
710                 for (int l = 0; l < localVariableTable.length; l++) {
711                      dos.writeShort((short) localVariableTable[l].getStartBCI());
712                      dos.writeShort((short) localVariableTable[l].getLength());
713                      dos.writeShort((short) localVariableTable[l].getNameCPIndex());
714                      dos.writeShort((short) localVariableTable[l].getDescriptorCPIndex());
715                      dos.writeShort((short) localVariableTable[l].getSlot());
716                 }
717             }
718         }
719 
720         if (hasCheckedExceptions) {
721             CheckedExceptionElement[] exceptions = m.getCheckedExceptions();
722             writeIndex(_exceptionsIndex);
723 
724             int attrSize = 2 /* number_of_exceptions */ +
725                            exceptions.length * 2 /* exception_index */;
726             dos.writeInt(attrSize);
727             dos.writeShort(exceptions.length);
728             if (DEBUG) debugMessage("\tmethod has " + exceptions.length
729                                         +  " checked exception(s)");
730             for (int e = 0; e < exceptions.length; e++) {
731                  short cpIndex = (short) exceptions[e].getClassCPIndex();
732                  dos.writeShort(cpIndex);
733             }
734         }
735 
736         if (isGeneric) {
737            writeGenericSignature(m.getGenericSignature().asString());
738         }
739 
740         if (annotationDefault != null) {
741            writeAnnotationAttribute("AnnotationDefault", annotationDefault);
742         }
743 
744         if (annotations != null) {
745            writeAnnotationAttribute("RuntimeVisibleAnnotations", annotations);
746         }
747 
748         if (parameterAnnotations != null) {
749            writeAnnotationAttribute("RuntimeVisibleParameterAnnotations", parameterAnnotations);
750         }
751 
752         if (typeAnnotations != null) {
753            writeAnnotationAttribute("RuntimeVisibleTypeAnnotations", typeAnnotations);
754         }
755     }
756 
757     protected void rewriteByteCode(Method m, byte[] code) {
758         ByteCodeRewriter r = new ByteCodeRewriter(m, cpool, code);
759         r.rewrite();
760     }
761 
762     protected void writeGenericSignature(String signature) throws IOException {
763         writeIndex(_signatureIndex);
764         if (DEBUG) debugMessage("signature attribute = " + _signatureIndex);
765         dos.writeInt(2);
766         Short index = utf8ToIndex.get(signature);
767         dos.writeShort(index.shortValue());
768         if (DEBUG) debugMessage("generic signature = " + index);
769     }
770 
771     protected void writeClassAttributes() throws IOException {
772         final long flags = klass.getAccessFlags();
773         final boolean hasSyn = hasSyntheticAttribute((short) flags);
774 
775         // check for source file
776         short classAttributeCount = 0;
777 
778         if (hasSyn)
779             classAttributeCount++;
780 
781         Symbol sourceFileName = klass.getSourceFileName();
782         if (sourceFileName != null)
783             classAttributeCount++;
784 
785         Symbol genericSignature = klass.getGenericSignature();
786         if (genericSignature != null)
787             classAttributeCount++;
788 
789         U2Array innerClasses = klass.getInnerClasses();
790         final int numInnerClasses = innerClasses.length() / 4;
791         if (numInnerClasses != 0)
792             classAttributeCount++;
793 
794         short nestHost = klass.getNestHostIndex();
795         if (nestHost != 0) {
796             classAttributeCount++;
797         }
798 
799         U2Array nestMembers = klass.getNestMembers();
800         final int numNestMembers = nestMembers.length();
801         if (numNestMembers != 0) {
802             classAttributeCount++;
803         }
804 
805         int bsmCount = klass.getConstants().getBootstrapMethodsCount();
806         if (bsmCount != 0) {
807             classAttributeCount++;
808         }
809 
810         U1Array classAnnotations = klass.getClassAnnotations();
811         if (classAnnotations != null) {
812             classAttributeCount++;
813         }
814 
815         U1Array classTypeAnnotations = klass.getClassTypeAnnotations();
816         if (classTypeAnnotations != null) {
817             classAttributeCount++;
818         }
819 
820         dos.writeShort(classAttributeCount);
821         if (DEBUG) debugMessage("class attribute count = " + classAttributeCount);
822 
823         if (hasSyn)
824             writeSynthetic();
825 
826         // write SourceFile, if any
827         if (sourceFileName != null) {
828             writeIndex(_sourceFileIndex);
829             if (DEBUG) debugMessage("source file attribute = " + _sourceFileIndex);
830             dos.writeInt(2);
831             Short index = utf8ToIndex.get(sourceFileName.asString());
832             dos.writeShort(index.shortValue());
833             if (DEBUG) debugMessage("source file name = " + index);
834         }
835 
836         // write Signature, if any
837         if (genericSignature != null) {
838             writeGenericSignature(genericSignature.asString());
839         }
840 
841         // write inner classes, if any
842         if (numInnerClasses != 0) {
843             writeIndex(_innerClassesIndex);
844             final int innerAttrLen = 2 /* number_of_inner_classes */ +
845                                      numInnerClasses * (
846                                                  2 /* inner_class_info_index */ +
847                                                  2 /* outer_class_info_index */ +
848                                                  2 /* inner_class_name_index */ +
849                                                  2 /* inner_class_access_flags */);
850             dos.writeInt(innerAttrLen);
851 
852             dos.writeShort(numInnerClasses);
853             if (DEBUG) debugMessage("class has " + numInnerClasses + " inner class entries");
854 
855             for (int index = 0; index < numInnerClasses * 4; index++) {
856                 dos.writeShort(innerClasses.at(index));
857             }
858         }
859 
860         if (nestHost != 0) {
861             writeIndex(_nestHostIndex);
862             final int nestHostAttrLen = 2;
863             dos.writeInt(nestHostAttrLen);
864             dos.writeShort(nestHost);
865         }
866 
867         if (numNestMembers != 0) {
868            writeIndex(_nestMembersIndex);
869            final int nestMembersAttrLen = 2 + numNestMembers * 2;
870            dos.writeInt(nestMembersAttrLen);
871            dos.writeShort(numNestMembers);
872            for (int index = 0; index < numNestMembers; index++) {
873                dos.writeShort(nestMembers.at(index));
874            }
875         }
876 
877         // write bootstrap method attribute, if any
878         if (bsmCount != 0) {
879             ConstantPool cpool = klass.getConstants();
880             writeIndex(_bootstrapMethodsIndex);
881             if (DEBUG) debugMessage("bootstrap methods attribute = " + _bootstrapMethodsIndex);
882             int attrLen = 2; // num_bootstrap_methods
883             for (int index = 0; index < bsmCount; index++) {
884                 int bsmArgsCount = cpool.getBootstrapMethodArgsCount(index);
885                 attrLen += 2 // bootstrap_method_ref
886                            + 2 // num_bootstrap_arguments
887                            + bsmArgsCount * 2;
888             }
889             dos.writeInt(attrLen);
890             dos.writeShort(bsmCount);
891             for (int index = 0; index < bsmCount; index++) {
892                 short value[] = cpool.getBootstrapMethodAt(index);
893                 for (int i = 0; i < value.length; i++) {
894                     dos.writeShort(value[i]);
895                 }
896             }
897         }
898 
899         if (classAnnotations != null) {
900            writeAnnotationAttribute("RuntimeVisibleAnnotations", classAnnotations);
901         }
902 
903         if (classTypeAnnotations != null) {
904            writeAnnotationAttribute("RuntimeVisibleTypeAnnotations", classTypeAnnotations);
905         }
906     }
907 
908     protected void writeAnnotationAttribute(String annotationName, U1Array annotation) throws IOException {
909       int length = annotation.length();
910       Short annotationNameIndex = utf8ToIndex.get(annotationName);
911       if (Assert.ASSERTS_ENABLED) {
912         Assert.that(annotationNameIndex != null, "should not be null");
913       }
914       writeIndex(annotationNameIndex.shortValue());
915       dos.writeInt(length);
916       for (int index = 0; index < length; index++) {
917         dos.writeByte(annotation.at(index));
918       }
919     }
920 }