• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 package javassist.bytecode;
17 
18 import java.io.OutputStream;
19 import java.io.DataOutputStream;
20 import java.io.ByteArrayOutputStream;
21 import 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  */
75 public 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      */
ClassFileWriter(int major, int minor)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      */
getConstPool()102     public ConstPoolWriter getConstPool() { return constPool; }
103 
104     /**
105      * Returns a filed writer.
106      */
getFieldWriter()107     public FieldWriter getFieldWriter() { return fields; }
108 
109     /**
110      * Returns a method writer.
111      */
getMethodWriter()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      */
end(int accessFlags, int thisClass, int superClass, int[] interfaces, AttributeWriter aw)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      */
end(DataOutputStream out, int accessFlags, int thisClass, int superClass, int[] interfaces, AttributeWriter aw)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          */
size()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          */
write(DataOutputStream out)232         public void write(DataOutputStream out) throws IOException;
233     }
234 
writeAttribute(ByteStream bs, AttributeWriter aw, int attrCount)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 
FieldWriter(ConstPoolWriter cp)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          */
add(int accessFlags, String name, String descriptor, AttributeWriter aw)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          */
add(int accessFlags, int name, int descriptor, AttributeWriter aw)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 
size()296         int size() { return fieldCount; }
297 
dataSize()298         int dataSize() { return output.size(); }
299 
300         /**
301          * Writes the added fields.
302          */
write(OutputStream out)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 
MethodWriter(ConstPoolWriter cp)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          */
begin(int accessFlags, String name, String descriptor, String[] exceptions, AttributeWriter aw)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          */
begin(int accessFlags, int name, int descriptor, int[] exceptions, AttributeWriter aw)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 
writeThrows(int[] exceptions)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          */
add(int b)412         public void add(int b) {
413             output.write(b);
414         }
415 
416         /**
417          * Appends a 16bit value of bytecode.
418          */
add16(int b)419         public void add16(int b) {
420             output.writeShort(b);
421         }
422 
423         /**
424          * Appends a 32bit value of bytecode.
425          */
add32(int b)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          */
addInvoke(int opcode, String targetClass, String methodName, String descriptor)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          */
codeEnd(int maxStack, int maxLocals)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          */
addCatch(int startPc, int endPc, int handlerPc, int catchType)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          */
end(StackMapTable.Writer smap, AttributeWriter aw)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 
size()505         int size() { return methodCount; }
506 
dataSize()507         int dataSize() { return output.size(); }
508 
509         /**
510          * Writes the added methods.
511          */
write(OutputStream out)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 
ConstPoolWriter(ByteStream out)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          */
addClassInfo(String[] classNames)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          */
addClassInfo(String jvmname)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          */
addClassInfo(int name)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          */
addNameAndTypeInfo(String name, String type)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          */
addNameAndTypeInfo(int name, int type)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          */
addFieldrefInfo(int classInfo, int nameAndTypeInfo)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          */
addMethodrefInfo(int classInfo, int nameAndTypeInfo)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          */
addInterfaceMethodrefInfo(int classInfo, int nameAndTypeInfo)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          */
addStringInfo(String str)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          */
addIntegerInfo(int i)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          */
addFloatInfo(float f)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          */
addLongInfo(long l)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          */
addDoubleInfo(double d)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          */
addUtf8Info(String utf8)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          */
end()727         void end() {
728             output.writeShort(startPos, num);
729         }
730     }
731 }
732