Source code

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