    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 * @version $Id: Code.java 1806200 2017-08-25 16:33:06Z ggregory $
    039 * @see     Attribute
    040 * @see     CodeException
    041 * @see     LineNumberTable
    042 * @see LocalVariableTable
    043 */
    044public final class Code extends Attribute {
    045
    046    private int max_stack; // Maximum size of stack used by this method  // TODO this could be made final (setter is not used)
    047    private int max_locals; // Number of local variables  // TODO this could be made final (setter is not used)
    048    private byte[] code; // Actual byte code
    049    private CodeException[] exception_table; // Table of handled exceptions
    050    private Attribute[] attributes; // or LocalVariable
    051
    052
    053    /**
    054     * Initialize from another object. Note that both objects use the same
    055     * references (shallow copy). Use copy() for a physical copy.
    056     */
    057    public Code(final Code c) {
    058        this(c.getNameIndex(), c.getLength(), c.getMaxStack(), c.getMaxLocals(), c.getCode(), c
    059                .getExceptionTable(), c.getAttributes(), c.getConstantPool());
    060    }
    061
    062
    063    /**
    064     * @param name_index Index pointing to the name <em>Code</em>
    065     * @param length Content length in bytes
    066     * @param file Input stream
    067     * @param constant_pool Array of constants
    068     */
    069    Code(final int name_index, final int length, final DataInput file, final ConstantPool constant_pool)
    070            throws IOException {
    071        // Initialize with some default values which will be overwritten later
    072        this(name_index, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null,
    073                (CodeException[]) null, (Attribute[]) null, constant_pool);
    074        final int code_length = file.readInt();
    075        code = new byte[code_length]; // Read byte code
    076        file.readFully(code);
    077        /* Read exception table that contains all regions where an exception
    078         * handler is active, i.e., a try { ... } catch() block.
    079         */
    080        final int exception_table_length = file.readUnsignedShort();
    081        exception_table = new CodeException[exception_table_length];
    082        for (int i = 0; i < exception_table_length; i++) {
    083            exception_table[i] = new CodeException(file);
    084        }
    085        /* Read all attributes, currently `LineNumberTable' and
    086         * `LocalVariableTable'
    087         */
    088        final int attributes_count = file.readUnsignedShort();
    089        attributes = new Attribute[attributes_count];
    090        for (int i = 0; i < attributes_count; i++) {
    091            attributes[i] = Attribute.readAttribute(file, constant_pool);
    092        }
    093        /* Adjust length, because of setAttributes in this(), s.b.  length
    094         * is incorrect, because it didn't take the internal attributes
    095         * into account yet! Very subtle bug, fixed in 3.1.1.
    096         */
    097        super.setLength(length);
    098    }
    099
    100
    101    /**
    102     * @param name_index Index pointing to the name <em>Code</em>
    103     * @param length Content length in bytes
    104     * @param max_stack Maximum size of stack
    105     * @param max_locals Number of local variables
    106     * @param code Actual byte code
    107     * @param exception_table Table of handled exceptions
    108     * @param attributes Attributes of code: LineNumber or LocalVariable
    109     * @param constant_pool Array of constants
    110     */
    111    public Code(final int name_index, final int length, final int max_stack, final int max_locals, final byte[] code,
    112            final CodeException[] exception_table, final Attribute[] attributes, final ConstantPool constant_pool) {
    113        super(Const.ATTR_CODE, name_index, length, constant_pool);
    114        this.max_stack = max_stack;
    115        this.max_locals = max_locals;
    116        this.code = code != null ? code : new byte[0];
    117        this.exception_table = exception_table != null ? exception_table : new CodeException[0];
    118        this.attributes = attributes != null ? attributes : new Attribute[0];
    119        super.setLength(calculateLength()); // Adjust length
    120    }
    121
    122
    123    /**
    124     * Called by objects that are traversing the nodes of the tree implicitely
    125     * defined by the contents of a Java class. I.e., the hierarchy of methods,
    126     * fields, attributes, etc. spawns a tree of objects.
    127     *
    128     * @param v Visitor object
    129     */
    130    @Override
    131    public void accept( final Visitor v ) {
    132        v.visitCode(this);
    133    }
    134
    135
    136    /**
    137     * Dump code attribute to file stream in binary format.
    138     *
    139     * @param file Output file stream
    140     * @throws IOException
    141     */
    142    @Override
    143    public final void dump( final DataOutputStream file ) throws IOException {
    144        super.dump(file);
    145        file.writeShort(max_stack);
    146        file.writeShort(max_locals);
    147        file.writeInt(code.length);
    148        file.write(code, 0, code.length);
    149        file.writeShort(exception_table.length);
    150        for (final CodeException exception : exception_table) {
    151            exception.dump(file);
    152        }
    153        file.writeShort(attributes.length);
    154        for (final Attribute attribute : attributes) {
    155            attribute.dump(file);
    156        }
    157    }
    158
    159
    160    /**
    161     * @return Collection of code attributes.
    162     * @see Attribute
    163     */
    164    public final Attribute[] getAttributes() {
    165        return attributes;
    166    }
    167
    168
    169    /**
    170     * @return LineNumberTable of Code, if it has one
    171     */
    172    public LineNumberTable getLineNumberTable() {
    173        for (final Attribute attribute : attributes) {
    174            if (attribute instanceof LineNumberTable) {
    175                return (LineNumberTable) attribute;
    176            }
    177        }
    178        return null;
    179    }
    180
    181
    182    /**
    183     * @return LocalVariableTable of Code, if it has one
    184     */
    185    public LocalVariableTable getLocalVariableTable() {
    186        for (final Attribute attribute : attributes) {
    187            if (attribute instanceof LocalVariableTable) {
    188                return (LocalVariableTable) attribute;
    189            }
    190        }
    191        return null;
    192    }
    193
    194
    195    /**
    196     * @return Actual byte code of the method.
    197     */
    198    public final byte[] getCode() {
    199        return code;
    200    }
    201
    202
    203    /**
    204     * @return Table of handled exceptions.
    205     * @see CodeException
    206     */
    207    public final CodeException[] getExceptionTable() {
    208        return exception_table;
    209    }
    210
    211
    212    /**
    213     * @return Number of local variables.
    214     */
    215    public final int getMaxLocals() {
    216        return max_locals;
    217    }
    218
    219
    220    /**
    221     * @return Maximum size of stack used by this method.
    222     */
    223    public final int getMaxStack() {
    224        return max_stack;
    225    }
    226
    227
    228    /**
    229     * @return the internal length of this code attribute (minus the first 6 bytes)
    230     * and excluding all its attributes
    231     */
    232    private int getInternalLength() {
    233        return 2 /*max_stack*/+ 2 /*max_locals*/+ 4 /*code length*/
    234                + code.length /*byte-code*/
    235                + 2 /*exception-table length*/
    236                + 8 * (exception_table == null ? 0 : exception_table.length) /* exception table */
    237                + 2 /* attributes count */;
    238    }
    239
    240
    241    /**
    242     * @return the full size of this code attribute, minus its first 6 bytes,
    243     * including the size of all its contained attributes
    244     */
    245    private int calculateLength() {
    246        int len = 0;
    247        if (attributes != null) {
    248            for (final Attribute attribute : attributes) {
    249                len += attribute.getLength() + 6 /*attribute header size*/;
    250            }
    251        }
    252        return len + getInternalLength();
    253    }
    254
    255
    256    /**
    257     * @param attributes the attributes to set for this Code
    258     */
    259    public final void setAttributes( final Attribute[] attributes ) {
    260        this.attributes = attributes != null ? attributes : new Attribute[0];
    261        super.setLength(calculateLength()); // Adjust length
    262    }
    263
    264
    265    /**
    266     * @param code byte code
    267     */
    268    public final void setCode( final byte[] code ) {
    269        this.code = code != null ? code : new byte[0];
    270        super.setLength(calculateLength()); // Adjust length
    271    }
    272
    273
    274    /**
    275     * @param exception_table exception table
    276     */
    277    public final void setExceptionTable( final CodeException[] exception_table ) {
    278        this.exception_table = exception_table != null ? exception_table : new CodeException[0];
    279        super.setLength(calculateLength()); // Adjust length
    280    }
    281
    282
    283    /**
    284     * @param max_locals maximum number of local variables
    285     */
    286    public final void setMaxLocals( final int max_locals ) {
    287        this.max_locals = max_locals;
    288    }
    289
    290
    291    /**
    292     * @param max_stack maximum stack size
    293     */
    294    public final void setMaxStack( final int max_stack ) {
    295        this.max_stack = max_stack;
    296    }
    297
    298
    299    /**
    300     * @return String representation of code chunk.
    301     */
    302    public final String toString( final boolean verbose ) {
    303        final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber
    304        buf.append("Code(max_stack = ").append(max_stack).append(", max_locals = ").append(
    305                max_locals).append(", code_length = ").append(code.length).append(")\n").append(
    306                Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose));
    307        if (exception_table.length > 0) {
    308            buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n");
    309            for (final CodeException exception : exception_table) {
    310                buf.append(exception.toString(super.getConstantPool(), verbose)).append("\n");
    311            }
    312        }
    313        if (attributes.length > 0) {
    314            buf.append("\nAttribute(s) = ");
    315            for (final Attribute attribute : attributes) {
    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 final 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.exception_table = new CodeException[exception_table.length];
    346        for (int i = 0; i < exception_table.length; i++) {
    347            c.exception_table[i] = exception_table[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}