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