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