001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.bcel.classfile; 021 022import java.io.DataInput; 023import java.io.DataOutputStream; 024import java.io.IOException; 025import java.util.Arrays; 026 027import org.apache.bcel.Const; 028import org.apache.bcel.util.Args; 029import org.apache.commons.lang3.ArrayUtils; 030 031/** 032 * This class represents a chunk of Java byte code contained in a method. It is instantiated by the 033 * <em>Attribute.readAttribute()</em> method. A <em>Code</em> attribute contains information about operand stack, local 034 * variables, byte code and the exceptions handled within this method. 035 * 036 * This attribute has attributes itself, namely <em>LineNumberTable</em> which is used for debugging purposes and 037 * <em>LocalVariableTable</em> which contains information about the local variables. 038 * 039 * <pre> 040 * Code_attribute { 041 * u2 attribute_name_index; 042 * u4 attribute_length; 043 * u2 max_stack; 044 * u2 max_locals; 045 * u4 code_length; 046 * u1 code[code_length]; 047 * u2 exception_table_length; 048 * { 049 * u2 start_pc; 050 * u2 end_pc; 051 * u2 handler_pc; 052 * u2 catch_type; 053 * } exception_table[exception_table_length]; 054 * u2 attributes_count; 055 * attribute_info attributes[attributes_count]; 056 * } 057 * </pre> 058 * 059 * <p> 060 * The layout above uses the standard Code header. This class also supports compact Code headers found in historical class file versions 45.0 through 45.2. 061 * Here, "compact" describes the narrower encoding of three fields: {@code max_stack} and {@code max_locals} each use {@code u1}, and {@code code_length} 062 * uses {@code u2}, instead of {@code u2}, {@code u2}, and {@code u4}, respectively. The notation {@code u1}, {@code u2}, and {@code u4} denotes unsigned 063 * one-, two-, and four-byte values. These three fields therefore occupy 4 bytes instead of 8, excluding the common six-byte attribute prefix. 064 * The layout of that prefix and the remaining Code fields is unchanged; the bytecode itself is not compressed. 065 * {@link #isCompactCodeHeader()} identifies this narrower layout. 066 * </p> 067 * 068 * <p> 069 * Class file versions 45.0 through 45.2 also occur with standard Code headers accepted by modern JVMs, so the version alone does not identify the layout. 070 * {@link ClassParser} uses standard headers by default; {@link ClassParser#setCompactCodeHeaderEnabled(boolean)} explicitly selects compact headers 071 * for those versions. 072 * </p> 073 * 074 * @see Attribute 075 * @see CodeException 076 * @see LineNumberTable 077 * @see LocalVariableTable 078 */ 079public final class Code extends Attribute { 080 081 /** 082 * Whether {@code max_stack}, {@code max_locals}, and {@code code_length} use {@code u1/u1/u2} (4 bytes) instead of {@code u2/u2/u4} (8 bytes). 083 * Compact describes these field widths, not compression of the bytecode or the rest of the attribute. 084 */ 085 private boolean compactCodeHeader; 086 private int maxStack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used) 087 private int maxLocals; // Number of local variables // TODO this could be made final (setter is not used) 088 private byte[] code; // Actual byte code 089 private CodeException[] exceptionTable; // Table of handled exceptions 090 private Attribute[] attributes; // or LocalVariable 091 092 /** 093 * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a 094 * physical copy. 095 * 096 * @param code The source Code. 097 */ 098 public Code(final Code code) { 099 this(code.getNameIndex(), code.getLength(), code.getMaxStack(), code.getMaxLocals(), code.getCode(), code.getExceptionTable(), code.getAttributes(), 100 code.getConstantPool(), code.compactCodeHeader); 101 } 102 103 /** 104 * Constructs a Code attribute object from a DataInput. 105 * 106 * @param nameIndex Index pointing to the name <em>Code</em>. 107 * @param length Content length in bytes. 108 * @param dataInput Data input. 109 * @param constantPool Array of constants. 110 * @throws ClassFormatException Thrown if the code array read from {@code file} is greater than {@link Const#MAX_CODE_SIZE}. 111 */ 112 Code(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool) throws IOException { 113 this(nameIndex, length, dataInput, constantPool, false); 114 } 115 116 /** 117 * Reads a Code attribute using the selected header layout. 118 */ 119 Code(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool, final boolean compactCodeHeader) throws IOException { 120 // Compact headers encode max_stack/max_locals/code_length as u1/u1/u2; standard headers use u2/u2/u4. 121 // Initialize the remaining fields with default values, then read them below. 122 this(nameIndex, length, compactCodeHeader ? dataInput.readUnsignedByte() : dataInput.readUnsignedShort(), 123 compactCodeHeader ? dataInput.readUnsignedByte() : dataInput.readUnsignedShort(), null, null, null, constantPool, compactCodeHeader); 124 final int codeLength = Args.requireU4(compactCodeHeader ? dataInput.readUnsignedShort() : dataInput.readInt(), 0, Const.MAX_CODE_SIZE, 125 "Code length attribute"); 126 code = new byte[codeLength]; // Read byte code 127 dataInput.readFully(code); 128 /* 129 * Read exception table that contains all regions where an exception handler is active, that is, a try { ... } catch () block. 130 */ 131 final int exceptionTableLength = dataInput.readUnsignedShort(); 132 exceptionTable = new CodeException[exceptionTableLength]; 133 for (int i = 0; i < exceptionTableLength; i++) { 134 exceptionTable[i] = new CodeException(dataInput); 135 } 136 /* 137 * Read all attributes, currently 'LineNumberTable' and 'LocalVariableTable' 138 */ 139 final int attributesCount = dataInput.readUnsignedShort(); 140 attributes = new Attribute[attributesCount]; 141 for (int i = 0; i < attributesCount; i++) { 142 attributes[i] = readAttribute(dataInput, constantPool, compactCodeHeader); 143 } 144 /* 145 * Adjust length, because of setAttributes in this(), s.b. length is incorrect, because it didn't take the internal attributes into account yet! Very 146 * subtle bug, fixed in 3.1.1. 147 */ 148 super.setLength(length); 149 } 150 151 /** 152 * Constructs a Code attribute using a standard eight-byte header. 153 * 154 * <p> 155 * Use {@link #setCompactCodeHeader(boolean)} to select the compact four-byte header after construction. 156 * </p> 157 * 158 * @param nameIndex Index pointing to the name <em>Code</em>. 159 * @param length Content length in bytes. 160 * @param maxStack Maximum size of stack. 161 * @param maxLocals Number of local variables. 162 * @param code Actual byte code. 163 * @param exceptionTable of handled exceptions. 164 * @param attributes Attributes of code: LineNumber or LocalVariable. 165 * @param constantPool Array of constants. 166 * @throws ClassFormatException Thrown if the code array is greater than {@link Const#MAX_CODE_SIZE}. 167 */ 168 public Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable, 169 final Attribute[] attributes, final ConstantPool constantPool) { 170 this(nameIndex, length, maxStack, maxLocals, code, exceptionTable, attributes, constantPool, false); 171 } 172 173 private Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable, 174 final Attribute[] attributes, final ConstantPool constantPool, final boolean compactCodeHeader) { 175 super(Const.ATTR_CODE, nameIndex, length, constantPool); 176 this.compactCodeHeader = compactCodeHeader; 177 this.maxStack = Args.requireU2(maxStack, "maxStack"); 178 this.maxLocals = Args.requireU2(maxLocals, "maxLocals"); 179 this.code = ArrayUtils.nullToEmpty(code); 180 Args.requireU4(this.code.length, 0, Const.MAX_CODE_SIZE, "Code length attribute"); 181 this.exceptionTable = ArrayUtils.nullToEmpty(exceptionTable, CodeException[].class); 182 Args.requireU2(this.exceptionTable.length, "exceptionTable.length"); 183 this.attributes = attributes != null ? attributes : EMPTY_ARRAY; 184 super.setLength(calculateLength()); // Adjust length 185 } 186 187 /** 188 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. 189 * That is, the hierarchy of methods, fields, attributes, and other elements forms a tree of objects. 190 * 191 * @param v Visitor object. 192 */ 193 @Override 194 public void accept(final Visitor v) { 195 v.visitCode(this); 196 } 197 198 /** 199 * @return The full size of this code attribute, minus its first 6 bytes, including the size of all its contained attributes. 200 */ 201 private int calculateLength() { 202 int len = 0; 203 if (attributes != null) { 204 for (final Attribute attribute : attributes) { 205 len += attribute.getLength() + 6 /* attribute header size */; 206 } 207 } 208 return len + getInternalLength(); 209 } 210 211 /** 212 * Creates a deep copy of this attribute. 213 * 214 * @param constantPool The constant pool to duplicate. 215 * @return deep copy of this attribute. 216 */ 217 @Override 218 public Attribute copy(final ConstantPool constantPool) { 219 final Code c = (Code) clone(); 220 if (code != null) { 221 c.code = code.clone(); 222 } 223 c.setConstantPool(constantPool); 224 c.exceptionTable = new CodeException[exceptionTable.length]; 225 Arrays.setAll(c.exceptionTable, i -> exceptionTable[i].copy()); 226 c.attributes = new Attribute[attributes.length]; 227 Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool)); 228 return c; 229 } 230 231 /** 232 * Dumps code attribute to file stream in binary format. 233 * 234 * @param file Output file stream. 235 * @throws IOException Thrown if an I/O error occurs. 236 * @throws ClassFormatException Thrown if a compact header's {@code max_stack} or {@code max_locals} is outside 0 through 255, 237 * or its code length exceeds 65535 bytes. 238 */ 239 @Override 240 public void dump(final DataOutputStream file) throws IOException { 241 if (compactCodeHeader) { 242 Args.requireU1(maxStack, "maxStack"); 243 Args.requireU1(maxLocals, "maxLocals"); 244 Args.requireU2(code.length, "Code length attribute"); 245 } 246 super.dump(file); 247 if (compactCodeHeader) { 248 file.writeByte(maxStack); 249 file.writeByte(maxLocals); 250 file.writeShort(code.length); 251 } else { 252 file.writeShort(maxStack); 253 file.writeShort(maxLocals); 254 file.writeInt(code.length); 255 } 256 file.write(code, 0, code.length); 257 file.writeShort(Args.requireU2(exceptionTable.length, "exceptionTable.length")); 258 for (final CodeException exception : exceptionTable) { 259 exception.dump(file); 260 } 261 file.writeShort(Args.requireU2(attributes.length, "attributes.length")); 262 for (final Attribute attribute : attributes) { 263 attribute.dump(file); 264 } 265 } 266 267 /** 268 * Gets the collection of code attributes. 269 * 270 * @return Collection of code attributes. 271 * @see Attribute 272 */ 273 public Attribute[] getAttributes() { 274 return attributes; 275 } 276 277 /** 278 * Gets the actual byte code of the method. 279 * 280 * @return Actual byte code of the method. 281 */ 282 public byte[] getCode() { 283 return code; 284 } 285 286 /** 287 * Gets the table of handled exceptions. 288 * 289 * @return Table of handled exceptions. 290 * @see CodeException 291 */ 292 public CodeException[] getExceptionTable() { 293 return exceptionTable; 294 } 295 296 /** 297 * Gets the internal length of this code attribute (minus the first 6 bytes) and excluding all its attributes. 298 * 299 * @return The internal length of this code attribute (minus the first 6 bytes) and excluding all its attributes. 300 */ 301 private int getInternalLength() { 302 // Only max_stack, max_locals, and code_length differ: 1 + 1 + 2 compact bytes versus 2 + 2 + 4 standard bytes. 303 return (compactCodeHeader ? 4 : 8) 304 + code.length /* byte-code */ 305 + 2 /* exception-table length */ 306 + 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */ 307 + 2 /* attributes count */; 308 } 309 310 /** 311 * Gets the LineNumberTable of Code, if it has one. 312 * 313 * @return LineNumberTable of Code, if it has one. 314 */ 315 public LineNumberTable getLineNumberTable() { 316 for (final Attribute attribute : attributes) { 317 if (attribute instanceof LineNumberTable) { 318 return (LineNumberTable) attribute; 319 } 320 } 321 return null; 322 } 323 324 /** 325 * Gets the LocalVariableTable of Code, if it has one. 326 * 327 * @return LocalVariableTable of Code, if it has one. 328 */ 329 public LocalVariableTable getLocalVariableTable() { 330 for (final Attribute attribute : attributes) { 331 if (attribute instanceof LocalVariableTable) { 332 return (LocalVariableTable) attribute; 333 } 334 } 335 return null; 336 } 337 338 /** 339 * Gets the local variable type table attribute {@link LocalVariableTypeTable}. 340 * 341 * @return LocalVariableTypeTable of Code, if it has one, null otherwise. 342 * @since 6.10.0 343 */ 344 public LocalVariableTypeTable getLocalVariableTypeTable() { 345 for (final Attribute attribute : attributes) { 346 if (attribute instanceof LocalVariableTypeTable) { 347 return (LocalVariableTypeTable) attribute; 348 } 349 } 350 return null; 351 } 352 353 /** 354 * Gets the number of local variables. 355 * 356 * @return Number of local variables. 357 */ 358 public int getMaxLocals() { 359 return maxLocals; 360 } 361 362 /** 363 * Gets the maximum size of stack used by this method. 364 * 365 * @return Maximum size of stack used by this method. 366 */ 367 public int getMaxStack() { 368 return maxStack; 369 } 370 371 /** 372 * Gets the {@link StackMap} attribute. 373 * 374 * @return StackMap of Code, if it has one, else null. 375 * @since 6.8.0 376 */ 377 public StackMap getStackMap() { 378 for (final Attribute attribute : attributes) { 379 if (attribute instanceof StackMap) { 380 return (StackMap) attribute; 381 } 382 } 383 return null; 384 } 385 386 /** 387 * Gets whether this attribute uses the compact four-byte Code header. 388 * 389 * @return Whether {@code max_stack}, {@code max_locals}, and {@code code_length} use {@code u1}, {@code u1}, and {@code u2}, respectively, 390 * instead of the standard {@code u2}, {@code u2}, and {@code u4}. 391 * @since 6.14.0 392 * @see #setCompactCodeHeader(boolean) 393 */ 394 public boolean isCompactCodeHeader() { 395 return compactCodeHeader; 396 } 397 398 /** 399 * Sets the attributes for this Code. 400 * 401 * @param attributes The attributes to set for this Code. 402 */ 403 public void setAttributes(final Attribute[] attributes) { 404 this.attributes = attributes != null ? attributes : EMPTY_ARRAY; 405 super.setLength(calculateLength()); // Adjust length 406 } 407 408 /** 409 * Sets the byte code. 410 * 411 * @param code byte code. 412 * @throws ClassFormatException Thrown if the code array is greater than {@link Const#MAX_CODE_SIZE}. 413 */ 414 public void setCode(final byte[] code) { 415 this.code = ArrayUtils.nullToEmpty(code); 416 Args.requireU4(this.code.length, 0, Const.MAX_CODE_SIZE, "Code length attribute"); 417 super.setLength(calculateLength()); // Adjust length 418 } 419 420 /** 421 * Sets the Code header layout and recalculates the attribute length. 422 * 423 * <p> 424 * Compact headers encode {@code max_stack}, {@code max_locals}, and {@code code_length} as {@code u1}, {@code u1}, and {@code u2}, respectively, 425 * totaling 4 bytes instead of the standard 8. This limits {@code max_stack} and {@code max_locals} to 0 through 255 and the code length to 65535 bytes. 426 * The remaining attribute layout is unchanged. Compact headers are intended for historical class file versions 45.0 through 45.2. 427 * Enabling compact headers validates these values before changing the layout. Subsequent changes to them are checked by {@link #dump(DataOutputStream)}. 428 * </p> 429 * 430 * <p> 431 * This setting controls this attribute's own dump. When dumping an entire class, {@link JavaClass#dump(DataOutputStream)} selects one layout for 432 * all method Code attributes based on the class version and the methods' header settings, without changing this attribute's setting. 433 * </p> 434 * 435 * @param compactCodeHeader Whether to use a compact four-byte header ({@code true}) or a standard eight-byte header ({@code false}). 436 * @throws ClassFormatException Thrown if {@code compactCodeHeader} is {@code true} and the current values do not fit in a compact header. 437 * @since 6.14.0 438 */ 439 public void setCompactCodeHeader(final boolean compactCodeHeader) { 440 if (compactCodeHeader) { 441 Args.requireU1(maxStack, "maxStack"); 442 Args.requireU1(maxLocals, "maxLocals"); 443 Args.requireU2(code.length, "Code length attribute"); 444 } 445 this.compactCodeHeader = compactCodeHeader; 446 super.setLength(calculateLength()); 447 } 448 449 /** 450 * Sets the exception table. 451 * 452 * @param exceptionTable exception table. 453 */ 454 public void setExceptionTable(final CodeException[] exceptionTable) { 455 this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_ARRAY; 456 Args.requireU2(this.exceptionTable.length, "exceptionTable.length"); 457 super.setLength(calculateLength()); // Adjust length 458 } 459 460 /** 461 * Sets the maximum number of local variables. 462 * 463 * @param maxLocals maximum number of local variables. 464 */ 465 public void setMaxLocals(final int maxLocals) { 466 this.maxLocals = maxLocals; 467 } 468 469 /** 470 * Sets the maximum stack size. 471 * 472 * @param maxStack maximum stack size. 473 */ 474 public void setMaxStack(final int maxStack) { 475 this.maxStack = maxStack; 476 } 477 478 /** 479 * @return String representation of code chunk. 480 */ 481 @Override 482 public String toString() { 483 return toString(true); 484 } 485 486 /** 487 * Converts this object to a String. 488 * 489 * @param verbose Provides verbose output when true. 490 * @return String representation of code chunk. 491 */ 492 public String toString(final boolean verbose) { 493 final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber 494 buf.append("Code(maxStack = ").append(maxStack).append(", maxLocals = ").append(maxLocals).append(", code_length = ").append(code.length).append(")\n") 495 .append(Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose)); 496 if (exceptionTable.length > 0) { 497 buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n"); 498 for (final CodeException exception : exceptionTable) { 499 buf.append(exception.toString(super.getConstantPool(), verbose)).append("\n"); 500 } 501 } 502 if (attributes.length > 0) { 503 buf.append("\nAttribute(s) = "); 504 for (final Attribute attribute : attributes) { 505 buf.append("\n").append(attribute.getName()).append(":"); 506 buf.append("\n").append(attribute); 507 } 508 } 509 return buf.toString(); 510 } 511}