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