Home | History | Annotate | Download | only in bytecode
      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      */
     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