    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 *
    017 */
    018package org.apache.bcel.classfile;
    019
    020import java.io.DataInput;
    021import java.io.DataOutputStream;
    022import java.io.IOException;
    023
    024import org.apache.bcel.Const;
    025
    026/**
    027 * This class represents a chunk of Java byte code contained in a
    028 * method. It is instantiated by the
    029 * <em>Attribute.readAttribute()</em> method. A <em>Code</em>
    030 * attribute contains informations about operand stack, local
    031 * variables, byte code and the exceptions handled within this
    032 * method.
    033 *
    034 * This attribute has attributes itself, namely <em>LineNumberTable</em> which
    035 * is used for debugging purposes and <em>LocalVariableTable</em> which
    036 * contains information about the local variables.
    037 *
    038 * @see     Attribute
    039 * @see     CodeException
    040 * @see     LineNumberTable
    041 * @see LocalVariableTable
    042 */
    043public final class Code extends Attribute {
    044
    045    private int maxStack; // Maximum size of stack used by this method  // TODO this could be made final (setter is not used)
    046    private int maxLocals; // Number of local variables  // TODO this could be made final (setter is not used)
    047    private byte[] code; // Actual byte code
    048    private CodeException[] exceptionTable; // Table of handled exceptions
    049    private Attribute[] attributes; // or LocalVariable
    050
    051
    052    /**
    053     * Initialize from another object. Note that both objects use the same
    054     * references (shallow copy). Use copy() for a physical copy.
    055     */
    056    public Code(final Code c) {
    057        this(c.getNameIndex(), c.getLength(), c.getMaxStack(), c.getMaxLocals(), c.getCode(), c
    058                .getExceptionTable(), c.getAttributes(), c.getConstantPool());
    059    }
    060
    061
    062    /**
    063     * @param name_index Index pointing to the name <em>Code</em>
    064     * @param length Content length in bytes
    065     * @param file Input stream
    066     * @param constant_pool Array of constants
    067     */
    068    Code(final int name_index, final int length, final DataInput file, final ConstantPool constant_pool)
    069            throws IOException {
    070        // Initialize with some default values which will be overwritten later
    071        this(name_index, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null,
    072                (CodeException[]) null, (Attribute[]) null, constant_pool);
    073        final int code_length = file.readInt();
    074        code = new byte[code_length]; // Read byte code
    075        file.readFully(code);
    076        /* Read exception table that contains all regions where an exception
    077         * handler is active, i.e., a try { ... } catch() block.
    078         */
    079        final int exception_table_length = file.readUnsignedShort();
    080        exceptionTable = new CodeException[exception_table_length];
    081        for (int i = 0; i < exception_table_length; i++) {
    082            exceptionTable[i] = new CodeException(file);
    083        }
    084        /* Read all attributes, currently `LineNumberTable' and
    085         * `LocalVariableTable'
    086         */
    087        final int attributes_count = file.readUnsignedShort();
    088        attributes = new Attribute[attributes_count];
    089        for (int i = 0; i < attributes_count; i++) {
    090            attributes[i] = Attribute.readAttribute(file, constant_pool);
    091        }
    092        /* Adjust length, because of setAttributes in this(), s.b.  length
    093         * is incorrect, because it didn't take the internal attributes
    094         * into account yet! Very subtle bug, fixed in 3.1.1.
    095         */
    096        super.setLength(length);
    097    }
    098
    099
    100    /**
    101     * @param name_index Index pointing to the name <em>Code</em>
    102     * @param length Content length in bytes
    103     * @param maxStack Maximum size of stack
    104     * @param maxLocals Number of local variables
    105     * @param code Actual byte code
    106     * @param exceptionTable of handled exceptions
    107     * @param attributes Attributes of code: LineNumber or LocalVariable
    108     * @param constant_pool Array of constants
    109     */
    110    public Code(final int name_index, final int length, final int maxStack, final int maxLocals, final byte[] code,
    111            final CodeException[] exceptionTable, final Attribute[] attributes, final ConstantPool constant_pool) {
    112        super(Const.ATTR_CODE, name_index, length, constant_pool);
    113        this.maxStack = maxStack;
    114        this.maxLocals = maxLocals;
    115        this.code = code != null ? code : new byte[0];
    116        this.exceptionTable = exceptionTable != null ? exceptionTable : new CodeException[0];
    117        this.attributes = attributes != null ? attributes : new Attribute[0];
    118        super.setLength(calculateLength()); // Adjust length
    119    }
    120
    121
    122    /**
    123     * Called by objects that are traversing the nodes of the tree implicitely
    124     * defined by the contents of a Java class. I.e., the hierarchy of methods,
    125     * fields, attributes, etc. spawns a tree of objects.
    126     *
    127     * @param v Visitor object
    128     */
    129    @Override
    130    public void accept( final Visitor v ) {
    131        v.visitCode(this);
    132    }
    133
    134
    135    /**
    136     * Dump code attribute to file stream in binary format.
    137     *
    138     * @param file Output file stream
    139     * @throws IOException
    140     */
    141    @Override
    142    public void dump( final DataOutputStream file ) throws IOException {
    143        super.dump(file);
    144        file.writeShort(maxStack);
    145        file.writeShort(maxLocals);
    146        file.writeInt(code.length);
    147        file.write(code, 0, code.length);
    148        file.writeShort(exceptionTable.length);
    149        for (final CodeException exception : exceptionTable) {
    150            exception.dump(file);
    151        }
    152        file.writeShort(attributes.length);
    153        for (final Attribute attribute : attributes) {
    154            attribute.dump(file);
    155        }
    156    }
    157
    158
    159    /**
    160     * @return Collection of code attributes.
    161     * @see Attribute
    162     */
    163    public Attribute[] getAttributes() {
    164        return attributes;
    165    }
    166
    167
    168    /**
    169     * @return LineNumberTable of Code, if it has one
    170     */
    171    public LineNumberTable getLineNumberTable() {
    172        for (final Attribute attribute : attributes) {
    173            if (attribute instanceof LineNumberTable) {
    174                return (LineNumberTable) attribute;
    175            }
    176        }
    177        return null;
    178    }
    179
    180
    181    /**
    182     * @return LocalVariableTable of Code, if it has one
    183     */
    184    public LocalVariableTable getLocalVariableTable() {
    185        for (final Attribute attribute : attributes) {
    186            if (attribute instanceof LocalVariableTable) {
    187                return (LocalVariableTable) attribute;
    188            }
    189        }
    190        return null;
    191    }
    192
    193
    194    /**
    195     * @return Actual byte code of the method.
    196     */
    197    public byte[] getCode() {
    198        return code;
    199    }
    200
    201
    202    /**
    203     * @return Table of handled exceptions.
    204     * @see CodeException
    205     */
    206    public CodeException[] getExceptionTable() {
    207        return exceptionTable;
    208    }
    209
    210
    211    /**
    212     * @return Number of local variables.
    213     */
    214    public int getMaxLocals() {
    215        return maxLocals;
    216    }
    217
    218
    219    /**
    220     * @return Maximum size of stack used by this method.
    221     */
    222    public int getMaxStack() {
    223        return maxStack;
    224    }
    225
    226
    227    /**
    228     * @return the internal length of this code attribute (minus the first 6 bytes)
    229     * and excluding all its attributes
    230     */
    231    private int getInternalLength() {
    232        return 2 /*maxStack*/+ 2 /*maxLocals*/+ 4 /*code length*/
    233                + code.length /*byte-code*/
    234                + 2 /*exception-table length*/
    235                + 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */
    236                + 2 /* attributes count */;
    237    }
    238
    239
    240    /**
    241     * @return the full size of this code attribute, minus its first 6 bytes,
    242     * including the size of all its contained attributes
    243     */
    244    private int calculateLength() {
    245        int len = 0;
    246        if (attributes != null) {
    247            for (final Attribute attribute : attributes) {
    248                len += attribute.getLength() + 6 /*attribute header size*/;
    249            }
    250        }
    251        return len + getInternalLength();
    252    }
    253
    254
    255    /**
    256     * @param attributes the attributes to set for this Code
    257     */
    258    public void setAttributes( final Attribute[] attributes ) {
    259        this.attributes = attributes != null ? attributes : new Attribute[0];
    260        super.setLength(calculateLength()); // Adjust length
    261    }
    262
    263
    264    /**
    265     * @param code byte code
    266     */
    267    public void setCode( final byte[] code ) {
    268        this.code = code != null ? code : new byte[0];
    269        super.setLength(calculateLength()); // Adjust length
    270    }
    271
    272
    273    /**
    274     * @param exceptionTable exception table
    275     */
    276    public void setExceptionTable( final CodeException[] exceptionTable ) {
    277        this.exceptionTable = exceptionTable != null ? exceptionTable : new CodeException[0];
    278        super.setLength(calculateLength()); // Adjust length
    279    }
    280
    281
    282    /**
    283     * @param maxLocals maximum number of local variables
    284     */
    285    public void setMaxLocals( final int maxLocals ) {
    286        this.maxLocals = maxLocals;
    287    }
    288
    289
    290    /**
    291     * @param maxStack maximum stack size
    292     */
    293    public void setMaxStack( final int maxStack ) {
    294        this.maxStack = maxStack;
    295    }
    296
    297
    298    /**
    299     * @return String representation of code chunk.
    300     */
    301    public String toString( final boolean verbose ) {
    302        final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber
    303        buf.append("Code(maxStack = ").append(maxStack).append(", maxLocals = ").append(
    304                maxLocals).append(", code_length = ").append(code.length).append(")\n").append(
    305                Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose));
    306        if (exceptionTable.length > 0) {
    307            buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n");
    308            for (final CodeException exception : exceptionTable) {
    309                buf.append(exception.toString(super.getConstantPool(), verbose)).append("\n");
    310            }
    311        }
    312        if (attributes.length > 0) {
    313            buf.append("\nAttribute(s) = ");
    314            for (final Attribute attribute : attributes) {
    315                buf.append("\n").append(attribute.getName()).append(":");
    316                buf.append("\n").append(attribute);
    317            }
    318        }
    319        return buf.toString();
    320    }
    321
    322
    323    /**
    324     * @return String representation of code chunk.
    325     */
    326    @Override
    327    public String toString() {
    328        return toString(true);
    329    }
    330
    331
    332    /**
    333     * @return deep copy of this attribute
    334     *
    335     * @param _constant_pool the constant pool to duplicate
    336     */
    337    @Override
    338    public Attribute copy( final ConstantPool _constant_pool ) {
    339        final Code c = (Code) clone();
    340        if (code != null) {
    341            c.code = new byte[code.length];
    342            System.arraycopy(code, 0, c.code, 0, code.length);
    343        }
    344        c.setConstantPool(_constant_pool);
    345        c.exceptionTable = new CodeException[exceptionTable.length];
    346        for (int i = 0; i < exceptionTable.length; i++) {
    347            c.exceptionTable[i] = exceptionTable[i].copy();
    348        }
    349        c.attributes = new Attribute[attributes.length];
    350        for (int i = 0; i < attributes.length; i++) {
    351            c.attributes[i] = attributes[i].copy(_constant_pool);
    352        }
    353        return c;
    354    }
    355}