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 }