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}