001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.bcel.classfile; 018 019import java.io.DataInput; 020import java.io.DataOutputStream; 021import java.io.IOException; 022import java.util.Arrays; 023 024import org.apache.bcel.Const; 025import org.apache.bcel.util.Args; 026import org.apache.commons.lang3.ArrayUtils; 027 028/** 029 * This class represents a chunk of Java byte code contained in a method. It is instantiated by the 030 * <em>Attribute.readAttribute()</em> method. A <em>Code</em> attribute contains informations about operand stack, local 031 * variables, byte code and the exceptions handled within this method. 032 * 033 * This attribute has attributes itself, namely <em>LineNumberTable</em> which is used for debugging purposes and 034 * <em>LocalVariableTable</em> which contains information about the local variables. 035 * 036 * <pre> 037 * Code_attribute { 038 * u2 attribute_name_index; 039 * u4 attribute_length; 040 * u2 max_stack; 041 * u2 max_locals; 042 * u4 code_length; 043 * u1 code[code_length]; 044 * u2 exception_table_length; 045 * { 046 * u2 start_pc; 047 * u2 end_pc; 048 * u2 handler_pc; 049 * u2 catch_type; 050 * } exception_table[exception_table_length]; 051 * u2 attributes_count; 052 * attribute_info attributes[attributes_count]; 053 * } 054 * </pre> 055 * @see Attribute 056 * @see CodeException 057 * @see LineNumberTable 058 * @see LocalVariableTable 059 */ 060public final class Code extends Attribute { 061 062 private int maxStack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used) 063 private int maxLocals; // Number of local variables // TODO this could be made final (setter is not used) 064 private byte[] code; // Actual byte code 065 private CodeException[] exceptionTable; // Table of handled exceptions 066 private Attribute[] attributes; // or LocalVariable 067 068 /** 069 * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a 070 * physical copy. 071 * 072 * @param code The source Code. 073 */ 074 public Code(final Code code) { 075 this(code.getNameIndex(), code.getLength(), code.getMaxStack(), code.getMaxLocals(), code.getCode(), code.getExceptionTable(), code.getAttributes(), 076 code.getConstantPool()); 077 } 078 079 /** 080 * @param nameIndex Index pointing to the name <em>Code</em> 081 * @param length Content length in bytes 082 * @param file Input stream 083 * @param constantPool Array of constants 084 */ 085 Code(final int nameIndex, final int length, final DataInput file, final ConstantPool constantPool) throws IOException { 086 // Initialize with some default values which will be overwritten later 087 this(nameIndex, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null, (CodeException[]) null, (Attribute[]) null, constantPool); 088 final int codeLength = Args.requireU4(file.readInt(), 1, "Code length attribute"); 089 code = new byte[codeLength]; // Read byte code 090 file.readFully(code); 091 /* 092 * Read exception table that contains all regions where an exception handler is active, i.e., a try { ... } catch () 093 * block. 094 */ 095 final int exceptionTableLength = file.readUnsignedShort(); 096 exceptionTable = new CodeException[exceptionTableLength]; 097 for (int i = 0; i < exceptionTableLength; i++) { 098 exceptionTable[i] = new CodeException(file); 099 } 100 /* 101 * Read all attributes, currently 'LineNumberTable' and 'LocalVariableTable' 102 */ 103 final int attributesCount = file.readUnsignedShort(); 104 attributes = new Attribute[attributesCount]; 105 for (int i = 0; i < attributesCount; i++) { 106 attributes[i] = readAttribute(file, constantPool); 107 } 108 /* 109 * Adjust length, because of setAttributes in this(), s.b. length is incorrect, because it didn't take the internal 110 * attributes into account yet! Very subtle bug, fixed in 3.1.1. 111 */ 112 super.setLength(length); 113 } 114 115 /** 116 * @param nameIndex Index pointing to the name <em>Code</em> 117 * @param length Content length in bytes 118 * @param maxStack Maximum size of stack 119 * @param maxLocals Number of local variables 120 * @param code Actual byte code 121 * @param exceptionTable of handled exceptions 122 * @param attributes Attributes of code: LineNumber or LocalVariable 123 * @param constantPool Array of constants 124 */ 125 public Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable, 126 final Attribute[] attributes, final ConstantPool constantPool) { 127 super(Const.ATTR_CODE, nameIndex, length, constantPool); 128 this.maxStack = Args.requireU2(maxStack, "maxStack"); 129 this.maxLocals = Args.requireU2(maxLocals, "maxLocals"); 130 this.code = ArrayUtils.nullToEmpty(code); 131 this.exceptionTable = ArrayUtils.nullToEmpty(exceptionTable, CodeException[].class); 132 Args.requireU2(this.exceptionTable.length, "exceptionTable.length"); 133 this.attributes = attributes != null ? attributes : EMPTY_ARRAY; 134 super.setLength(calculateLength()); // Adjust length 135 } 136 137 /** 138 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. 139 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. 140 * 141 * @param v Visitor object 142 */ 143 @Override 144 public void accept(final Visitor v) { 145 v.visitCode(this); 146 } 147 148 /** 149 * @return the full size of this code attribute, minus its first 6 bytes, including the size of all its contained 150 * attributes 151 */ 152 private int calculateLength() { 153 int len = 0; 154 if (attributes != null) { 155 for (final Attribute attribute : attributes) { 156 len += attribute.getLength() + 6 /* attribute header size */; 157 } 158 } 159 return len + getInternalLength(); 160 } 161 162 /** 163 * @return deep copy of this attribute 164 * 165 * @param constantPool the constant pool to duplicate 166 */ 167 @Override 168 public Attribute copy(final ConstantPool constantPool) { 169 final Code c = (Code) clone(); 170 if (code != null) { 171 c.code = code.clone(); 172 } 173 c.setConstantPool(constantPool); 174 c.exceptionTable = new CodeException[exceptionTable.length]; 175 Arrays.setAll(c.exceptionTable, i -> exceptionTable[i].copy()); 176 c.attributes = new Attribute[attributes.length]; 177 Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool)); 178 return c; 179 } 180 181 /** 182 * Dump code attribute to file stream in binary format. 183 * 184 * @param file Output file stream 185 * @throws IOException if an I/O error occurs. 186 */ 187 @Override 188 public void dump(final DataOutputStream file) throws IOException { 189 super.dump(file); 190 file.writeShort(maxStack); 191 file.writeShort(maxLocals); 192 file.writeInt(code.length); 193 file.write(code, 0, code.length); 194 file.writeShort(exceptionTable.length); 195 for (final CodeException exception : exceptionTable) { 196 exception.dump(file); 197 } 198 file.writeShort(attributes.length); 199 for (final Attribute attribute : attributes) { 200 attribute.dump(file); 201 } 202 } 203 204 /** 205 * @return Collection of code attributes. 206 * @see Attribute 207 */ 208 public Attribute[] getAttributes() { 209 return attributes; 210 } 211 212 /** 213 * @return Actual byte code of the method. 214 */ 215 public byte[] getCode() { 216 return code; 217 } 218 219 /** 220 * @return Table of handled exceptions. 221 * @see CodeException 222 */ 223 public CodeException[] getExceptionTable() { 224 return exceptionTable; 225 } 226 227 /** 228 * @return the internal length of this code attribute (minus the first 6 bytes) and excluding all its attributes 229 */ 230 private int getInternalLength() { 231 return 2 /* maxStack */ + 2 /* maxLocals */ + 4 /* code length */ 232 + code.length /* byte-code */ 233 + 2 /* exception-table length */ 234 + 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */ 235 + 2 /* attributes count */; 236 } 237 238 /** 239 * @return LineNumberTable of Code, if it has one 240 */ 241 public LineNumberTable getLineNumberTable() { 242 for (final Attribute attribute : attributes) { 243 if (attribute instanceof LineNumberTable) { 244 return (LineNumberTable) attribute; 245 } 246 } 247 return null; 248 } 249 250 /** 251 * @return LocalVariableTable of Code, if it has one 252 */ 253 public LocalVariableTable getLocalVariableTable() { 254 for (final Attribute attribute : attributes) { 255 if (attribute instanceof LocalVariableTable) { 256 return (LocalVariableTable) attribute; 257 } 258 } 259 return null; 260 } 261 262 /** 263 * Gets the local variable type table attribute {@link LocalVariableTypeTable}. 264 * @return LocalVariableTypeTable of Code, if it has one, null otherwise. 265 * @since 6.10.0 266 */ 267 public LocalVariableTypeTable getLocalVariableTypeTable() { 268 for (final Attribute attribute : attributes) { 269 if (attribute instanceof LocalVariableTypeTable) { 270 return (LocalVariableTypeTable) attribute; 271 } 272 } 273 return null; 274 } 275 276 /** 277 * @return Number of local variables. 278 */ 279 public int getMaxLocals() { 280 return maxLocals; 281 } 282 283 /** 284 * @return Maximum size of stack used by this method. 285 */ 286 public int getMaxStack() { 287 return maxStack; 288 } 289 290 /** 291 * Finds the attribute of {@link StackMap} instance. 292 * @return StackMap of Code, if it has one, else null. 293 * @since 6.8.0 294 */ 295 public StackMap getStackMap() { 296 for (final Attribute attribute : attributes) { 297 if (attribute instanceof StackMap) { 298 return (StackMap) attribute; 299 } 300 } 301 return null; 302 } 303 304 /** 305 * @param attributes the attributes to set for this Code 306 */ 307 public void setAttributes(final Attribute[] attributes) { 308 this.attributes = attributes != null ? attributes : EMPTY_ARRAY; 309 super.setLength(calculateLength()); // Adjust length 310 } 311 312 /** 313 * @param code byte code 314 */ 315 public void setCode(final byte[] code) { 316 this.code = ArrayUtils.nullToEmpty(code); 317 super.setLength(calculateLength()); // Adjust length 318 } 319 320 /** 321 * @param exceptionTable exception table 322 */ 323 public void setExceptionTable(final CodeException[] exceptionTable) { 324 this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_ARRAY; 325 super.setLength(calculateLength()); // Adjust length 326 } 327 328 /** 329 * @param maxLocals maximum number of local variables 330 */ 331 public void setMaxLocals(final int maxLocals) { 332 this.maxLocals = maxLocals; 333 } 334 335 /** 336 * @param maxStack maximum stack size 337 */ 338 public void setMaxStack(final int maxStack) { 339 this.maxStack = maxStack; 340 } 341 342 /** 343 * @return String representation of code chunk. 344 */ 345 @Override 346 public String toString() { 347 return toString(true); 348 } 349 350 /** 351 * Converts this object to a String. 352 * 353 * @param verbose Provides verbose output when true. 354 * @return String representation of code chunk. 355 */ 356 public String toString(final boolean verbose) { 357 final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber 358 buf.append("Code(maxStack = ").append(maxStack).append(", maxLocals = ").append(maxLocals).append(", code_length = ").append(code.length).append(")\n") 359 .append(Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose)); 360 if (exceptionTable.length > 0) { 361 buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n"); 362 for (final CodeException exception : exceptionTable) { 363 buf.append(exception.toString(super.getConstantPool(), verbose)).append("\n"); 364 } 365 } 366 if (attributes.length > 0) { 367 buf.append("\nAttribute(s) = "); 368 for (final Attribute attribute : attributes) { 369 buf.append("\n").append(attribute.getName()).append(":"); 370 buf.append("\n").append(attribute); 371 } 372 } 373 return buf.toString(); 374 } 375}