001    /*
002     * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    package com.sun.tools.classfile;
027    
028    import java.io.ByteArrayOutputStream;
029    import java.io.DataOutputStream;
030    import java.io.File;
031    import java.io.FileOutputStream;
032    import java.io.IOException;
033    import java.io.OutputStream;
034    
035    import static com.sun.tools.classfile.Annotation.*;
036    import static com.sun.tools.classfile.ConstantPool.*;
037    import static com.sun.tools.classfile.StackMapTable_attribute.*;
038    import static com.sun.tools.classfile.StackMapTable_attribute.verification_type_info.*;
039    
040    /**
041     * Write a ClassFile data structure to a file or stream.
042     *
043     *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
044     *  you write code that depends on this, you do so at your own risk.
045     *  This code and its internal interfaces are subject to change or
046     *  deletion without notice.</b>
047     */
048    public class ClassWriter {
049        public ClassWriter() {
050            attributeWriter = new AttributeWriter();
051            constantPoolWriter = new ConstantPoolWriter();
052            out = new ClassOutputStream();
053        }
054    
055        /**
056         * Write a ClassFile data structure to a file.
057         */
058        public void write(ClassFile classFile, File f) throws IOException {
059            FileOutputStream f_out = new FileOutputStream(f);
060            try {
061                write(classFile, f_out);
062            } finally {
063                f_out.close();
064            }
065        }
066    
067        /**
068         * Write a ClassFile data structure to a stream.
069         */
070        public void write(ClassFile classFile, OutputStream s) throws IOException {
071            this.classFile = classFile;
072            out.reset();
073            write();
074            out.writeTo(s);
075        }
076    
077        protected void write() throws IOException {
078            writeHeader();
079            writeConstantPool();
080            writeAccessFlags(classFile.access_flags);
081            writeClassInfo();
082            writeFields();
083            writeMethods();
084            writeAttributes(classFile.attributes);
085        }
086    
087        protected void writeHeader() {
088            out.writeInt(classFile.magic);
089            out.writeShort(classFile.minor_version);
090            out.writeShort(classFile.major_version);
091        }
092    
093        protected void writeAccessFlags(AccessFlags flags) {
094            out.writeShort(flags.flags);
095        }
096    
097        protected void writeAttributes(Attributes attributes) {
098            int size = attributes.size();
099            out.writeShort(size);
100            for (Attribute attr: attributes)
101                attributeWriter.write(attr, out);
102        }
103    
104        protected void writeClassInfo() {
105            out.writeShort(classFile.this_class);
106            out.writeShort(classFile.super_class);
107            int[] interfaces = classFile.interfaces;
108            out.writeShort(interfaces.length);
109            for (int i: interfaces)
110                out.writeShort(i);
111        }
112    
113        protected void writeDescriptor(Descriptor d) {
114            out.writeShort(d.index);
115        }
116    
117        protected void writeConstantPool() {
118            ConstantPool pool = classFile.constant_pool;
119            int size = pool.size();
120            out.writeShort(size);
121            try {
122                for (int i = 1; i < size; ) {
123                    i += constantPoolWriter.write(pool.get(i), out);
124                }
125            } catch (ConstantPoolException e) {
126                throw new Error(e); // ??
127            }
128        }
129    
130        protected void writeFields() throws IOException {
131            Field[] fields = classFile.fields;
132            out.writeShort(fields.length);
133            for (Field f: fields)
134                writeField(f);
135        }
136    
137        protected void writeField(Field f) throws IOException {
138            writeAccessFlags(f.access_flags);
139            out.writeShort(f.name_index);
140            writeDescriptor(f.descriptor);
141            writeAttributes(f.attributes);
142        }
143    
144        protected void writeMethods() throws IOException {
145            Method[] methods = classFile.methods;
146            out.writeShort(methods.length);
147            for (Method m: methods) {
148                writeMethod(m);
149            }
150        }
151    
152        protected void writeMethod(Method m) throws IOException {
153            writeAccessFlags(m.access_flags);
154            out.writeShort(m.name_index);
155            writeDescriptor(m.descriptor);
156            writeAttributes(m.attributes);
157        }
158    
159        protected ClassFile classFile;
160        protected ClassOutputStream out;
161        protected AttributeWriter attributeWriter;
162        protected ConstantPoolWriter constantPoolWriter;
163    
164        /**
165         * Subtype of ByteArrayOutputStream with the convenience methods of
166         * a DataOutputStream. Since ByteArrayOutputStream does not throw
167         * IOException, there are no exceptions from the additional
168         * convenience methods either,
169         */
170        protected static class ClassOutputStream extends ByteArrayOutputStream {
171            public ClassOutputStream() {
172                d = new DataOutputStream(this);
173            }
174    
175            public void writeByte(int value) {
176                try {
177                    d.writeByte(value);
178                } catch (IOException ignore) {
179                }
180            }
181    
182            public void writeShort(int value) {
183                try {
184                    d.writeShort(value);
185                } catch (IOException ignore) {
186                }
187            }
188    
189            public void writeInt(int value) {
190                try {
191                    d.writeInt(value);
192                } catch (IOException ignore) {
193                }
194            }
195    
196            public void writeLong(long value) {
197                try {
198                    d.writeLong(value);
199                } catch (IOException ignore) {
200                }
201            }
202    
203            public void writeFloat(float value) {
204                try {
205                    d.writeFloat(value);
206                } catch (IOException ignore) {
207                }
208            }
209    
210            public void writeDouble(double value) {
211                try {
212                    d.writeDouble(value);
213                } catch (IOException ignore) {
214                }
215            }
216    
217            public void writeUTF(String value) {
218                try {
219                    d.writeUTF(value);
220                } catch (IOException ignore) {
221                }
222            }
223    
224            public void writeTo(ClassOutputStream s) {
225                try {
226                    super.writeTo(s);
227                } catch (IOException ignore) {
228                }
229            }
230    
231            private DataOutputStream d;
232        }
233    
234        /**
235         * Writer for the entries in the constant pool.
236         */
237        protected static class ConstantPoolWriter
238               implements ConstantPool.Visitor<Integer,ClassOutputStream> {
239            protected int write(CPInfo info, ClassOutputStream out) {
240                out.writeByte(info.getTag());
241                return info.accept(this, out);
242            }
243    
244            public Integer visitClass(CONSTANT_Class_info info, ClassOutputStream out) {
245                out.writeShort(info.name_index);
246                return 1;
247            }
248    
249            public Integer visitDouble(CONSTANT_Double_info info, ClassOutputStream out) {
250                out.writeDouble(info.value);
251                return 2;
252            }
253    
254            public Integer visitFieldref(CONSTANT_Fieldref_info info, ClassOutputStream out) {
255                writeRef(info, out);
256                return 1;
257            }
258    
259            public Integer visitFloat(CONSTANT_Float_info info, ClassOutputStream out) {
260                out.writeFloat(info.value);
261                return 1;
262            }
263    
264            public Integer visitInteger(CONSTANT_Integer_info info, ClassOutputStream out) {
265                out.writeInt(info.value);
266                return 1;
267            }
268    
269            public Integer visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, ClassOutputStream out) {
270                writeRef(info, out);
271                return 1;
272            }
273    
274            public Integer visitLong(CONSTANT_Long_info info, ClassOutputStream out) {
275                out.writeLong(info.value);
276                return 2;
277            }
278    
279            public Integer visitNameAndType(CONSTANT_NameAndType_info info, ClassOutputStream out) {
280                out.writeShort(info.name_index);
281                out.writeShort(info.type_index);
282                return 1;
283            }
284    
285            public Integer visitMethodref(CONSTANT_Methodref_info info, ClassOutputStream out) {
286                return writeRef(info, out);
287            }
288    
289            public Integer visitString(CONSTANT_String_info info, ClassOutputStream out) {
290                out.writeShort(info.string_index);
291                return 1;
292            }
293    
294            public Integer visitUtf8(CONSTANT_Utf8_info info, ClassOutputStream out) {
295                out.writeUTF(info.value);
296                return 1;
297            }
298    
299            protected Integer writeRef(CPRefInfo info, ClassOutputStream out) {
300                out.writeShort(info.class_index);
301                out.writeShort(info.name_and_type_index);
302                return 1;
303            }
304        }
305    
306        /**
307         * Writer for the different types of attribute.
308         */
309        protected static class AttributeWriter implements Attribute.Visitor<Void,ClassOutputStream> {
310            public void write(Attributes attributes, ClassOutputStream out) {
311                int size = attributes.size();
312                out.writeShort(size);
313                for (Attribute a: attributes)
314                    write(a, out);
315            }
316    
317            // Note: due to the use of shared resources, this method is not reentrant.
318            public void write(Attribute attr, ClassOutputStream out) {
319                out.writeShort(attr.attribute_name_index);
320                sharedOut.reset();
321                attr.accept(this, sharedOut);
322                out.writeInt(sharedOut.size());
323                sharedOut.writeTo(out);
324            }
325    
326            protected ClassOutputStream sharedOut = new ClassOutputStream();
327            protected AnnotationWriter annotationWriter = new AnnotationWriter();
328    
329            public Void visitDefault(DefaultAttribute attr, ClassOutputStream out) {
330                out.write(attr.info, 0, attr.info.length);
331                return null;
332            }
333    
334            public Void visitAnnotationDefault(AnnotationDefault_attribute attr, ClassOutputStream out) {
335                annotationWriter.write(attr.default_value, out);
336                return null;
337            }
338    
339            public Void visitCharacterRangeTable(CharacterRangeTable_attribute attr, ClassOutputStream out) {
340                out.writeShort(attr.character_range_table.length);
341                for (CharacterRangeTable_attribute.Entry e: attr.character_range_table)
342                    writeCharacterRangeTableEntry(e, out);
343                return null;
344            }
345    
346            protected void writeCharacterRangeTableEntry(CharacterRangeTable_attribute.Entry entry, ClassOutputStream out) {
347                out.writeShort(entry.start_pc);
348                out.writeShort(entry.end_pc);
349                out.writeInt(entry.character_range_start);
350                out.writeInt(entry.character_range_end);
351                out.writeShort(entry.flags);
352            }
353    
354            public Void visitCode(Code_attribute attr, ClassOutputStream out) {
355                out.writeShort(attr.max_stack);
356                out.writeShort(attr.max_locals);
357                out.writeInt(attr.code.length);
358                out.write(attr.code, 0, attr.code.length);
359                out.writeShort(attr.exception_table.length);
360                for (Code_attribute.Exception_data e: attr.exception_table)
361                    writeExceptionTableEntry(e, out);
362                new AttributeWriter().write(attr.attributes, out);
363                return null;
364            }
365    
366            protected void writeExceptionTableEntry(Code_attribute.Exception_data exception_data, ClassOutputStream out) {
367                out.writeShort(exception_data.start_pc);
368                out.writeShort(exception_data.end_pc);
369                out.writeShort(exception_data.handler_pc);
370                out.writeShort(exception_data.catch_type);
371            }
372    
373            public Void visitCompilationID(CompilationID_attribute attr, ClassOutputStream out) {
374                out.writeShort(attr.compilationID_index);
375                return null;
376            }
377    
378            public Void visitConstantValue(ConstantValue_attribute attr, ClassOutputStream out) {
379                out.writeShort(attr.constantvalue_index);
380                return null;
381            }
382    
383            public Void visitDeprecated(Deprecated_attribute attr, ClassOutputStream out) {
384                return null;
385            }
386    
387            public Void visitEnclosingMethod(EnclosingMethod_attribute attr, ClassOutputStream out) {
388                out.writeShort(attr.class_index);
389                out.writeShort(attr.method_index);
390                return null;
391            }
392    
393            public Void visitExceptions(Exceptions_attribute attr, ClassOutputStream out) {
394                out.writeShort(attr.exception_index_table.length);
395                for (int i: attr.exception_index_table)
396                    out.writeShort(i);
397                return null;
398            }
399    
400            public Void visitInnerClasses(InnerClasses_attribute attr, ClassOutputStream out) {
401                out.writeShort(attr.classes.length);
402                for (InnerClasses_attribute.Info info: attr.classes)
403                    writeInnerClassesInfo(info, out);
404                return null;
405            }
406    
407            protected void writeInnerClassesInfo(InnerClasses_attribute.Info info, ClassOutputStream out) {
408                out.writeShort(info.inner_class_info_index);
409                out.writeShort(info.outer_class_info_index);
410                out.writeShort(info.inner_name_index);
411                writeAccessFlags(info.inner_class_access_flags, out);
412            }
413    
414            public Void visitLineNumberTable(LineNumberTable_attribute attr, ClassOutputStream out) {
415                out.writeShort(attr.line_number_table.length);
416                for (LineNumberTable_attribute.Entry e: attr.line_number_table)
417                    writeLineNumberTableEntry(e, out);
418                return null;
419            }
420    
421            protected void writeLineNumberTableEntry(LineNumberTable_attribute.Entry entry, ClassOutputStream out) {
422                out.writeShort(entry.start_pc);
423                out.writeShort(entry.line_number);
424            }
425    
426            public Void visitLocalVariableTable(LocalVariableTable_attribute attr, ClassOutputStream out) {
427                out.writeShort(attr.local_variable_table.length);
428                for (LocalVariableTable_attribute.Entry e: attr.local_variable_table)
429                    writeLocalVariableTableEntry(e, out);
430                return null;
431            }
432    
433            protected void writeLocalVariableTableEntry(LocalVariableTable_attribute.Entry entry, ClassOutputStream out) {
434                out.writeShort(entry.start_pc);
435                out.writeShort(entry.length);
436                out.writeShort(entry.name_index);
437                out.writeShort(entry.descriptor_index);
438                out.writeShort(entry.index);
439            }
440    
441            public Void visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, ClassOutputStream out) {
442                out.writeByte(attr.local_variable_table.length);
443                for (LocalVariableTypeTable_attribute.Entry e: attr.local_variable_table)
444                    writeLocalVariableTypeTableEntry(e, out);
445                return null;
446            }
447    
448            protected void writeLocalVariableTypeTableEntry(LocalVariableTypeTable_attribute.Entry entry, ClassOutputStream out) {
449                out.writeShort(entry.start_pc);
450                out.writeShort(entry.length);
451                out.writeShort(entry.name_index);
452                out.writeShort(entry.signature_index);
453                out.writeShort(entry.index);
454            }
455    
456            public Void visitModule(Module_attribute attr, ClassOutputStream out) {
457                out.writeShort(attr.module_name);
458                return null;
459            }
460    
461            public Void visitModuleExportTable(ModuleExportTable_attribute attr, ClassOutputStream out) {
462                out.writeShort(attr.export_type_table.length);
463                for (int i: attr.export_type_table)
464                    out.writeShort(i);
465                return null;
466            }
467    
468            public Void visitModuleMemberTable(ModuleMemberTable_attribute attr, ClassOutputStream out) {
469                out.writeShort(attr.package_member_table.length);
470                for (int i: attr.package_member_table)
471                    out.writeShort(i);
472                return null;
473            }
474    
475            public Void visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, ClassOutputStream out) {
476                annotationWriter.write(attr.annotations, out);
477                return null;
478            }
479    
480            public Void visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, ClassOutputStream out) {
481                annotationWriter.write(attr.annotations, out);
482                return null;
483            }
484    
485            public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, ClassOutputStream out) {
486                out.writeByte(attr.parameter_annotations.length);
487                for (Annotation[] annos: attr.parameter_annotations)
488                    annotationWriter.write(annos, out);
489                return null;
490            }
491    
492            public Void visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, ClassOutputStream out) {
493                out.writeByte(attr.parameter_annotations.length);
494                for (Annotation[] annos: attr.parameter_annotations)
495                    annotationWriter.write(annos, out);
496                return null;
497            }
498    
499            public Void visitSignature(Signature_attribute attr, ClassOutputStream out) {
500                out.writeShort(attr.signature_index);
501                return null;
502            }
503    
504            public Void visitSourceDebugExtension(SourceDebugExtension_attribute attr, ClassOutputStream out) {
505                out.write(attr.debug_extension, 0, attr.debug_extension.length);
506                return null;
507            }
508    
509            public Void visitSourceFile(SourceFile_attribute attr, ClassOutputStream out) {
510                out.writeShort(attr.sourcefile_index);
511                return null;
512            }
513    
514            public Void visitSourceID(SourceID_attribute attr, ClassOutputStream out) {
515                out.writeShort(attr.sourceID_index);
516                return null;
517            }
518    
519            public Void visitStackMap(StackMap_attribute attr, ClassOutputStream out) {
520                if (stackMapWriter == null)
521                    stackMapWriter = new StackMapTableWriter();
522    
523                out.writeShort(attr.entries.length);
524                for (stack_map_frame f: attr.entries)
525                    stackMapWriter.write(f, out);
526                return null;
527            }
528    
529            public Void visitStackMapTable(StackMapTable_attribute attr, ClassOutputStream out) {
530                if (stackMapWriter == null)
531                    stackMapWriter = new StackMapTableWriter();
532    
533                out.writeShort(attr.entries.length);
534                for (stack_map_frame f: attr.entries)
535                    stackMapWriter.write(f, out);
536                return null;
537            }
538    
539            public Void visitSynthetic(Synthetic_attribute attr, ClassOutputStream out) {
540                return null;
541            }
542    
543            protected void writeAccessFlags(AccessFlags flags, ClassOutputStream p) {
544                sharedOut.writeShort(flags.flags);
545            }
546    
547            protected StackMapTableWriter stackMapWriter;
548        }
549    
550        /**
551         * Writer for the frames of StackMap and StackMapTable attributes.
552         */
553        protected static class StackMapTableWriter
554                implements stack_map_frame.Visitor<Void,ClassOutputStream> {
555    
556            public void write(stack_map_frame frame, ClassOutputStream out) {
557                out.write(frame.frame_type);
558                frame.accept(this, out);
559            }
560    
561            public Void visit_same_frame(same_frame frame, ClassOutputStream p) {
562                return null;
563            }
564    
565            public Void visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame frame, ClassOutputStream out) {
566                writeVerificationTypeInfo(frame.stack[0], out);
567                return null;
568            }
569    
570            public Void visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended frame, ClassOutputStream out) {
571                out.writeShort(frame.offset_delta);
572                writeVerificationTypeInfo(frame.stack[0], out);
573                return null;
574            }
575    
576            public Void visit_chop_frame(chop_frame frame, ClassOutputStream out) {
577                out.writeShort(frame.offset_delta);
578                return null;
579            }
580    
581            public Void visit_same_frame_extended(same_frame_extended frame, ClassOutputStream out) {
582                out.writeShort(frame.offset_delta);
583                return null;
584            }
585    
586            public Void visit_append_frame(append_frame frame, ClassOutputStream out) {
587                out.writeShort(frame.offset_delta);
588                for (verification_type_info l: frame.locals)
589                    writeVerificationTypeInfo(l, out);
590                return null;
591            }
592    
593            public Void visit_full_frame(full_frame frame, ClassOutputStream out) {
594                out.writeShort(frame.offset_delta);
595                out.writeShort(frame.locals.length);
596                for (verification_type_info l: frame.locals)
597                    writeVerificationTypeInfo(l, out);
598                out.writeShort(frame.stack.length);
599                for (verification_type_info s: frame.stack)
600                    writeVerificationTypeInfo(s, out);
601                return null;
602            }
603    
604            protected void writeVerificationTypeInfo(verification_type_info info,
605                    ClassOutputStream out)  {
606                out.write(info.tag);
607                switch (info.tag) {
608                case ITEM_Top:
609                case ITEM_Integer:
610                case ITEM_Float:
611                case ITEM_Long:
612                case ITEM_Double:
613                case ITEM_Null:
614                case ITEM_UninitializedThis:
615                    break;
616    
617                case ITEM_Object:
618                    Object_variable_info o = (Object_variable_info) info;
619                    out.writeShort(o.cpool_index);
620                    break;
621    
622                case ITEM_Uninitialized:
623                    Uninitialized_variable_info u = (Uninitialized_variable_info) info;
624                    out.writeShort(u.offset);
625                    break;
626    
627                default:
628                    throw new Error();
629                }
630            }
631        }
632    
633        /**
634         * Writer for annotations and the values they contain.
635         */
636        protected static class AnnotationWriter
637                implements Annotation.element_value.Visitor<Void,ClassOutputStream> {
638            public void write(Annotation[] annos, ClassOutputStream out) {
639                out.writeShort(annos.length);
640                for (Annotation anno: annos)
641                    write(anno, out);
642            }
643    
644            public void write(Annotation anno, ClassOutputStream out) {
645                out.writeShort(anno.type_index);
646                out.writeShort(anno.element_value_pairs.length);
647                for (element_value_pair p: anno.element_value_pairs)
648                    write(p, out);
649            }
650    
651            public void write(element_value_pair pair, ClassOutputStream out) {
652                out.writeShort(pair.element_name_index);
653                write(pair.value, out);
654            }
655    
656            public void write(element_value ev, ClassOutputStream out) {
657                out.writeByte(ev.tag);
658                ev.accept(this, out);
659            }
660    
661            public Void visitPrimitive(Primitive_element_value ev, ClassOutputStream out) {
662                out.writeShort(ev.const_value_index);
663                return null;
664            }
665    
666            public Void visitEnum(Enum_element_value ev, ClassOutputStream out) {
667                out.writeShort(ev.type_name_index);
668                out.writeShort(ev.const_name_index);
669                return null;
670            }
671    
672            public Void visitClass(Class_element_value ev, ClassOutputStream out) {
673                out.writeShort(ev.class_info_index);
674                return null;
675            }
676    
677            public Void visitAnnotation(Annotation_element_value ev, ClassOutputStream out) {
678                write(ev.annotation_value, out);
679                return null;
680            }
681    
682            public Void visitArray(Array_element_value ev, ClassOutputStream out) {
683                out.writeShort(ev.num_values);
684                for (element_value v: ev.values)
685                    write(v, out);
686                return null;
687            }
688        }
689    }