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  package org.apache.bcel.classfile;
20  
21  import java.io.DataInput;
22  import java.io.DataOutputStream;
23  import java.io.IOException;
24  import java.util.Arrays;
25  
26  import org.apache.bcel.Const;
27  import org.apache.bcel.util.Args;
28  import org.apache.commons.lang3.ArrayUtils;
29  
30  /**
31   * This class represents a chunk of Java byte code contained in a method. It is instantiated by the
32   * <em>Attribute.readAttribute()</em> method. A <em>Code</em> attribute contains informations about operand stack, local
33   * variables, byte code and the exceptions handled within this method.
34   *
35   * This attribute has attributes itself, namely <em>LineNumberTable</em> which is used for debugging purposes and
36   * <em>LocalVariableTable</em> which contains information about the local variables.
37   *
38   * <pre>
39   * Code_attribute {
40   *   u2 attribute_name_index;
41   *   u4 attribute_length;
42   *   u2 max_stack;
43   *   u2 max_locals;
44   *   u4 code_length;
45   *   u1 code[code_length];
46   *   u2 exception_table_length;
47   *   {
48   *     u2 start_pc;
49   *     u2 end_pc;
50   *     u2 handler_pc;
51   *     u2 catch_type;
52   *   } exception_table[exception_table_length];
53   *   u2 attributes_count;
54   *   attribute_info attributes[attributes_count];
55   * }
56   * </pre>
57   * @see Attribute
58   * @see CodeException
59   * @see LineNumberTable
60   * @see LocalVariableTable
61   */
62  public final class Code extends Attribute {
63  
64      private int maxStack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used)
65      private int maxLocals; // Number of local variables // TODO this could be made final (setter is not used)
66      private byte[] code; // Actual byte code
67      private CodeException[] exceptionTable; // Table of handled exceptions
68      private Attribute[] attributes; // or LocalVariable
69  
70      /**
71       * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
72       * physical copy.
73       *
74       * @param code The source Code.
75       */
76      public Code(final Code code) {
77          this(code.getNameIndex(), code.getLength(), code.getMaxStack(), code.getMaxLocals(), code.getCode(), code.getExceptionTable(), code.getAttributes(),
78                  code.getConstantPool());
79      }
80  
81      /**
82       * @param nameIndex Index pointing to the name <em>Code</em>
83       * @param length Content length in bytes
84       * @param file Input stream
85       * @param constantPool Array of constants
86       */
87      Code(final int nameIndex, final int length, final DataInput file, final ConstantPool constantPool) throws IOException {
88          // Initialize with some default values which will be overwritten later
89          this(nameIndex, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null, (CodeException[]) null, (Attribute[]) null, constantPool);
90          final int codeLength = Args.requireU4(file.readInt(), 1, "Code length attribute");
91          code = new byte[codeLength]; // Read byte code
92          file.readFully(code);
93          /*
94           * Read exception table that contains all regions where an exception handler is active, i.e., a try { ... } catch ()
95           * block.
96           */
97          final int exceptionTableLength = file.readUnsignedShort();
98          exceptionTable = new CodeException[exceptionTableLength];
99          for (int i = 0; i < exceptionTableLength; i++) {
100             exceptionTable[i] = new CodeException(file);
101         }
102         /*
103          * Read all attributes, currently 'LineNumberTable' and 'LocalVariableTable'
104          */
105         final int attributesCount = file.readUnsignedShort();
106         attributes = new Attribute[attributesCount];
107         for (int i = 0; i < attributesCount; i++) {
108             attributes[i] = readAttribute(file, constantPool);
109         }
110         /*
111          * Adjust length, because of setAttributes in this(), s.b. length is incorrect, because it didn't take the internal
112          * attributes into account yet! Very subtle bug, fixed in 3.1.1.
113          */
114         super.setLength(length);
115     }
116 
117     /**
118      * @param nameIndex Index pointing to the name <em>Code</em>
119      * @param length Content length in bytes
120      * @param maxStack Maximum size of stack
121      * @param maxLocals Number of local variables
122      * @param code Actual byte code
123      * @param exceptionTable of handled exceptions
124      * @param attributes Attributes of code: LineNumber or LocalVariable
125      * @param constantPool Array of constants
126      */
127     public Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable,
128         final Attribute[] attributes, final ConstantPool constantPool) {
129         super(Const.ATTR_CODE, nameIndex, length, constantPool);
130         this.maxStack = Args.requireU2(maxStack, "maxStack");
131         this.maxLocals = Args.requireU2(maxLocals, "maxLocals");
132         this.code = ArrayUtils.nullToEmpty(code);
133         this.exceptionTable = ArrayUtils.nullToEmpty(exceptionTable, CodeException[].class);
134         Args.requireU2(this.exceptionTable.length, "exceptionTable.length");
135         this.attributes = attributes != null ? attributes : EMPTY_ARRAY;
136         super.setLength(calculateLength()); // Adjust length
137     }
138 
139     /**
140      * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
141      * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
142      *
143      * @param v Visitor object
144      */
145     @Override
146     public void accept(final Visitor v) {
147         v.visitCode(this);
148     }
149 
150     /**
151      * @return the full size of this code attribute, minus its first 6 bytes, including the size of all its contained
152      *         attributes
153      */
154     private int calculateLength() {
155         int len = 0;
156         if (attributes != null) {
157             for (final Attribute attribute : attributes) {
158                 len += attribute.getLength() + 6 /* attribute header size */;
159             }
160         }
161         return len + getInternalLength();
162     }
163 
164     /**
165      * @return deep copy of this attribute
166      * @param constantPool the constant pool to duplicate
167      */
168     @Override
169     public Attribute copy(final ConstantPool constantPool) {
170         final Code c = (Code) clone();
171         if (code != null) {
172             c.code = code.clone();
173         }
174         c.setConstantPool(constantPool);
175         c.exceptionTable = new CodeException[exceptionTable.length];
176         Arrays.setAll(c.exceptionTable, i -> exceptionTable[i].copy());
177         c.attributes = new Attribute[attributes.length];
178         Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool));
179         return c;
180     }
181 
182     /**
183      * Dump code attribute to file stream in binary format.
184      *
185      * @param file Output file stream
186      * @throws IOException if an I/O error occurs.
187      */
188     @Override
189     public void dump(final DataOutputStream file) throws IOException {
190         super.dump(file);
191         file.writeShort(maxStack);
192         file.writeShort(maxLocals);
193         file.writeInt(code.length);
194         file.write(code, 0, code.length);
195         file.writeShort(exceptionTable.length);
196         for (final CodeException exception : exceptionTable) {
197             exception.dump(file);
198         }
199         file.writeShort(attributes.length);
200         for (final Attribute attribute : attributes) {
201             attribute.dump(file);
202         }
203     }
204 
205     /**
206      * @return Collection of code attributes.
207      * @see Attribute
208      */
209     public Attribute[] getAttributes() {
210         return attributes;
211     }
212 
213     /**
214      * @return Actual byte code of the method.
215      */
216     public byte[] getCode() {
217         return code;
218     }
219 
220     /**
221      * @return Table of handled exceptions.
222      * @see CodeException
223      */
224     public CodeException[] getExceptionTable() {
225         return exceptionTable;
226     }
227 
228     /**
229      * @return the internal length of this code attribute (minus the first 6 bytes) and excluding all its attributes
230      */
231     private int getInternalLength() {
232         return 2 /* maxStack */ + 2 /* maxLocals */ + 4 /* code length */
233             + code.length /* byte-code */
234             + 2 /* exception-table length */
235             + 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */
236             + 2 /* attributes count */;
237     }
238 
239     /**
240      * @return LineNumberTable of Code, if it has one
241      */
242     public LineNumberTable getLineNumberTable() {
243         for (final Attribute attribute : attributes) {
244             if (attribute instanceof LineNumberTable) {
245                 return (LineNumberTable) attribute;
246             }
247         }
248         return null;
249     }
250 
251     /**
252      * @return LocalVariableTable of Code, if it has one
253      */
254     public LocalVariableTable getLocalVariableTable() {
255         for (final Attribute attribute : attributes) {
256             if (attribute instanceof LocalVariableTable) {
257                 return (LocalVariableTable) attribute;
258             }
259         }
260         return null;
261     }
262 
263     /**
264      * Gets the local variable type table attribute {@link LocalVariableTypeTable}.
265      * @return LocalVariableTypeTable of Code, if it has one, null otherwise.
266      * @since 6.10.0
267      */
268     public LocalVariableTypeTable getLocalVariableTypeTable() {
269         for (final Attribute attribute : attributes) {
270             if (attribute instanceof LocalVariableTypeTable) {
271                 return (LocalVariableTypeTable) attribute;
272             }
273         }
274         return null;
275     }
276 
277     /**
278      * @return Number of local variables.
279      */
280     public int getMaxLocals() {
281         return maxLocals;
282     }
283 
284     /**
285      * @return Maximum size of stack used by this method.
286      */
287     public int getMaxStack() {
288         return maxStack;
289     }
290 
291     /**
292      * Finds the attribute of {@link StackMap} instance.
293      * @return StackMap of Code, if it has one, else null.
294      * @since 6.8.0
295      */
296     public StackMap getStackMap() {
297         for (final Attribute attribute : attributes) {
298             if (attribute instanceof StackMap) {
299                 return (StackMap) attribute;
300             }
301         }
302         return null;
303     }
304 
305     /**
306      * @param attributes the attributes to set for this Code
307      */
308     public void setAttributes(final Attribute[] attributes) {
309         this.attributes = attributes != null ? attributes : EMPTY_ARRAY;
310         super.setLength(calculateLength()); // Adjust length
311     }
312 
313     /**
314      * @param code byte code
315      */
316     public void setCode(final byte[] code) {
317         this.code = ArrayUtils.nullToEmpty(code);
318         super.setLength(calculateLength()); // Adjust length
319     }
320 
321     /**
322      * @param exceptionTable exception table
323      */
324     public void setExceptionTable(final CodeException[] exceptionTable) {
325         this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_ARRAY;
326         super.setLength(calculateLength()); // Adjust length
327     }
328 
329     /**
330      * @param maxLocals maximum number of local variables
331      */
332     public void setMaxLocals(final int maxLocals) {
333         this.maxLocals = maxLocals;
334     }
335 
336     /**
337      * @param maxStack maximum stack size
338      */
339     public void setMaxStack(final int maxStack) {
340         this.maxStack = maxStack;
341     }
342 
343     /**
344      * @return String representation of code chunk.
345      */
346     @Override
347     public String toString() {
348         return toString(true);
349     }
350 
351     /**
352      * Converts this object to a String.
353      *
354      * @param verbose Provides verbose output when true.
355      * @return String representation of code chunk.
356      */
357     public String toString(final boolean verbose) {
358         final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber
359         buf.append("Code(maxStack = ").append(maxStack).append(", maxLocals = ").append(maxLocals).append(", code_length = ").append(code.length).append(")\n")
360             .append(Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose));
361         if (exceptionTable.length > 0) {
362             buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n");
363             for (final CodeException exception : exceptionTable) {
364                 buf.append(exception.toString(super.getConstantPool(), verbose)).append("\n");
365             }
366         }
367         if (attributes.length > 0) {
368             buf.append("\nAttribute(s) = ");
369             for (final Attribute attribute : attributes) {
370                 buf.append("\n").append(attribute.getName()).append(":");
371                 buf.append("\n").append(attribute);
372             }
373         }
374         return buf.toString();
375     }
376 }