1 /*
2  * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 
27 package com.sun.tools.classfile;
28 
29 import java.io.ByteArrayOutputStream;
30 import java.io.DataOutputStream;
31 import java.io.File;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 import java.io.OutputStream;
35 
36 import static com.sun.tools.classfile.Annotation.*;
37 import static com.sun.tools.classfile.ConstantPool.*;
38 import static com.sun.tools.classfile.StackMapTable_attribute.*;
39 import static com.sun.tools.classfile.StackMapTable_attribute.verification_type_info.*;
40 
41 /**
42  * Write a ClassFile data structure to a file or stream.
43  *
44  *  <p><b>This is NOT part of any supported API.
45  *  If you write code that depends on this, you do so at your own risk.
46  *  This code and its internal interfaces are subject to change or
47  *  deletion without notice.</b>
48  */
49 public class ClassWriter {
ClassWriter()50     public ClassWriter() {
51         attributeWriter = new AttributeWriter();
52         constantPoolWriter = new ConstantPoolWriter();
53         out = new ClassOutputStream();
54     }
55 
56     /**
57      * Write a ClassFile data structure to a file.
58      * @param classFile the classfile object to be written
59      * @param f the file
60      * @throws IOException if an error occurs while writing the file
61      */
write(ClassFile classFile, File f)62     public void write(ClassFile classFile, File f) throws IOException {
63         try (FileOutputStream f_out = new FileOutputStream(f)) {
64             write(classFile, f_out);
65         }
66     }
67 
68     /**
69      * Write a ClassFile data structure to a stream.
70      * @param classFile the classfile object to be written
71      * @param s the stream
72      * @throws IOException if an error occurs while writing the file
73      */
write(ClassFile classFile, OutputStream s)74     public void write(ClassFile classFile, OutputStream s) throws IOException {
75         this.classFile = classFile;
76         out.reset();
77         write();
78         out.writeTo(s);
79     }
80 
write()81     protected void write() throws IOException {
82         writeHeader();
83         writeConstantPool();
84         writeAccessFlags(classFile.access_flags);
85         writeClassInfo();
86         writeFields();
87         writeMethods();
88         writeAttributes(classFile.attributes);
89     }
90 
writeHeader()91     protected void writeHeader() {
92         out.writeInt(classFile.magic);
93         out.writeShort(classFile.minor_version);
94         out.writeShort(classFile.major_version);
95     }
96 
writeAccessFlags(AccessFlags flags)97     protected void writeAccessFlags(AccessFlags flags) {
98         out.writeShort(flags.flags);
99     }
100 
writeAttributes(Attributes attributes)101     protected void writeAttributes(Attributes attributes) {
102         int size = attributes.size();
103         out.writeShort(size);
104         for (Attribute attr: attributes)
105             attributeWriter.write(attr, out);
106     }
107 
writeClassInfo()108     protected void writeClassInfo() {
109         out.writeShort(classFile.this_class);
110         out.writeShort(classFile.super_class);
111         int[] interfaces = classFile.interfaces;
112         out.writeShort(interfaces.length);
113         for (int i: interfaces)
114             out.writeShort(i);
115     }
116 
writeDescriptor(Descriptor d)117     protected void writeDescriptor(Descriptor d) {
118         out.writeShort(d.index);
119     }
120 
writeConstantPool()121     protected void writeConstantPool() {
122         ConstantPool pool = classFile.constant_pool;
123         int size = pool.size();
124         out.writeShort(size);
125         for (CPInfo cpInfo: pool.entries())
126             constantPoolWriter.write(cpInfo, out);
127     }
128 
writeFields()129     protected void writeFields() throws IOException {
130         Field[] fields = classFile.fields;
131         out.writeShort(fields.length);
132         for (Field f: fields)
133             writeField(f);
134     }
135 
writeField(Field f)136     protected void writeField(Field f) throws IOException {
137         writeAccessFlags(f.access_flags);
138         out.writeShort(f.name_index);
139         writeDescriptor(f.descriptor);
140         writeAttributes(f.attributes);
141     }
142 
writeMethods()143     protected void writeMethods() throws IOException {
144         Method[] methods = classFile.methods;
145         out.writeShort(methods.length);
146         for (Method m: methods) {
147             writeMethod(m);
148         }
149     }
150 
writeMethod(Method m)151     protected void writeMethod(Method m) throws IOException {
152         writeAccessFlags(m.access_flags);
153         out.writeShort(m.name_index);
154         writeDescriptor(m.descriptor);
155         writeAttributes(m.attributes);
156     }
157 
158     protected ClassFile classFile;
159     protected ClassOutputStream out;
160     protected AttributeWriter attributeWriter;
161     protected ConstantPoolWriter constantPoolWriter;
162 
163     /**
164      * Subtype of ByteArrayOutputStream with the convenience methods of
165      * a DataOutputStream. Since ByteArrayOutputStream does not throw
166      * IOException, there are no exceptions from the additional
167      * convenience methods either,
168      */
169     protected static class ClassOutputStream extends ByteArrayOutputStream {
ClassOutputStream()170         public ClassOutputStream() {
171             d = new DataOutputStream(this);
172         }
173 
writeByte(int value)174         public void writeByte(int value) {
175             try {
176                 d.writeByte(value);
177             } catch (IOException ignore) {
178             }
179         }
180 
writeShort(int value)181         public void writeShort(int value) {
182             try {
183                 d.writeShort(value);
184             } catch (IOException ignore) {
185             }
186         }
187 
writeInt(int value)188         public void writeInt(int value) {
189             try {
190                 d.writeInt(value);
191             } catch (IOException ignore) {
192             }
193         }
194 
writeLong(long value)195         public void writeLong(long value) {
196             try {
197                 d.writeLong(value);
198             } catch (IOException ignore) {
199             }
200         }
201 
writeFloat(float value)202         public void writeFloat(float value) {
203             try {
204                 d.writeFloat(value);
205             } catch (IOException ignore) {
206             }
207         }
208 
writeDouble(double value)209         public void writeDouble(double value) {
210             try {
211                 d.writeDouble(value);
212             } catch (IOException ignore) {
213             }
214         }
215 
writeUTF(String value)216         public void writeUTF(String value) {
217             try {
218                 d.writeUTF(value);
219             } catch (IOException ignore) {
220             }
221         }
222 
writeTo(ClassOutputStream s)223         public void writeTo(ClassOutputStream s) {
224             try {
225                 super.writeTo(s);
226             } catch (IOException ignore) {
227             }
228         }
229 
230         private final DataOutputStream d;
231     }
232 
233     /**
234      * Writer for the entries in the constant pool.
235      */
236     protected static class ConstantPoolWriter
237            implements ConstantPool.Visitor<Integer,ClassOutputStream> {
write(CPInfo info, ClassOutputStream out)238         protected int write(CPInfo info, ClassOutputStream out) {
239             out.writeByte(info.getTag());
240             return info.accept(this, out);
241         }
242 
243         @Override
visitClass(CONSTANT_Class_info info, ClassOutputStream out)244         public Integer visitClass(CONSTANT_Class_info info, ClassOutputStream out) {
245             out.writeShort(info.name_index);
246             return 1;
247         }
248 
249         @Override
visitDouble(CONSTANT_Double_info info, ClassOutputStream out)250         public Integer visitDouble(CONSTANT_Double_info info, ClassOutputStream out) {
251             out.writeDouble(info.value);
252             return 2;
253         }
254 
255         @Override
visitFieldref(CONSTANT_Fieldref_info info, ClassOutputStream out)256         public Integer visitFieldref(CONSTANT_Fieldref_info info, ClassOutputStream out) {
257             writeRef(info, out);
258             return 1;
259         }
260 
261         @Override
visitFloat(CONSTANT_Float_info info, ClassOutputStream out)262         public Integer visitFloat(CONSTANT_Float_info info, ClassOutputStream out) {
263             out.writeFloat(info.value);
264             return 1;
265         }
266 
267         @Override
visitInteger(CONSTANT_Integer_info info, ClassOutputStream out)268         public Integer visitInteger(CONSTANT_Integer_info info, ClassOutputStream out) {
269             out.writeInt(info.value);
270             return 1;
271         }
272 
273         @Override
visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, ClassOutputStream out)274         public Integer visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, ClassOutputStream out) {
275             writeRef(info, out);
276             return 1;
277         }
278 
279         @Override
visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, ClassOutputStream out)280         public Integer visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, ClassOutputStream out) {
281             out.writeShort(info.bootstrap_method_attr_index);
282             out.writeShort(info.name_and_type_index);
283             return 1;
284         }
285 
visitDynamicConstant(CONSTANT_Dynamic_info info, ClassOutputStream out)286         public Integer visitDynamicConstant(CONSTANT_Dynamic_info info, ClassOutputStream out) {
287             out.writeShort(info.bootstrap_method_attr_index);
288             out.writeShort(info.name_and_type_index);
289             return 1;
290         }
291 
292         @Override
visitLong(CONSTANT_Long_info info, ClassOutputStream out)293         public Integer visitLong(CONSTANT_Long_info info, ClassOutputStream out) {
294             out.writeLong(info.value);
295             return 2;
296         }
297 
298         @Override
visitMethodHandle(CONSTANT_MethodHandle_info info, ClassOutputStream out)299         public Integer visitMethodHandle(CONSTANT_MethodHandle_info info, ClassOutputStream out) {
300             out.writeByte(info.reference_kind.tag);
301             out.writeShort(info.reference_index);
302             return 1;
303         }
304 
305         @Override
visitMethodType(CONSTANT_MethodType_info info, ClassOutputStream out)306         public Integer visitMethodType(CONSTANT_MethodType_info info, ClassOutputStream out) {
307             out.writeShort(info.descriptor_index);
308             return 1;
309         }
310 
311         @Override
visitMethodref(CONSTANT_Methodref_info info, ClassOutputStream out)312         public Integer visitMethodref(CONSTANT_Methodref_info info, ClassOutputStream out) {
313             return writeRef(info, out);
314         }
315 
316         @Override
visitModule(CONSTANT_Module_info info, ClassOutputStream out)317         public Integer visitModule(CONSTANT_Module_info info, ClassOutputStream out) {
318             out.writeShort(info.name_index);
319             return 1;
320         }
321 
322         @Override
visitNameAndType(CONSTANT_NameAndType_info info, ClassOutputStream out)323         public Integer visitNameAndType(CONSTANT_NameAndType_info info, ClassOutputStream out) {
324             out.writeShort(info.name_index);
325             out.writeShort(info.type_index);
326             return 1;
327         }
328 
329         @Override
visitPackage(CONSTANT_Package_info info, ClassOutputStream out)330         public Integer visitPackage(CONSTANT_Package_info info, ClassOutputStream out) {
331             out.writeShort(info.name_index);
332             return 1;
333         }
334 
335         @Override
visitString(CONSTANT_String_info info, ClassOutputStream out)336         public Integer visitString(CONSTANT_String_info info, ClassOutputStream out) {
337             out.writeShort(info.string_index);
338             return 1;
339         }
340 
341         @Override
visitUtf8(CONSTANT_Utf8_info info, ClassOutputStream out)342         public Integer visitUtf8(CONSTANT_Utf8_info info, ClassOutputStream out) {
343             out.writeUTF(info.value);
344             return 1;
345         }
346 
writeRef(CPRefInfo info, ClassOutputStream out)347         protected Integer writeRef(CPRefInfo info, ClassOutputStream out) {
348             out.writeShort(info.class_index);
349             out.writeShort(info.name_and_type_index);
350             return 1;
351         }
352     }
353 
354     /**
355      * Writer for the different types of attribute.
356      */
357     protected static class AttributeWriter implements Attribute.Visitor<Void,ClassOutputStream> {
write(Attributes attributes, ClassOutputStream out)358         public void write(Attributes attributes, ClassOutputStream out) {
359             int size = attributes.size();
360             out.writeShort(size);
361             for (Attribute a: attributes)
362                 write(a, out);
363         }
364 
write(Attribute attr, ClassOutputStream out)365         public void write(Attribute attr, ClassOutputStream out) {
366             out.writeShort(attr.attribute_name_index);
367             ClassOutputStream nestedOut = new ClassOutputStream();
368             attr.accept(this, nestedOut);
369             out.writeInt(nestedOut.size());
370             nestedOut.writeTo(out);
371         }
372 
373         protected AnnotationWriter annotationWriter = new AnnotationWriter();
374 
375         @Override
visitDefault(DefaultAttribute attr, ClassOutputStream out)376         public Void visitDefault(DefaultAttribute attr, ClassOutputStream out) {
377             out.write(attr.info, 0, attr.info.length);
378             return null;
379         }
380 
381         @Override
visitAnnotationDefault(AnnotationDefault_attribute attr, ClassOutputStream out)382         public Void visitAnnotationDefault(AnnotationDefault_attribute attr, ClassOutputStream out) {
383             annotationWriter.write(attr.default_value, out);
384             return null;
385         }
386 
387         @Override
visitBootstrapMethods(BootstrapMethods_attribute attr, ClassOutputStream out)388         public Void visitBootstrapMethods(BootstrapMethods_attribute attr, ClassOutputStream out) {
389             out.writeShort(attr.bootstrap_method_specifiers.length);
390             for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsm : attr.bootstrap_method_specifiers) {
391                 out.writeShort(bsm.bootstrap_method_ref);
392                 int bsm_args_count = bsm.bootstrap_arguments.length;
393                 out.writeShort(bsm_args_count);
394                 for (int i : bsm.bootstrap_arguments) {
395                     out.writeShort(i);
396                 }
397             }
398             return null;
399         }
400 
401         @Override
visitCharacterRangeTable(CharacterRangeTable_attribute attr, ClassOutputStream out)402         public Void visitCharacterRangeTable(CharacterRangeTable_attribute attr, ClassOutputStream out) {
403             out.writeShort(attr.character_range_table.length);
404             for (CharacterRangeTable_attribute.Entry e: attr.character_range_table)
405                 writeCharacterRangeTableEntry(e, out);
406             return null;
407         }
408 
writeCharacterRangeTableEntry(CharacterRangeTable_attribute.Entry entry, ClassOutputStream out)409         protected void writeCharacterRangeTableEntry(CharacterRangeTable_attribute.Entry entry, ClassOutputStream out) {
410             out.writeShort(entry.start_pc);
411             out.writeShort(entry.end_pc);
412             out.writeInt(entry.character_range_start);
413             out.writeInt(entry.character_range_end);
414             out.writeShort(entry.flags);
415         }
416 
417         @Override
visitCode(Code_attribute attr, ClassOutputStream out)418         public Void visitCode(Code_attribute attr, ClassOutputStream out) {
419             out.writeShort(attr.max_stack);
420             out.writeShort(attr.max_locals);
421             out.writeInt(attr.code.length);
422             out.write(attr.code, 0, attr.code.length);
423             out.writeShort(attr.exception_table.length);
424             for (Code_attribute.Exception_data e: attr.exception_table)
425                 writeExceptionTableEntry(e, out);
426             new AttributeWriter().write(attr.attributes, out);
427             return null;
428         }
429 
writeExceptionTableEntry(Code_attribute.Exception_data exception_data, ClassOutputStream out)430         protected void writeExceptionTableEntry(Code_attribute.Exception_data exception_data, ClassOutputStream out) {
431             out.writeShort(exception_data.start_pc);
432             out.writeShort(exception_data.end_pc);
433             out.writeShort(exception_data.handler_pc);
434             out.writeShort(exception_data.catch_type);
435         }
436 
437         @Override
visitCompilationID(CompilationID_attribute attr, ClassOutputStream out)438         public Void visitCompilationID(CompilationID_attribute attr, ClassOutputStream out) {
439             out.writeShort(attr.compilationID_index);
440             return null;
441         }
442 
443         @Override
visitConstantValue(ConstantValue_attribute attr, ClassOutputStream out)444         public Void visitConstantValue(ConstantValue_attribute attr, ClassOutputStream out) {
445             out.writeShort(attr.constantvalue_index);
446             return null;
447         }
448 
449         @Override
visitDeprecated(Deprecated_attribute attr, ClassOutputStream out)450         public Void visitDeprecated(Deprecated_attribute attr, ClassOutputStream out) {
451             return null;
452         }
453 
454         @Override
visitEnclosingMethod(EnclosingMethod_attribute attr, ClassOutputStream out)455         public Void visitEnclosingMethod(EnclosingMethod_attribute attr, ClassOutputStream out) {
456             out.writeShort(attr.class_index);
457             out.writeShort(attr.method_index);
458             return null;
459         }
460 
461         @Override
visitExceptions(Exceptions_attribute attr, ClassOutputStream out)462         public Void visitExceptions(Exceptions_attribute attr, ClassOutputStream out) {
463             out.writeShort(attr.exception_index_table.length);
464             for (int i: attr.exception_index_table)
465                 out.writeShort(i);
466             return null;
467         }
468 
469         @Override
visitInnerClasses(InnerClasses_attribute attr, ClassOutputStream out)470         public Void visitInnerClasses(InnerClasses_attribute attr, ClassOutputStream out) {
471             out.writeShort(attr.classes.length);
472             for (InnerClasses_attribute.Info info: attr.classes)
473                 writeInnerClassesInfo(info, out);
474             return null;
475         }
476 
writeInnerClassesInfo(InnerClasses_attribute.Info info, ClassOutputStream out)477         protected void writeInnerClassesInfo(InnerClasses_attribute.Info info, ClassOutputStream out) {
478             out.writeShort(info.inner_class_info_index);
479             out.writeShort(info.outer_class_info_index);
480             out.writeShort(info.inner_name_index);
481             writeAccessFlags(info.inner_class_access_flags, out);
482         }
483 
484         @Override
visitLineNumberTable(LineNumberTable_attribute attr, ClassOutputStream out)485         public Void visitLineNumberTable(LineNumberTable_attribute attr, ClassOutputStream out) {
486             out.writeShort(attr.line_number_table.length);
487             for (LineNumberTable_attribute.Entry e: attr.line_number_table)
488                 writeLineNumberTableEntry(e, out);
489             return null;
490         }
491 
writeLineNumberTableEntry(LineNumberTable_attribute.Entry entry, ClassOutputStream out)492         protected void writeLineNumberTableEntry(LineNumberTable_attribute.Entry entry, ClassOutputStream out) {
493             out.writeShort(entry.start_pc);
494             out.writeShort(entry.line_number);
495         }
496 
497         @Override
visitLocalVariableTable(LocalVariableTable_attribute attr, ClassOutputStream out)498         public Void visitLocalVariableTable(LocalVariableTable_attribute attr, ClassOutputStream out) {
499             out.writeShort(attr.local_variable_table.length);
500             for (LocalVariableTable_attribute.Entry e: attr.local_variable_table)
501                 writeLocalVariableTableEntry(e, out);
502             return null;
503         }
504 
writeLocalVariableTableEntry(LocalVariableTable_attribute.Entry entry, ClassOutputStream out)505         protected void writeLocalVariableTableEntry(LocalVariableTable_attribute.Entry entry, ClassOutputStream out) {
506             out.writeShort(entry.start_pc);
507             out.writeShort(entry.length);
508             out.writeShort(entry.name_index);
509             out.writeShort(entry.descriptor_index);
510             out.writeShort(entry.index);
511         }
512 
513         @Override
visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, ClassOutputStream out)514         public Void visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, ClassOutputStream out) {
515             out.writeShort(attr.local_variable_table.length);
516             for (LocalVariableTypeTable_attribute.Entry e: attr.local_variable_table)
517                 writeLocalVariableTypeTableEntry(e, out);
518             return null;
519         }
520 
writeLocalVariableTypeTableEntry(LocalVariableTypeTable_attribute.Entry entry, ClassOutputStream out)521         protected void writeLocalVariableTypeTableEntry(LocalVariableTypeTable_attribute.Entry entry, ClassOutputStream out) {
522             out.writeShort(entry.start_pc);
523             out.writeShort(entry.length);
524             out.writeShort(entry.name_index);
525             out.writeShort(entry.signature_index);
526             out.writeShort(entry.index);
527         }
528 
529         @Override
visitNestHost(NestHost_attribute attr, ClassOutputStream out)530         public Void visitNestHost(NestHost_attribute attr, ClassOutputStream out) {
531             out.writeShort(attr.top_index);
532             return null;
533         }
534 
535         @Override
visitMethodParameters(MethodParameters_attribute attr, ClassOutputStream out)536         public Void visitMethodParameters(MethodParameters_attribute attr, ClassOutputStream out) {
537             out.writeByte(attr.method_parameter_table.length);
538             for (MethodParameters_attribute.Entry e : attr.method_parameter_table) {
539                 out.writeShort(e.name_index);
540                 out.writeShort(e.flags);
541             }
542             return null;
543         }
544 
545         @Override
visitModule(Module_attribute attr, ClassOutputStream out)546         public Void visitModule(Module_attribute attr, ClassOutputStream out) {
547             out.writeShort(attr.module_name);
548             out.writeShort(attr.module_flags);
549             out.writeShort(attr.module_version_index);
550 
551             out.writeShort(attr.requires.length);
552             for (Module_attribute.RequiresEntry e: attr.requires) {
553                 out.writeShort(e.requires_index);
554                 out.writeShort(e.requires_flags);
555                 out.writeShort(e.requires_version_index);
556             }
557 
558             out.writeShort(attr.exports.length);
559             for (Module_attribute.ExportsEntry e: attr.exports) {
560                 out.writeShort(e.exports_index);
561                 out.writeShort(e.exports_flags);
562                 out.writeShort(e.exports_to_index.length);
563                 for (int index: e.exports_to_index)
564                     out.writeShort(index);
565             }
566 
567             out.writeShort(attr.opens.length);
568             for (Module_attribute.OpensEntry e: attr.opens) {
569                 out.writeShort(e.opens_index);
570                 out.writeShort(e.opens_flags);
571                 out.writeShort(e.opens_to_index.length);
572                 for (int index: e.opens_to_index)
573                     out.writeShort(index);
574             }
575 
576             out.writeShort(attr.uses_index.length);
577             for (int index: attr.uses_index) {
578                 out.writeShort(index);
579             }
580 
581             out.writeShort(attr.provides.length);
582             for (Module_attribute.ProvidesEntry e: attr.provides) {
583                 out.writeShort(e.provides_index);
584                 out.writeShort(e.with_count);
585                 for (int with : e.with_index) {
586                     out.writeShort(with);
587                 }
588             }
589 
590             return null;
591         }
592 
593         @Override
visitModuleHashes(ModuleHashes_attribute attr, ClassOutputStream out)594         public Void visitModuleHashes(ModuleHashes_attribute attr, ClassOutputStream out) {
595             out.writeShort(attr.algorithm_index);
596             out.writeShort(attr.hashes_table.length);
597             for (ModuleHashes_attribute.Entry e: attr.hashes_table) {
598                 out.writeShort(e.module_name_index);
599                 out.writeShort(e.hash.length);
600                 for (byte b: e.hash) {
601                     out.writeByte(b);
602                 }
603             }
604             return null;
605         }
606 
607         @Override
visitModuleMainClass(ModuleMainClass_attribute attr, ClassOutputStream out)608         public Void visitModuleMainClass(ModuleMainClass_attribute attr, ClassOutputStream out) {
609             out.writeShort(attr.main_class_index);
610             return null;
611         }
612 
613         @Override
visitModulePackages(ModulePackages_attribute attr, ClassOutputStream out)614         public Void visitModulePackages(ModulePackages_attribute attr, ClassOutputStream out) {
615             out.writeShort(attr.packages_count);
616             for (int i: attr.packages_index)
617                 out.writeShort(i);
618             return null;
619         }
620 
621         @Override
visitModuleResolution(ModuleResolution_attribute attr, ClassOutputStream out)622         public Void visitModuleResolution(ModuleResolution_attribute attr, ClassOutputStream out) {
623             out.writeShort(attr.resolution_flags);
624             return null;
625         }
626 
627         @Override
visitModuleTarget(ModuleTarget_attribute attr, ClassOutputStream out)628         public Void visitModuleTarget(ModuleTarget_attribute attr, ClassOutputStream out) {
629             out.writeShort(attr.target_platform_index);
630             return null;
631         }
632 
633         @Override
visitNestMembers(NestMembers_attribute attr, ClassOutputStream out)634         public Void visitNestMembers(NestMembers_attribute attr, ClassOutputStream out) {
635             out.writeShort(attr.members_indexes.length);
636             for (int i : attr.members_indexes) {
637                 out.writeShort(i);
638             }
639             return null;
640         }
641 
642         @Override
visitRecord(Record_attribute attr, ClassOutputStream out)643         public Void visitRecord(Record_attribute attr, ClassOutputStream out) {
644             out.writeShort(attr.component_count);
645             for (Record_attribute.ComponentInfo info: attr.component_info_arr) {
646                 out.writeShort(info.name_index);
647                 out.writeShort(info.descriptor.index);
648                 int size = info.attributes.size();
649                 out.writeShort(size);
650                 for (Attribute componentAttr: info.attributes)
651                     write(componentAttr, out);
652             }
653             return null;
654         }
655 
656         @Override
visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, ClassOutputStream out)657         public Void visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, ClassOutputStream out) {
658             annotationWriter.write(attr.annotations, out);
659             return null;
660         }
661 
662         @Override
visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, ClassOutputStream out)663         public Void visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, ClassOutputStream out) {
664             out.writeByte(attr.parameter_annotations.length);
665             for (Annotation[] annos: attr.parameter_annotations)
666                 annotationWriter.write(annos, out);
667             return null;
668         }
669 
670         @Override
visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, ClassOutputStream out)671         public Void visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, ClassOutputStream out) {
672             annotationWriter.write(attr.annotations, out);
673             return null;
674         }
675 
676         @Override
visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, ClassOutputStream out)677         public Void visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, ClassOutputStream out) {
678             annotationWriter.write(attr.annotations, out);
679             return null;
680         }
681 
682         @Override
visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, ClassOutputStream out)683         public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, ClassOutputStream out) {
684             out.writeByte(attr.parameter_annotations.length);
685             for (Annotation[] annos: attr.parameter_annotations)
686                 annotationWriter.write(annos, out);
687             return null;
688         }
689 
690         @Override
visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, ClassOutputStream out)691         public Void visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, ClassOutputStream out) {
692             annotationWriter.write(attr.annotations, out);
693             return null;
694         }
695 
696         @Override
visitPermittedSubclasses(PermittedSubclasses_attribute attr, ClassOutputStream out)697         public Void visitPermittedSubclasses(PermittedSubclasses_attribute attr, ClassOutputStream out) {
698             int n = attr.subtypes.length;
699             out.writeShort(n);
700             for (int i = 0 ; i < n ; i++) {
701                 out.writeShort(attr.subtypes[i]);
702             }
703             return null;
704         }
705 
706         @Override
visitSignature(Signature_attribute attr, ClassOutputStream out)707         public Void visitSignature(Signature_attribute attr, ClassOutputStream out) {
708             out.writeShort(attr.signature_index);
709             return null;
710         }
711 
712         @Override
visitSourceDebugExtension(SourceDebugExtension_attribute attr, ClassOutputStream out)713         public Void visitSourceDebugExtension(SourceDebugExtension_attribute attr, ClassOutputStream out) {
714             out.write(attr.debug_extension, 0, attr.debug_extension.length);
715             return null;
716         }
717 
718         @Override
visitSourceFile(SourceFile_attribute attr, ClassOutputStream out)719         public Void visitSourceFile(SourceFile_attribute attr, ClassOutputStream out) {
720             out.writeShort(attr.sourcefile_index);
721             return null;
722         }
723 
724         @Override
visitSourceID(SourceID_attribute attr, ClassOutputStream out)725         public Void visitSourceID(SourceID_attribute attr, ClassOutputStream out) {
726             out.writeShort(attr.sourceID_index);
727             return null;
728         }
729 
730         @Override
visitStackMap(StackMap_attribute attr, ClassOutputStream out)731         public Void visitStackMap(StackMap_attribute attr, ClassOutputStream out) {
732             if (stackMapWriter == null)
733                 stackMapWriter = new StackMapTableWriter();
734 
735             out.writeShort(attr.entries.length);
736             for (stack_map_frame f: attr.entries)
737                 stackMapWriter.write(f, out);
738             return null;
739         }
740 
741         @Override
visitStackMapTable(StackMapTable_attribute attr, ClassOutputStream out)742         public Void visitStackMapTable(StackMapTable_attribute attr, ClassOutputStream out) {
743             if (stackMapWriter == null)
744                 stackMapWriter = new StackMapTableWriter();
745 
746             out.writeShort(attr.entries.length);
747             for (stack_map_frame f: attr.entries)
748                 stackMapWriter.write(f, out);
749             return null;
750         }
751 
752         @Override
visitSynthetic(Synthetic_attribute attr, ClassOutputStream out)753         public Void visitSynthetic(Synthetic_attribute attr, ClassOutputStream out) {
754             return null;
755         }
756 
writeAccessFlags(AccessFlags flags, ClassOutputStream out)757         protected void writeAccessFlags(AccessFlags flags, ClassOutputStream out) {
758             out.writeShort(flags.flags);
759         }
760 
761         protected StackMapTableWriter stackMapWriter;
762     }
763 
764     /**
765      * Writer for the frames of StackMap and StackMapTable attributes.
766      */
767     protected static class StackMapTableWriter
768             implements stack_map_frame.Visitor<Void,ClassOutputStream> {
769 
write(stack_map_frame frame, ClassOutputStream out)770         public void write(stack_map_frame frame, ClassOutputStream out) {
771             out.write(frame.frame_type);
772             frame.accept(this, out);
773         }
774 
775         @Override
visit_same_frame(same_frame frame, ClassOutputStream p)776         public Void visit_same_frame(same_frame frame, ClassOutputStream p) {
777             return null;
778         }
779 
780         @Override
visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame frame, ClassOutputStream out)781         public Void visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame frame, ClassOutputStream out) {
782             writeVerificationTypeInfo(frame.stack[0], out);
783             return null;
784         }
785 
786         @Override
visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended frame, ClassOutputStream out)787         public Void visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended frame, ClassOutputStream out) {
788             out.writeShort(frame.offset_delta);
789             writeVerificationTypeInfo(frame.stack[0], out);
790             return null;
791         }
792 
793         @Override
visit_chop_frame(chop_frame frame, ClassOutputStream out)794         public Void visit_chop_frame(chop_frame frame, ClassOutputStream out) {
795             out.writeShort(frame.offset_delta);
796             return null;
797         }
798 
799         @Override
visit_same_frame_extended(same_frame_extended frame, ClassOutputStream out)800         public Void visit_same_frame_extended(same_frame_extended frame, ClassOutputStream out) {
801             out.writeShort(frame.offset_delta);
802             return null;
803         }
804 
805         @Override
visit_append_frame(append_frame frame, ClassOutputStream out)806         public Void visit_append_frame(append_frame frame, ClassOutputStream out) {
807             out.writeShort(frame.offset_delta);
808             for (verification_type_info l: frame.locals)
809                 writeVerificationTypeInfo(l, out);
810             return null;
811         }
812 
813         @Override
visit_full_frame(full_frame frame, ClassOutputStream out)814         public Void visit_full_frame(full_frame frame, ClassOutputStream out) {
815             out.writeShort(frame.offset_delta);
816             out.writeShort(frame.locals.length);
817             for (verification_type_info l: frame.locals)
818                 writeVerificationTypeInfo(l, out);
819             out.writeShort(frame.stack.length);
820             for (verification_type_info s: frame.stack)
821                 writeVerificationTypeInfo(s, out);
822             return null;
823         }
824 
writeVerificationTypeInfo(verification_type_info info, ClassOutputStream out)825         protected void writeVerificationTypeInfo(verification_type_info info,
826                 ClassOutputStream out)  {
827             out.write(info.tag);
828             switch (info.tag) {
829             case ITEM_Top:
830             case ITEM_Integer:
831             case ITEM_Float:
832             case ITEM_Long:
833             case ITEM_Double:
834             case ITEM_Null:
835             case ITEM_UninitializedThis:
836                 break;
837 
838             case ITEM_Object:
839                 Object_variable_info o = (Object_variable_info) info;
840                 out.writeShort(o.cpool_index);
841                 break;
842 
843             case ITEM_Uninitialized:
844                 Uninitialized_variable_info u = (Uninitialized_variable_info) info;
845                 out.writeShort(u.offset);
846                 break;
847 
848             default:
849                 throw new Error();
850             }
851         }
852     }
853 
854     /**
855      * Writer for annotations and the values they contain.
856      */
857     protected static class AnnotationWriter
858             implements Annotation.element_value.Visitor<Void,ClassOutputStream> {
write(Annotation[] annos, ClassOutputStream out)859         public void write(Annotation[] annos, ClassOutputStream out) {
860             out.writeShort(annos.length);
861             for (Annotation anno: annos)
862                 write(anno, out);
863         }
864 
write(TypeAnnotation[] annos, ClassOutputStream out)865         public void write(TypeAnnotation[] annos, ClassOutputStream out) {
866             out.writeShort(annos.length);
867             for (TypeAnnotation anno: annos)
868                 write(anno, out);
869         }
870 
write(Annotation anno, ClassOutputStream out)871         public void write(Annotation anno, ClassOutputStream out) {
872             out.writeShort(anno.type_index);
873             out.writeShort(anno.element_value_pairs.length);
874             for (element_value_pair p: anno.element_value_pairs)
875                 write(p, out);
876         }
877 
write(TypeAnnotation anno, ClassOutputStream out)878         public void write(TypeAnnotation anno, ClassOutputStream out) {
879             write(anno.position, out);
880             write(anno.annotation, out);
881         }
882 
write(element_value_pair pair, ClassOutputStream out)883         public void write(element_value_pair pair, ClassOutputStream out) {
884             out.writeShort(pair.element_name_index);
885             write(pair.value, out);
886         }
887 
write(element_value ev, ClassOutputStream out)888         public void write(element_value ev, ClassOutputStream out) {
889             out.writeByte(ev.tag);
890             ev.accept(this, out);
891         }
892 
893         @Override
visitPrimitive(Primitive_element_value ev, ClassOutputStream out)894         public Void visitPrimitive(Primitive_element_value ev, ClassOutputStream out) {
895             out.writeShort(ev.const_value_index);
896             return null;
897         }
898 
899         @Override
visitEnum(Enum_element_value ev, ClassOutputStream out)900         public Void visitEnum(Enum_element_value ev, ClassOutputStream out) {
901             out.writeShort(ev.type_name_index);
902             out.writeShort(ev.const_name_index);
903             return null;
904         }
905 
906         @Override
visitClass(Class_element_value ev, ClassOutputStream out)907         public Void visitClass(Class_element_value ev, ClassOutputStream out) {
908             out.writeShort(ev.class_info_index);
909             return null;
910         }
911 
912         @Override
visitAnnotation(Annotation_element_value ev, ClassOutputStream out)913         public Void visitAnnotation(Annotation_element_value ev, ClassOutputStream out) {
914             write(ev.annotation_value, out);
915             return null;
916         }
917 
918         @Override
visitArray(Array_element_value ev, ClassOutputStream out)919         public Void visitArray(Array_element_value ev, ClassOutputStream out) {
920             out.writeShort(ev.num_values);
921             for (element_value v: ev.values)
922                 write(v, out);
923             return null;
924         }
925 
926         // TODO: Move this to TypeAnnotation to be closer with similar logic?
write(TypeAnnotation.Position p, ClassOutputStream out)927         private void write(TypeAnnotation.Position p, ClassOutputStream out) {
928             out.writeByte(p.type.targetTypeValue());
929             switch (p.type) {
930             // instanceof
931             case INSTANCEOF:
932             // new expression
933             case NEW:
934             // constructor/method reference receiver
935             case CONSTRUCTOR_REFERENCE:
936             case METHOD_REFERENCE:
937                 out.writeShort(p.offset);
938                 break;
939             // local variable
940             case LOCAL_VARIABLE:
941             // resource variable
942             case RESOURCE_VARIABLE:
943                 int table_length = p.lvarOffset.length;
944                 out.writeShort(table_length);
945                 for (int i = 0; i < table_length; ++i) {
946                     out.writeShort(1);  // for table length
947                     out.writeShort(p.lvarOffset[i]);
948                     out.writeShort(p.lvarLength[i]);
949                     out.writeShort(p.lvarIndex[i]);
950                 }
951                 break;
952             // exception parameter
953             case EXCEPTION_PARAMETER:
954                 out.writeShort(p.exception_index);
955                 break;
956             // method receiver
957             case METHOD_RECEIVER:
958                 // Do nothing
959                 break;
960             // type parameters
961             case CLASS_TYPE_PARAMETER:
962             case METHOD_TYPE_PARAMETER:
963                 out.writeByte(p.parameter_index);
964                 break;
965             // type parameters bounds
966             case CLASS_TYPE_PARAMETER_BOUND:
967             case METHOD_TYPE_PARAMETER_BOUND:
968                 out.writeByte(p.parameter_index);
969                 out.writeByte(p.bound_index);
970                 break;
971             // class extends or implements clause
972             case CLASS_EXTENDS:
973                 out.writeShort(p.type_index);
974                 break;
975             // throws
976             case THROWS:
977                 out.writeShort(p.type_index);
978                 break;
979             // method parameter
980             case METHOD_FORMAL_PARAMETER:
981                 out.writeByte(p.parameter_index);
982                 break;
983             // type cast
984             case CAST:
985             // method/constructor/reference type argument
986             case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
987             case METHOD_INVOCATION_TYPE_ARGUMENT:
988             case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
989             case METHOD_REFERENCE_TYPE_ARGUMENT:
990                 out.writeShort(p.offset);
991                 out.writeByte(p.type_index);
992                 break;
993             // We don't need to worry about these
994             case METHOD_RETURN:
995             case FIELD:
996                 break;
997             case UNKNOWN:
998                 throw new AssertionError("ClassWriter: UNKNOWN target type should never occur!");
999             default:
1000                 throw new AssertionError("ClassWriter: Unknown target type for position: " + p);
1001             }
1002 
1003             { // Append location data for generics/arrays.
1004                 // TODO: check for overrun?
1005                 out.writeByte((byte)p.location.size());
1006                 for (int i : TypeAnnotation.Position.getBinaryFromTypePath(p.location))
1007                     out.writeByte((byte)i);
1008             }
1009         }
1010     }
1011 }
1012