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 
375         if (DEBUG) debugMessage("number of interfaces = " + len);
376 
377         // write interfaces count
378         dos.writeShort((short) len);
379         for (int i = 0; i < len; i++) {
380            Klass k = interfaces.getAt(i);
381            Short index = classToIndex.get(k.getName().asString());
382            dos.writeShort(index.shortValue());
383            if (DEBUG) debugMessage("\t" + index);
384         }
385     }
386 
387     protected void writeFields() throws IOException {
388         final int javaFieldsCount = klass.getJavaFieldsCount();
389 
390         // write number of fields
391         dos.writeShort((short) javaFieldsCount);
392 
393         if (DEBUG) debugMessage("number of fields = " + javaFieldsCount);
394 
395         for (int index = 0; index < javaFieldsCount; index++) {
396             short accessFlags    = klass.getFieldAccessFlags(index);
397             dos.writeShort(accessFlags & (short) JVM_RECOGNIZED_FIELD_MODIFIERS);
398 
399             int nameIndex = klass.getFieldNameIndex(index);
400             dos.writeShort(nameIndex);
401 
402             int signatureIndex = klass.getFieldSignatureIndex(index);
403             dos.writeShort(signatureIndex);
404             if (DEBUG) debugMessage("\tfield name = " + nameIndex + ", signature = " + signatureIndex);
405 
406             short fieldAttributeCount = 0;
407             boolean hasSyn = hasSyntheticAttribute(accessFlags);
408             if (hasSyn)
409                 fieldAttributeCount++;
410 
411             int initvalIndex = klass.getFieldInitialValueIndex(index);
412             if (initvalIndex != 0)
413                 fieldAttributeCount++;
414 
415             int genSigIndex = klass.getFieldGenericSignatureIndex(index);
416             if (genSigIndex != 0)
417                 fieldAttributeCount++;
418 
419             U1Array fieldAnnotations = klass.getFieldAnnotations(index);
420             if (fieldAnnotations != null) {
421                 fieldAttributeCount++;
422             }
423 
424             U1Array fieldTypeAnnotations = klass.getFieldTypeAnnotations(index);
425             if (fieldTypeAnnotations != null) {
426                 fieldAttributeCount++;
427             }
428 
429             dos.writeShort(fieldAttributeCount);
430 
431             // write synthetic, if applicable
432             if (hasSyn)
433                 writeSynthetic();
434 
435             if (initvalIndex != 0) {
436                 writeIndex(_constantValueIndex);
437                 dos.writeInt(2);
438                 dos.writeShort(initvalIndex);
439                 if (DEBUG) debugMessage("\tfield init value = " + initvalIndex);
440             }
441 
442             if (genSigIndex != 0) {
443                 writeIndex(_signatureIndex);
444                 dos.writeInt(2);
445                 dos.writeShort(genSigIndex);
446                 if (DEBUG) debugMessage("\tfield generic signature index " + genSigIndex);
447             }
448 
449             if (fieldAnnotations != null) {
450                 writeAnnotationAttribute("RuntimeVisibleAnnotations", fieldAnnotations);
451             }
452 
453             if (fieldTypeAnnotations != null) {
454                 writeAnnotationAttribute("RuntimeVisibleTypeAnnotations", fieldTypeAnnotations);
455             }
456         }
457     }
458 
459     protected boolean isSynthetic(short accessFlags) {
460         return (accessFlags & (short) JVM_ACC_SYNTHETIC) != 0;
461     }
462 
463     protected boolean hasSyntheticAttribute(short accessFlags) {
464         // Check if flags have the attribute and if the constant pool contains an entry for it.
465         return isSynthetic(accessFlags) && _syntheticIndex != 0;
466     }
467 
468     protected void writeSynthetic() throws IOException {
469         writeIndex(_syntheticIndex);
470         dos.writeInt(0);
471     }
472 
473     protected void writeMethods() throws IOException {
474         MethodArray methods = klass.getMethods();
475         ArrayList<Method> valid_methods = new ArrayList<Method>();
476         for (int i = 0; i < methods.length(); i++) {
477             Method m = methods.at(i);
478             long accessFlags = m.getAccessFlags() & JVM_RECOGNIZED_METHOD_MODIFIERS;
479             // skip overpass methods
480             if (accessFlags == (JVM_ACC_PUBLIC | JVM_ACC_SYNTHETIC | JVM_ACC_BRIDGE)) {
481                 continue;
482             }
483             valid_methods.add(m);
484         }
485         final int len = valid_methods.size();
486         // write number of methods
487         dos.writeShort((short) len);
488         if (DEBUG) debugMessage("number of methods = " + len);
489         for (int m = 0; m < len; m++) {
490             writeMethod(valid_methods.get(m));
491         }
492     }
493 
494     protected void writeMethod(Method m) throws IOException {
495         long accessFlags = m.getAccessFlags();
496         dos.writeShort((short) (accessFlags & JVM_RECOGNIZED_METHOD_MODIFIERS));
497         dos.writeShort((short) m.getNameIndex());
498         dos.writeShort((short) m.getSignatureIndex());
499         if (DEBUG) debugMessage("\tmethod name = " + m.getNameIndex() + ", signature = "
500                         + m.getSignatureIndex());
501 
502         final boolean isNative = ((accessFlags & JVM_ACC_NATIVE) != 0);
503         final boolean isAbstract = ((accessFlags & JVM_ACC_ABSTRACT) != 0);
504 
505         short methodAttributeCount = 0;
506 
507         final boolean hasSyn = hasSyntheticAttribute((short)accessFlags);
508         if (hasSyn)
509             methodAttributeCount++;
510 
511         final boolean hasCheckedExceptions = m.hasCheckedExceptions();
512         if (hasCheckedExceptions)
513             methodAttributeCount++;
514 
515         final boolean isCodeAvailable = (!isNative) && (!isAbstract);
516         if (isCodeAvailable)
517             methodAttributeCount++;
518 
519         final boolean isGeneric = (m.getGenericSignature() != null);
520         if (isGeneric)
521             methodAttributeCount++;
522 
523         final U1Array annotations = m.getAnnotations();
524         if (annotations != null) {
525             methodAttributeCount++;
526         }
527 
528         final U1Array parameterAnnotations = m.getParameterAnnotations();
529         if (parameterAnnotations != null) {
530             methodAttributeCount++;
531         }
532 
533         final U1Array typeAnnotations = m.getTypeAnnotations();
534         if (typeAnnotations != null) {
535             methodAttributeCount++;
536         }
537 
538         final U1Array annotationDefault = m.getAnnotationDefault();
539         if (annotationDefault != null) {
540             methodAttributeCount++;
541         }
542 
543         dos.writeShort(methodAttributeCount);
544         if (DEBUG) debugMessage("\tmethod attribute count = " + methodAttributeCount);
545 
546         if (hasSyn) {
547             if (DEBUG) debugMessage("\tmethod is synthetic");
548             writeSynthetic();
549         }
550 
551         if (isCodeAvailable) {
552             byte[] code = m.getByteCode();
553             short codeAttrCount = 0;
554             int codeSize  = 2           /* max_stack   */ +
555                             2           /* max_locals  */ +
556                             4           /* code_length */ +
557                             code.length /* code        */ +
558                             2           /* exp. table len.  */ +
559                             2           /* code attr. count */;
560 
561             boolean hasExceptionTable = m.hasExceptionTable();
562             ExceptionTableElement[] exceptionTable = null;
563             int exceptionTableLen = 0;
564             if (hasExceptionTable) {
565                 exceptionTable = m.getExceptionTable();
566                 exceptionTableLen = exceptionTable.length;
567                 if (DEBUG) debugMessage("\tmethod has exception table");
568                 codeSize += exceptionTableLen /* exception table is 4-tuple array */
569                                          * (2 /* start_pc     */ +
570                                             2 /* end_pc       */ +
571                                             2 /* handler_pc   */ +
572                                             2 /* catch_type   */);
573             }
574 
575             boolean hasStackMapTable = m.hasStackMapTable();
576             U1Array stackMapData = null;
577             int stackMapAttrLen = 0;
578 
579             if (hasStackMapTable) {
580                 if (DEBUG) debugMessage("\tmethod has stack map table");
581                 stackMapData = m.getStackMapData();
582                 if (DEBUG) debugMessage("\t\tstack map table length = " + stackMapData.length());
583 
584                 stackMapAttrLen = stackMapData.length();
585 
586                 codeSize += 2 /* stack map table attr index */ +
587                             4 /* stack map table attr length */ +
588                             stackMapAttrLen;
589 
590                 if (DEBUG) debugMessage("\t\tstack map table attr size = " +
591                                         stackMapAttrLen);
592 
593                 codeAttrCount++;
594             }
595 
596             boolean hasLineNumberTable = m.hasLineNumberTable();
597             LineNumberTableElement[] lineNumberTable = null;
598             int lineNumberAttrLen = 0;
599 
600             if (hasLineNumberTable) {
601                 if (DEBUG) debugMessage("\tmethod has line number table");
602                 lineNumberTable = m.getLineNumberTable();
603                 if (DEBUG) debugMessage("\t\tline table length = " + lineNumberTable.length);
604 
605                 lineNumberAttrLen = 2 /* line number table length         */ +
606                            lineNumberTable.length * (2 /* start_pc */ + 2 /* line_number */);
607 
608                 codeSize += 2 /* line number table attr index     */ +
609                             4 /* line number table attr length    */ +
610                             lineNumberAttrLen;
611 
612                 if (DEBUG) debugMessage("\t\tline number table attr size = " +
613                                               lineNumberAttrLen);
614 
615                 codeAttrCount++;
616             }
617 
618             boolean hasLocalVariableTable = m.hasLocalVariableTable();
619             LocalVariableTableElement[] localVariableTable = null;
620             int localVarAttrLen = 0;
621 
622             if (hasLocalVariableTable) {
623                 if (DEBUG) debugMessage("\tmethod has local variable table");
624                 localVariableTable = m.getLocalVariableTable();
625                 if (DEBUG) debugMessage("\t\tlocal variable table length = "
626                               + localVariableTable.length);
627                 localVarAttrLen =
628                                2 /* local variable table length      */ +
629                                localVariableTable.length * ( 2 /* start_pc          */ +
630                                                           2 /* length            */ +
631                                                           2 /* name_index        */ +
632                                                           2 /* signature_index   */ +
633                                                           2 /* variable index    */ );
634 
635                 if (DEBUG) debugMessage("\t\tlocal variable attr size = " +
636                                               localVarAttrLen);
637 
638                 codeSize += 2 /* local variable table attr index  */ +
639                             4 /* local variable table attr length */ +
640                             localVarAttrLen;
641 
642                 codeAttrCount++;
643             }
644 
645             // fix ConstantPoolCache indices to ConstantPool indices.
646             rewriteByteCode(m, code);
647 
648             // start writing Code
649 
650             writeIndex(_codeIndex);
651 
652             dos.writeInt(codeSize);
653             if (DEBUG) debugMessage("\tcode attribute length = " + codeSize);
654 
655             dos.writeShort((short) m.getMaxStack());
656             if (DEBUG) debugMessage("\tmax stack = " + m.getMaxStack());
657 
658             dos.writeShort((short) m.getMaxLocals());
659             if (DEBUG) debugMessage("\tmax locals = " + m.getMaxLocals());
660 
661             dos.writeInt(code.length);
662             if (DEBUG) debugMessage("\tcode size = " + code.length);
663 
664             dos.write(code);
665 
666             // write exception table size
667             dos.writeShort((short) exceptionTableLen);
668             if (DEBUG) debugMessage("\texception table length = " + exceptionTableLen);
669 
670             if (exceptionTableLen != 0) {
671                 for (int e = 0; e < exceptionTableLen; e++) {
672                      dos.writeShort((short) exceptionTable[e].getStartPC());
673                      dos.writeShort((short) exceptionTable[e].getEndPC());
674                      dos.writeShort((short) exceptionTable[e].getHandlerPC());
675                      dos.writeShort((short) exceptionTable[e].getCatchTypeIndex());
676                 }
677             }
678 
679             dos.writeShort(codeAttrCount);
680             if (DEBUG) debugMessage("\tcode attribute count = " + codeAttrCount);
681 
682             // write StackMapTable, if available
683             if (hasStackMapTable) {
684                 writeIndex(_stackMapTableIndex);
685                 dos.writeInt(stackMapAttrLen);
686                 // We write bytes directly as stackMapData is
687                 // raw data (#entries + entries)
688                 for (int i = 0; i < stackMapData.length(); i++) {
689                     dos.writeByte(stackMapData.at(i));
690                 }
691             }
692 
693             // write LineNumberTable, if available.
694             if (hasLineNumberTable) {
695                 writeIndex(_lineNumberTableIndex);
696                 dos.writeInt(lineNumberAttrLen);
697                 dos.writeShort((short) lineNumberTable.length);
698                 for (int l = 0; l < lineNumberTable.length; l++) {
699                      dos.writeShort((short) lineNumberTable[l].getStartBCI());
700                      dos.writeShort((short) lineNumberTable[l].getLineNumber());
701                 }
702             }
703 
704             // write LocalVariableTable, if available.
705             if (hasLocalVariableTable) {
706                 writeIndex(_localVariableTableIndex);
707                 dos.writeInt(localVarAttrLen);
708                 dos.writeShort((short) localVariableTable.length);
709                 for (int l = 0; l < localVariableTable.length; l++) {
710                      dos.writeShort((short) localVariableTable[l].getStartBCI());
711                      dos.writeShort((short) localVariableTable[l].getLength());
712                      dos.writeShort((short) localVariableTable[l].getNameCPIndex());
713                      dos.writeShort((short) localVariableTable[l].getDescriptorCPIndex());
714                      dos.writeShort((short) localVariableTable[l].getSlot());
715                 }
716             }
717         }
718 
719         if (hasCheckedExceptions) {
720             CheckedExceptionElement[] exceptions = m.getCheckedExceptions();
721             writeIndex(_exceptionsIndex);
722 
723             int attrSize = 2 /* number_of_exceptions */ +
724                            exceptions.length * 2 /* exception_index */;
725             dos.writeInt(attrSize);
726             dos.writeShort(exceptions.length);
727             if (DEBUG) debugMessage("\tmethod has " + exceptions.length
728                                         +  " checked exception(s)");
729             for (int e = 0; e < exceptions.length; e++) {
730                  short cpIndex = (short) exceptions[e].getClassCPIndex();
731                  dos.writeShort(cpIndex);
732             }
733         }
734 
735         if (isGeneric) {
736            writeGenericSignature(m.getGenericSignature().asString());
737         }
738 
739         if (annotationDefault != null) {
740            writeAnnotationAttribute("AnnotationDefault", annotationDefault);
741         }
742 
743         if (annotations != null) {
744            writeAnnotationAttribute("RuntimeVisibleAnnotations", annotations);
745         }
746 
747         if (parameterAnnotations != null) {
748            writeAnnotationAttribute("RuntimeVisibleParameterAnnotations", parameterAnnotations);
749         }
750 
751         if (typeAnnotations != null) {
752            writeAnnotationAttribute("RuntimeVisibleTypeAnnotations", typeAnnotations);
753         }
754     }
755 
756     protected void rewriteByteCode(Method m, byte[] code) {
757         ByteCodeRewriter r = new ByteCodeRewriter(m, cpool, code);
758         r.rewrite();
759     }
760 
761     protected void writeGenericSignature(String signature) throws IOException {
762         writeIndex(_signatureIndex);
763         if (DEBUG) debugMessage("signature attribute = " + _signatureIndex);
764         dos.writeInt(2);
765         Short index = utf8ToIndex.get(signature);
766         dos.writeShort(index.shortValue());
767         if (DEBUG) debugMessage("generic signature = " + index);
768     }
769 
770     protected void writeClassAttributes() throws IOException {
771         final long flags = klass.getAccessFlags();
772         final boolean hasSyn = hasSyntheticAttribute((short) flags);
773 
774         // check for source file
775         short classAttributeCount = 0;
776 
777         if (hasSyn)
778             classAttributeCount++;
779 
780         Symbol sourceFileName = klass.getSourceFileName();
781         if (sourceFileName != null)
782             classAttributeCount++;
783 
784         Symbol genericSignature = klass.getGenericSignature();
785         if (genericSignature != null)
786             classAttributeCount++;
787 
788         U2Array innerClasses = klass.getInnerClasses();
789         final int numInnerClasses = innerClasses.length() / 4;
790         if (numInnerClasses != 0)
791             classAttributeCount++;
792 
793         short nestHost = klass.getNestHostIndex();
794         if (nestHost != 0) {
795             classAttributeCount++;
796         }
797 
798         U2Array nestMembers = klass.getNestMembers();
799         final int numNestMembers = nestMembers.length();
800         if (numNestMembers != 0) {
801             classAttributeCount++;
802         }
803 
804         int bsmCount = klass.getConstants().getBootstrapMethodsCount();
805         if (bsmCount != 0) {
806             classAttributeCount++;
807         }
808 
809         U1Array classAnnotations = klass.getClassAnnotations();
810         if (classAnnotations != null) {
811             classAttributeCount++;
812         }
813 
814         U1Array classTypeAnnotations = klass.getClassTypeAnnotations();
815         if (classTypeAnnotations != null) {
816             classAttributeCount++;
817         }
818 
819         dos.writeShort(classAttributeCount);
820         if (DEBUG) debugMessage("class attribute count = " + classAttributeCount);
821 
822         if (hasSyn)
823             writeSynthetic();
824 
825         // write SourceFile, if any
826         if (sourceFileName != null) {
827             writeIndex(_sourceFileIndex);
828             if (DEBUG) debugMessage("source file attribute = " + _sourceFileIndex);
829             dos.writeInt(2);
830             Short index = utf8ToIndex.get(sourceFileName.asString());
831             dos.writeShort(index.shortValue());
832             if (DEBUG) debugMessage("source file name = " + index);
833         }
834 
835         // write Signature, if any
836         if (genericSignature != null) {
837             writeGenericSignature(genericSignature.asString());
838         }
839 
840         // write inner classes, if any
841         if (numInnerClasses != 0) {
842             writeIndex(_innerClassesIndex);
843             final int innerAttrLen = 2 /* number_of_inner_classes */ +
844                                      numInnerClasses * (
845                                                  2 /* inner_class_info_index */ +
846                                                  2 /* outer_class_info_index */ +
847                                                  2 /* inner_class_name_index */ +
848                                                  2 /* inner_class_access_flags */);
849             dos.writeInt(innerAttrLen);
850 
851             dos.writeShort(numInnerClasses);
852             if (DEBUG) debugMessage("class has " + numInnerClasses + " inner class entries");
853 
854             for (int index = 0; index < numInnerClasses * 4; index++) {
855                 dos.writeShort(innerClasses.at(index));
856             }
857         }
858 
859         if (nestHost != 0) {
860             writeIndex(_nestHostIndex);
861             final int nestHostAttrLen = 2;
862             dos.writeInt(nestHostAttrLen);
863             dos.writeShort(nestHost);
864         }
865 
866         if (numNestMembers != 0) {
867            writeIndex(_nestMembersIndex);
868            final int nestMembersAttrLen = 2 + numNestMembers * 2;
869            dos.writeInt(nestMembersAttrLen);
870            dos.writeShort(numNestMembers);
871            for (int index = 0; index < numNestMembers; index++) {
872                dos.writeShort(nestMembers.at(index));
873            }
874         }
875 
876         // write bootstrap method attribute, if any
877         if (bsmCount != 0) {
878             ConstantPool cpool = klass.getConstants();
879             writeIndex(_bootstrapMethodsIndex);
880             if (DEBUG) debugMessage("bootstrap methods attribute = " + _bootstrapMethodsIndex);
881             int attrLen = 2; // num_bootstrap_methods
882             for (int index = 0; index < bsmCount; index++) {
883                 int bsmArgsCount = cpool.getBootstrapMethodArgsCount(index);
884                 attrLen += 2 // bootstrap_method_ref
885                            + 2 // num_bootstrap_arguments
886                            + bsmArgsCount * 2;
887             }
888             dos.writeInt(attrLen);
889             dos.writeShort(bsmCount);
890             for (int index = 0; index < bsmCount; index++) {
891                 short value[] = cpool.getBootstrapMethodAt(index);
892                 for (int i = 0; i < value.length; i++) {
893                     dos.writeShort(value[i]);
894                 }
895             }
896         }
897 
898         if (classAnnotations != null) {
899            writeAnnotationAttribute("RuntimeVisibleAnnotations", classAnnotations);
900         }
901 
902         if (classTypeAnnotations != null) {
903            writeAnnotationAttribute("RuntimeVisibleTypeAnnotations", classTypeAnnotations);
904         }
905     }
906 
907     protected void writeAnnotationAttribute(String annotationName, U1Array annotation) throws IOException {
908       int length = annotation.length();
909       Short annotationNameIndex = utf8ToIndex.get(annotationName);
910       if (Assert.ASSERTS_ENABLED) {
911         Assert.that(annotationNameIndex != null, "should not be null");
912       }
913       writeIndex(annotationNameIndex.shortValue());
914       dos.writeInt(length);
915       for (int index = 0; index < length; index++) {
916         dos.writeByte(annotation.at(index));
917       }
918     }
919 }