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   */
18  package org.apache.bcel.classfile;
19  
20  import java.io.DataInput;
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  
24  import org.apache.bcel.Const;
25  
26  /**
27   * This class represents a chunk of Java byte code contained in a
28   * method. It is instantiated by the
29   * <em>Attribute.readAttribute()</em> method. A <em>Code</em>
30   * attribute contains informations about operand stack, local
31   * variables, byte code and the exceptions handled within this
32   * method.
33   *
34   * This attribute has attributes itself, namely <em>LineNumberTable</em> which
35   * is used for debugging purposes and <em>LocalVariableTable</em> which
36   * contains information about the local variables.
37   *
38   * @version $Id: Code.html 1018313 2017-09-18 09:03:04Z britter $
39   * @see     Attribute
40   * @see     CodeException
41   * @see     LineNumberTable
42   * @see LocalVariableTable
43   */
44  public final class Code extends Attribute {
45  
46      private int max_stack; // Maximum size of stack used by this method  // TODO this could be made final (setter is not used)
47      private int max_locals; // Number of local variables  // TODO this could be made final (setter is not used)
48      private byte[] code; // Actual byte code
49      private CodeException[] exception_table; // Table of handled exceptions
50      private Attribute[] attributes; // or LocalVariable
51  
52  
53      /**
54       * Initialize from another object. Note that both objects use the same
55       * references (shallow copy). Use copy() for a physical copy.
56       */
57      public Code(final Code c) {
58          this(c.getNameIndex(), c.getLength(), c.getMaxStack(), c.getMaxLocals(), c.getCode(), c
59                  .getExceptionTable(), c.getAttributes(), c.getConstantPool());
60      }
61  
62  
63      /**
64       * @param name_index Index pointing to the name <em>Code</em>
65       * @param length Content length in bytes
66       * @param file Input stream
67       * @param constant_pool Array of constants
68       */
69      Code(final int name_index, final int length, final DataInput file, final ConstantPool constant_pool)
70              throws IOException {
71          // Initialize with some default values which will be overwritten later
72          this(name_index, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null,
73                  (CodeException[]) null, (Attribute[]) null, constant_pool);
74          final int code_length = file.readInt();
75          code = new byte[code_length]; // Read byte code
76          file.readFully(code);
77          /* Read exception table that contains all regions where an exception
78           * handler is active, i.e., a try { ... } catch() block.
79           */
80          final int exception_table_length = file.readUnsignedShort();
81          exception_table = new CodeException[exception_table_length];
82          for (int i = 0; i < exception_table_length; i++) {
83              exception_table[i] = new CodeException(file);
84          }
85          /* Read all attributes, currently `LineNumberTable' and
86           * `LocalVariableTable'
87           */
88          final int attributes_count = file.readUnsignedShort();
89          attributes = new Attribute[attributes_count];
90          for (int i = 0; i < attributes_count; i++) {
91              attributes[i] = Attribute.readAttribute(file, constant_pool);
92          }
93          /* Adjust length, because of setAttributes in this(), s.b.  length
94           * is incorrect, because it didn't take the internal attributes
95           * into account yet! Very subtle bug, fixed in 3.1.1.
96           */
97          super.setLength(length);
98      }
99  
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 }