View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.bcel.classfile;
21  
22  import java.io.DataInput;
23  import java.io.DataOutputStream;
24  import java.io.IOException;
25  import java.util.Arrays;
26  
27  import org.apache.bcel.Const;
28  import org.apache.bcel.util.Args;
29  import org.apache.commons.lang3.ArrayUtils;
30  
31  /**
32   * This class represents a chunk of Java byte code contained in a method. It is instantiated by the
33   * <em>Attribute.readAttribute()</em> method. A <em>Code</em> attribute contains informations about operand stack, local
34   * variables, byte code and the exceptions handled within this method.
35   *
36   * This attribute has attributes itself, namely <em>LineNumberTable</em> which is used for debugging purposes and
37   * <em>LocalVariableTable</em> which contains information about the local variables.
38   *
39   * <pre>
40   * Code_attribute {
41   *   u2 attribute_name_index;
42   *   u4 attribute_length;
43   *   u2 max_stack;
44   *   u2 max_locals;
45   *   u4 code_length;
46   *   u1 code[code_length];
47   *   u2 exception_table_length;
48   *   {
49   *     u2 start_pc;
50   *     u2 end_pc;
51   *     u2 handler_pc;
52   *     u2 catch_type;
53   *   } exception_table[exception_table_length];
54   *   u2 attributes_count;
55   *   attribute_info attributes[attributes_count];
56   * }
57   * </pre>
58   *
59   * @see Attribute
60   * @see CodeException
61   * @see LineNumberTable
62   * @see LocalVariableTable
63   */
64  public final class Code extends Attribute {
65  
66      private int maxStack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used)
67      private int maxLocals; // Number of local variables // TODO this could be made final (setter is not used)
68      private byte[] code; // Actual byte code
69      private CodeException[] exceptionTable; // Table of handled exceptions
70      private Attribute[] attributes; // or LocalVariable
71  
72      /**
73       * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
74       * physical copy.
75       *
76       * @param code The source Code.
77       */
78      public Code(final Code code) {
79          this(code.getNameIndex(), code.getLength(), code.getMaxStack(), code.getMaxLocals(), code.getCode(), code.getExceptionTable(), code.getAttributes(),
80                  code.getConstantPool());
81      }
82  
83      /**
84       * Constructs a Code attribute object from a DataInput.
85       *
86       * @param nameIndex    Index pointing to the name <em>Code</em>.
87       * @param length       Content length in bytes.
88       * @param dataInput    Data input.
89       * @param constantPool Array of constants.
90       * @throws ClassFormatException if the code array read from {@code file} is greater than {@link Const#MAX_CODE_SIZE}.
91       */
92      Code(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool) throws IOException {
93          // Initialize with some default values which will be overwritten later
94          this(nameIndex, length, dataInput.readUnsignedShort(), dataInput.readUnsignedShort(), (byte[]) null, (CodeException[]) null, (Attribute[]) null,
95                  constantPool);
96          final int codeLength = Args.requireU4(dataInput.readInt(), 0, Const.MAX_CODE_SIZE, "Code length attribute");
97          code = new byte[codeLength]; // Read byte code
98          dataInput.readFully(code);
99          /*
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 }