• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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$
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      */
Code(final Code c)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      */
Code(final int name_index, final int length, final DataInput file, final ConstantPool constant_pool)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      */
Code(final int name_index, final int length, final int max_stack, final int max_locals, final byte[] code, final CodeException[] exception_table, final Attribute[] attributes, final ConstantPool constant_pool)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
accept( final Visitor v )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
dump( final DataOutputStream file )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      */
getAttributes()164     public final Attribute[] getAttributes() {
165         return attributes;
166     }
167 
168 
169     /**
170      * @return LineNumberTable of Code, if it has one
171      */
getLineNumberTable()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      */
getLocalVariableTable()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      */
getCode()198     public final byte[] getCode() {
199         return code;
200     }
201 
202 
203     /**
204      * @return Table of handled exceptions.
205      * @see CodeException
206      */
getExceptionTable()207     public final CodeException[] getExceptionTable() {
208         return exception_table;
209     }
210 
211 
212     /**
213      * @return Number of local variables.
214      */
getMaxLocals()215     public final int getMaxLocals() {
216         return max_locals;
217     }
218 
219 
220     /**
221      * @return Maximum size of stack used by this method.
222      */
getMaxStack()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      */
getInternalLength()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      */
calculateLength()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      */
setAttributes( final Attribute[] attributes )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      */
setCode( final byte[] code )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      */
setExceptionTable( final CodeException[] exception_table )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      */
setMaxLocals( final int max_locals )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      */
setMaxStack( final int max_stack )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      */
toString( final boolean verbose )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
toString()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
copy( final ConstantPool _constant_pool )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 }
356