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