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}