1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.bytecode;
17
18import java.io.OutputStream;
19import java.io.DataOutputStream;
20import java.io.ByteArrayOutputStream;
21import java.io.IOException;
22
23/**
24 * A quick class-file writer.  This is useful when a generated
25 * class file is simple and the code generation should be fast.
26 *
27 * <p>Example:
28 *
29 * <blockquote><pre>
30 * ClassFileWriter cfw = new ClassFileWriter(ClassFile.JAVA_4, 0);
31 * ConstPoolWriter cpw = cfw.getConstPool();
32 *
33 * FieldWriter fw = cfw.getFieldWriter();
34 * fw.add(AccessFlag.PUBLIC, "value", "I", null);
35 * fw.add(AccessFlag.PUBLIC, "value2", "J", null);
36 *
37 * int thisClass = cpw.addClassInfo("sample/Test");
38 * int superClass = cpw.addClassInfo("java/lang/Object");
39 *
40 * MethodWriter mw = cfw.getMethodWriter();
41 *
42 * mw.begin(AccessFlag.PUBLIC, MethodInfo.nameInit, "()V", null, null);
43 * mw.add(Opcode.ALOAD_0);
44 * mw.add(Opcode.INVOKESPECIAL);
45 * int signature = cpw.addNameAndTypeInfo(MethodInfo.nameInit, "()V");
46 * mw.add16(cpw.addMethodrefInfo(superClass, signature));
47 * mw.add(Opcode.RETURN);
48 * mw.codeEnd(1, 1);
49 * mw.end(null, null);
50 *
51 * mw.begin(AccessFlag.PUBLIC, "one", "()I", null, null);
52 * mw.add(Opcode.ICONST_1);
53 * mw.add(Opcode.IRETURN);
54 * mw.codeEnd(1, 1);
55 * mw.end(null, null);
56 *
57 * byte[] classfile = cfw.end(AccessFlag.PUBLIC, thisClass, superClass,
58 *                            null, null);
59 * </pre></blockquote>
60 *
61 * <p>The code above generates the following class:
62 *
63 * <blockquote><pre>
64 * package sample;
65 * public class Test {
66 *     public int value;
67 *     public long value2;
68 *     public Test() { super(); }
69 *     public one() { return 1; }
70 * }
71 * </pre></blockquote>
72 *
73 * @since 3.13
74 */
75public class ClassFileWriter {
76    private ByteStream output;
77    private ConstPoolWriter constPool;
78    private FieldWriter fields;
79    private MethodWriter methods;
80    int thisClass, superClass;
81
82    /**
83     * Constructs a class file writer.
84     *
85     * @param major     the major version ({@link ClassFile#JAVA_4}, {@link ClassFile#JAVA_5}, ...).
86     * @param minor     the minor version (0 for JDK 1.3 and later).
87     */
88    public ClassFileWriter(int major, int minor) {
89        output = new ByteStream(512);
90        output.writeInt(0xCAFEBABE); // magic
91        output.writeShort(minor);
92        output.writeShort(major);
93        constPool = new ConstPoolWriter(output);
94        fields = new FieldWriter(constPool);
95        methods = new MethodWriter(constPool);
96
97    }
98
99    /**
100     * Returns a constant pool.
101     */
102    public ConstPoolWriter getConstPool() { return constPool; }
103
104    /**
105     * Returns a filed writer.
106     */
107    public FieldWriter getFieldWriter() { return fields; }
108
109    /**
110     * Returns a method writer.
111     */
112    public MethodWriter getMethodWriter() { return methods; }
113
114    /**
115     * Ends writing and returns the contents of the class file.
116     *
117     * @param accessFlags       access flags.
118     * @param thisClass         this class.  an index indicating its <code>CONSTANT_Class_info</code>.
119     * @param superClass        super class.  an index indicating its <code>CONSTANT_Class_info</code>.
120     * @param interfaces        implemented interfaces.
121     *                          index numbers indicating their <code>ClassInfo</code>.
122     *                          It may be null.
123     * @param aw        attributes of the class file.  May be null.
124     *
125     * @see AccessFlag
126     */
127    public byte[] end(int accessFlags, int thisClass, int superClass,
128                      int[] interfaces, AttributeWriter aw) {
129        constPool.end();
130        output.writeShort(accessFlags);
131        output.writeShort(thisClass);
132        output.writeShort(superClass);
133        if (interfaces == null)
134            output.writeShort(0);
135        else {
136            int n = interfaces.length;
137            output.writeShort(n);
138            for (int i = 0; i < n; i++)
139                output.writeShort(interfaces[i]);
140        }
141
142        output.enlarge(fields.dataSize() + methods.dataSize() + 6);
143        try {
144            output.writeShort(fields.size());
145            fields.write(output);
146
147            output.writeShort(methods.size());
148            methods.write(output);
149        }
150        catch (IOException e) {}
151
152        writeAttribute(output, aw, 0);
153        return output.toByteArray();
154    }
155
156    /**
157     * Ends writing and writes the contents of the class file into the
158     * given output stream.
159     *
160     * @param accessFlags       access flags.
161     * @param thisClass         this class.  an index indicating its <code>CONSTANT_Class_info</code>.
162     * @param superClass        super class.  an index indicating its <code>CONSTANT_Class_info</code>.
163     * @param interfaces        implemented interfaces.
164     *                          index numbers indicating their <code>CONSTATNT_Class_info</code>.
165     *                          It may be null.
166     * @param aw        attributes of the class file.  May be null.
167     *
168     * @see AccessFlag
169     */
170    public void end(DataOutputStream out,
171                    int accessFlags, int thisClass, int superClass,
172                    int[] interfaces, AttributeWriter aw)
173        throws IOException
174    {
175        constPool.end();
176        output.writeTo(out);
177        out.writeShort(accessFlags);
178        out.writeShort(thisClass);
179        out.writeShort(superClass);
180        if (interfaces == null)
181            out.writeShort(0);
182        else {
183            int n = interfaces.length;
184            out.writeShort(n);
185            for (int i = 0; i < n; i++)
186                out.writeShort(interfaces[i]);
187        }
188
189        out.writeShort(fields.size());
190        fields.write(out);
191
192        out.writeShort(methods.size());
193        methods.write(out);
194        if (aw == null)
195            out.writeShort(0);
196        else {
197            out.writeShort(aw.size());
198            aw.write(out);
199        }
200    }
201
202    /**
203     * This writes attributes.
204     *
205     * <p>For example, the following object writes a synthetic attribute:
206     *
207     * <pre>
208     * ConstPoolWriter cpw = ...;
209     * final int tag = cpw.addUtf8Info("Synthetic");
210     * AttributeWriter aw = new AttributeWriter() {
211     *     public int size() {
212     *         return 1;
213     *     }
214     *     public void write(DataOutputStream out) throws java.io.IOException {
215     *         out.writeShort(tag);
216     *         out.writeInt(0);
217     *     }
218     * };
219     * </pre>
220     */
221    public static interface AttributeWriter {
222        /**
223         * Returns the number of attributes that this writer will
224         * write.
225         */
226        public int size();
227
228        /**
229         * Writes all the contents of the attributes.  The binary representation
230         * of the contents is an array of <code>attribute_info</code>.
231         */
232        public void write(DataOutputStream out) throws IOException;
233    }
234
235    static void writeAttribute(ByteStream bs, AttributeWriter aw, int attrCount) {
236        if (aw == null) {
237            bs.writeShort(attrCount);
238            return;
239        }
240
241        bs.writeShort(aw.size() + attrCount);
242        DataOutputStream dos = new DataOutputStream(bs);
243        try {
244            aw.write(dos);
245            dos.flush();
246        }
247        catch (IOException e) {}
248    }
249
250    /**
251     * Field.
252     */
253    public static final class FieldWriter {
254        protected ByteStream output;
255        protected ConstPoolWriter constPool;
256        private int fieldCount;
257
258        FieldWriter(ConstPoolWriter cp) {
259            output = new ByteStream(128);
260            constPool = cp;
261            fieldCount = 0;
262        }
263
264        /**
265         * Adds a new field.
266         *
267         * @param accessFlags       access flags.
268         * @param name              the field name.
269         * @param descriptor        the field type.
270         * @param aw                the attributes of the field.  may be null.
271         * @see AccessFlag
272         */
273        public void add(int accessFlags, String name, String descriptor, AttributeWriter aw) {
274            int nameIndex = constPool.addUtf8Info(name);
275            int descIndex = constPool.addUtf8Info(descriptor);
276            add(accessFlags, nameIndex, descIndex, aw);
277        }
278
279        /**
280         * Adds a new field.
281         *
282         * @param accessFlags       access flags.
283         * @param name              the field name.  an index indicating its <code>CONSTANT_Utf8_info</code>.
284         * @param descriptor        the field type.  an index indicating its <code>CONSTANT_Utf8_info</code>.
285         * @param aw                the attributes of the field.  may be null.
286         * @see AccessFlag
287         */
288        public void add(int accessFlags, int name, int descriptor, AttributeWriter aw) {
289            ++fieldCount;
290            output.writeShort(accessFlags);
291            output.writeShort(name);
292            output.writeShort(descriptor);
293            writeAttribute(output, aw, 0);
294        }
295
296        int size() { return fieldCount; }
297
298        int dataSize() { return output.size(); }
299
300        /**
301         * Writes the added fields.
302         */
303        void write(OutputStream out) throws IOException {
304            output.writeTo(out);
305        }
306    }
307
308    /**
309     * Method.
310     */
311    public static final class MethodWriter {
312        protected ByteStream output;
313        protected ConstPoolWriter constPool;
314        private int methodCount;
315        protected int codeIndex;
316        protected int throwsIndex;
317        protected int stackIndex;
318
319        private int startPos;
320        private boolean isAbstract;
321        private int catchPos;
322        private int catchCount;
323
324        MethodWriter(ConstPoolWriter cp) {
325            output = new ByteStream(256);
326            constPool = cp;
327            methodCount = 0;
328            codeIndex = 0;
329            throwsIndex = 0;
330            stackIndex = 0;
331        }
332
333        /**
334         * Starts Adding a new method.
335         *
336         * @param accessFlags       access flags.
337         * @param name              the method name.
338         * @param descriptor        the method signature.
339         * @param exceptions        throws clause.  It may be null.
340         *                          The class names must be the JVM-internal
341         *                          representations like <code>java/lang/Exception</code>.
342         * @param aw                attributes to the <code>Method_info</code>.
343         */
344        public void begin(int accessFlags, String name, String descriptor,
345                        String[] exceptions, AttributeWriter aw) {
346            int nameIndex = constPool.addUtf8Info(name);
347            int descIndex = constPool.addUtf8Info(descriptor);
348            int[] intfs;
349            if (exceptions == null)
350                intfs = null;
351            else
352                intfs = constPool.addClassInfo(exceptions);
353
354            begin(accessFlags, nameIndex, descIndex, intfs, aw);
355        }
356
357        /**
358         * Starts adding a new method.
359         *
360         * @param accessFlags       access flags.
361         * @param name              the method name.  an index indicating its <code>CONSTANT_Utf8_info</code>.
362         * @param descriptor        the field type.  an index indicating its <code>CONSTANT_Utf8_info</code>.
363         * @param exceptions        throws clause.  indexes indicating <code>CONSTANT_Class_info</code>s.
364         *                          It may be null.
365         * @param aw                attributes to the <code>Method_info</code>.
366         */
367        public void begin(int accessFlags, int name, int descriptor, int[] exceptions, AttributeWriter aw) {
368            ++methodCount;
369            output.writeShort(accessFlags);
370            output.writeShort(name);
371            output.writeShort(descriptor);
372            isAbstract = (accessFlags & AccessFlag.ABSTRACT) != 0;
373
374            int attrCount = isAbstract ? 0 : 1;
375            if (exceptions != null)
376                ++attrCount;
377
378            writeAttribute(output, aw, attrCount);
379
380            if (exceptions != null)
381                writeThrows(exceptions);
382
383            if (!isAbstract) {
384                if (codeIndex == 0)
385                    codeIndex = constPool.addUtf8Info(CodeAttribute.tag);
386
387                startPos = output.getPos();
388                output.writeShort(codeIndex);
389                output.writeBlank(12);   // attribute_length, maxStack, maxLocals, code_lenth
390            }
391
392            catchPos = -1;
393            catchCount = 0;
394        }
395
396        private void writeThrows(int[] exceptions) {
397            if (throwsIndex == 0)
398                throwsIndex = constPool.addUtf8Info(ExceptionsAttribute.tag);
399
400            output.writeShort(throwsIndex);
401            output.writeInt(exceptions.length * 2 + 2);
402            output.writeShort(exceptions.length);
403            for (int i = 0; i < exceptions.length; i++)
404                output.writeShort(exceptions[i]);
405        }
406
407        /**
408         * Appends an 8bit value of bytecode.
409         *
410         * @see Opcode
411         */
412        public void add(int b) {
413            output.write(b);
414        }
415
416        /**
417         * Appends a 16bit value of bytecode.
418         */
419        public void add16(int b) {
420            output.writeShort(b);
421        }
422
423        /**
424         * Appends a 32bit value of bytecode.
425         */
426        public void add32(int b) {
427            output.writeInt(b);
428        }
429
430        /**
431         * Appends a invokevirtual, inovkespecial, or invokestatic bytecode.
432         *
433         * @see Opcode
434         */
435        public void addInvoke(int opcode, String targetClass, String methodName,
436                              String descriptor) {
437            int target = constPool.addClassInfo(targetClass);
438            int nt = constPool.addNameAndTypeInfo(methodName, descriptor);
439            int method = constPool.addMethodrefInfo(target, nt);
440            add(opcode);
441            add16(method);
442        }
443
444        /**
445         * Ends appending bytecode.
446         */
447        public void codeEnd(int maxStack, int maxLocals) {
448            if (!isAbstract) {
449                output.writeShort(startPos + 6, maxStack);
450                output.writeShort(startPos + 8, maxLocals);
451                output.writeInt(startPos + 10, output.getPos() - startPos - 14);  // code_length
452                catchPos = output.getPos();
453                catchCount = 0;
454                output.writeShort(0);   // number of catch clauses
455            }
456        }
457
458        /**
459         * Appends an <code>exception_table</code> entry to the
460         * <code>Code_attribute</code>.  This method is available
461         * only after the <code>codeEnd</code> method is called.
462         *
463         * @param catchType     an index indicating a <code>CONSTANT_Class_info</code>.
464         */
465        public void addCatch(int startPc, int endPc, int handlerPc, int catchType) {
466            ++catchCount;
467            output.writeShort(startPc);
468            output.writeShort(endPc);
469            output.writeShort(handlerPc);
470            output.writeShort(catchType);
471        }
472
473        /**
474         * Ends adding a new method.  The <code>add</code> method must be
475         * called before the <code>end</code> method is called.
476         *
477         * @param smap              a stack map table.  may be null.
478         * @param aw                attributes to the <code>Code_attribute</code>.
479         *                          may be null.
480         */
481        public void end(StackMapTable.Writer smap, AttributeWriter aw) {
482            if (isAbstract)
483                return;
484
485            // exception_table_length
486            output.writeShort(catchPos, catchCount);
487
488            int attrCount = smap == null ? 0 : 1;
489            writeAttribute(output, aw, attrCount);
490
491            if (smap != null) {
492                if (stackIndex == 0)
493                    stackIndex = constPool.addUtf8Info(StackMapTable.tag);
494
495                output.writeShort(stackIndex);
496                byte[] data = smap.toByteArray();
497                output.writeInt(data.length);
498                output.write(data);
499            }
500
501            // Code attribute_length
502            output.writeInt(startPos + 2, output.getPos() - startPos - 6);
503        }
504
505        int size() { return methodCount; }
506
507        int dataSize() { return output.size(); }
508
509        /**
510         * Writes the added methods.
511         */
512        void write(OutputStream out) throws IOException {
513            output.writeTo(out);
514        }
515    }
516
517    /**
518     * Constant Pool.
519     */
520    public static final class ConstPoolWriter {
521        ByteStream output;
522        protected int startPos;
523        protected int num;
524
525        ConstPoolWriter(ByteStream out) {
526            output = out;
527            startPos = out.getPos();
528            num = 1;
529            output.writeShort(1);   // number of entries
530        }
531
532        /**
533         * Makes <code>CONSTANT_Class_info</code> objects for each class name.
534         *
535         * @return an array of indexes indicating <code>CONSTANT_Class_info</code>s.
536         */
537        public int[] addClassInfo(String[] classNames) {
538            int n = classNames.length;
539            int[] result = new int[n];
540            for (int i = 0; i < n; i++)
541                result[i] = addClassInfo(classNames[i]);
542
543            return result;
544        }
545
546        /**
547         * Adds a new <code>CONSTANT_Class_info</code> structure.
548         *
549         * <p>This also adds a <code>CONSTANT_Utf8_info</code> structure
550         * for storing the class name.
551         *
552         * @param jvmname   the JVM-internal representation of a class name.
553         *                  e.g. <code>java/lang/Object</code>.
554         * @return          the index of the added entry.
555         */
556        public int addClassInfo(String jvmname) {
557            int utf8 = addUtf8Info(jvmname);
558            output.write(ClassInfo.tag);
559            output.writeShort(utf8);
560            return num++;
561        }
562
563        /**
564         * Adds a new <code>CONSTANT_Class_info</code> structure.
565         *
566         * @param name      <code>name_index</code>
567         * @return          the index of the added entry.
568         */
569        public int addClassInfo(int name) {
570            output.write(ClassInfo.tag);
571            output.writeShort(name);
572            return num++;
573        }
574
575        /**
576         * Adds a new <code>CONSTANT_NameAndType_info</code> structure.
577         *
578         * @param name      <code>name_index</code>
579         * @param type      <code>descriptor_index</code>
580         * @return          the index of the added entry.
581         */
582        public int addNameAndTypeInfo(String name, String type) {
583            return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type));
584        }
585
586        /**
587         * Adds a new <code>CONSTANT_NameAndType_info</code> structure.
588         *
589         * @param name      <code>name_index</code>
590         * @param type      <code>descriptor_index</code>
591         * @return          the index of the added entry.
592         */
593        public int addNameAndTypeInfo(int name, int type) {
594            output.write(NameAndTypeInfo.tag);
595            output.writeShort(name);
596            output.writeShort(type);
597            return num++;
598        }
599
600        /**
601         * Adds a new <code>CONSTANT_Fieldref_info</code> structure.
602         *
603         * @param classInfo         <code>class_index</code>
604         * @param nameAndTypeInfo   <code>name_and_type_index</code>.
605         * @return          the index of the added entry.
606         */
607        public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) {
608            output.write(FieldrefInfo.tag);
609            output.writeShort(classInfo);
610            output.writeShort(nameAndTypeInfo);
611            return num++;
612        }
613
614        /**
615         * Adds a new <code>CONSTANT_Methodref_info</code> structure.
616         *
617         * @param classInfo         <code>class_index</code>
618         * @param nameAndTypeInfo   <code>name_and_type_index</code>.
619         * @return          the index of the added entry.
620         */
621        public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) {
622            output.write(MethodrefInfo.tag);
623            output.writeShort(classInfo);
624            output.writeShort(nameAndTypeInfo);
625            return num++;
626        }
627
628        /**
629         * Adds a new <code>CONSTANT_InterfaceMethodref_info</code>
630         * structure.
631         *
632         * @param classInfo         <code>class_index</code>
633         * @param nameAndTypeInfo   <code>name_and_type_index</code>.
634         * @return          the index of the added entry.
635         */
636        public int addInterfaceMethodrefInfo(int classInfo,
637                                             int nameAndTypeInfo) {
638            output.write(InterfaceMethodrefInfo.tag);
639            output.writeShort(classInfo);
640            output.writeShort(nameAndTypeInfo);
641            return num++;
642        }
643
644        /**
645         * Adds a new <code>CONSTANT_String_info</code>
646         * structure.
647         *
648         * <p>This also adds a new <code>CONSTANT_Utf8_info</code>
649         * structure.
650         *
651         * @return          the index of the added entry.
652         */
653        public int addStringInfo(String str) {
654            int utf8 = addUtf8Info(str);
655            output.write(StringInfo.tag);
656            output.writeShort(utf8);
657            return num++;
658        }
659
660        /**
661         * Adds a new <code>CONSTANT_Integer_info</code>
662         * structure.
663         *
664         * @return          the index of the added entry.
665         */
666        public int addIntegerInfo(int i) {
667            output.write(IntegerInfo.tag);
668            output.writeInt(i);
669            return num++;
670        }
671
672        /**
673         * Adds a new <code>CONSTANT_Float_info</code>
674         * structure.
675         *
676         * @return          the index of the added entry.
677         */
678        public int addFloatInfo(float f) {
679            output.write(FloatInfo.tag);
680            output.writeFloat(f);
681            return num++;
682        }
683
684        /**
685         * Adds a new <code>CONSTANT_Long_info</code>
686         * structure.
687         *
688         * @return          the index of the added entry.
689         */
690        public int addLongInfo(long l) {
691            output.write(LongInfo.tag);
692            output.writeLong(l);
693            int n = num;
694            num += 2;
695            return n;
696        }
697
698        /**
699         * Adds a new <code>CONSTANT_Double_info</code>
700         * structure.
701         *
702         * @return          the index of the added entry.
703         */
704        public int addDoubleInfo(double d) {
705            output.write(DoubleInfo.tag);
706            output.writeDouble(d);
707            int n = num;
708            num += 2;
709            return n;
710        }
711
712        /**
713         * Adds a new <code>CONSTANT_Utf8_info</code>
714         * structure.
715         *
716         * @return          the index of the added entry.
717         */
718        public int addUtf8Info(String utf8) {
719            output.write(Utf8Info.tag);
720            output.writeUTF(utf8);
721            return num++;
722        }
723
724        /**
725         * Writes the contents of this class pool.
726         */
727        void end() {
728            output.writeShort(startPos, num);
729        }
730    }
731}
732