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 1749603 2016-06-21 20:50:19Z 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}