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