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.commons.lang3.ArrayUtils; 026 027/** 028 * This class represents a chunk of Java byte code contained in a method. It is instantiated by the 029 * <em>Attribute.readAttribute()</em> method. A <em>Code</em> attribute contains informations about operand stack, local 030 * variables, byte code and the exceptions handled within this method. 031 * 032 * This attribute has attributes itself, namely <em>LineNumberTable</em> which is used for debugging purposes and 033 * <em>LocalVariableTable</em> which contains information about the local variables. 034 * 035 * @see Attribute 036 * @see CodeException 037 * @see LineNumberTable 038 * @see LocalVariableTable 039 */ 040public final class Code extends Attribute { 041 042 private int maxStack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used) 043 private int maxLocals; // Number of local variables // TODO this could be made final (setter is not used) 044 private byte[] code; // Actual byte code 045 private CodeException[] exceptionTable; // Table of handled exceptions 046 private Attribute[] attributes; // or LocalVariable 047 048 /** 049 * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a 050 * physical copy. 051 */ 052 public Code(final Code c) { 053 this(c.getNameIndex(), c.getLength(), c.getMaxStack(), c.getMaxLocals(), c.getCode(), c.getExceptionTable(), c.getAttributes(), c.getConstantPool()); 054 } 055 056 /** 057 * @param nameIndex Index pointing to the name <em>Code</em> 058 * @param length Content length in bytes 059 * @param file Input stream 060 * @param constantPool Array of constants 061 */ 062 Code(final int nameIndex, final int length, final DataInput file, final ConstantPool constantPool) throws IOException { 063 // Initialize with some default values which will be overwritten later 064 this(nameIndex, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null, (CodeException[]) null, (Attribute[]) null, constantPool); 065 final int codeLength = file.readInt(); 066 code = new byte[codeLength]; // Read byte code 067 file.readFully(code); 068 /* 069 * Read exception table that contains all regions where an exception handler is active, i.e., a try { ... } catch() 070 * block. 071 */ 072 final int exceptionTableLength = file.readUnsignedShort(); 073 exceptionTable = new CodeException[exceptionTableLength]; 074 for (int i = 0; i < exceptionTableLength; i++) { 075 exceptionTable[i] = new CodeException(file); 076 } 077 /* 078 * Read all attributes, currently `LineNumberTable' and `LocalVariableTable' 079 */ 080 final int attributesCount = file.readUnsignedShort(); 081 attributes = new Attribute[attributesCount]; 082 for (int i = 0; i < attributesCount; i++) { 083 attributes[i] = Attribute.readAttribute(file, constantPool); 084 } 085 /* 086 * Adjust length, because of setAttributes in this(), s.b. length is incorrect, because it didn't take the internal 087 * attributes into account yet! Very subtle bug, fixed in 3.1.1. 088 */ 089 super.setLength(length); 090 } 091 092 /** 093 * @param nameIndex Index pointing to the name <em>Code</em> 094 * @param length Content length in bytes 095 * @param maxStack Maximum size of stack 096 * @param maxLocals Number of local variables 097 * @param code Actual byte code 098 * @param exceptionTable of handled exceptions 099 * @param attributes Attributes of code: LineNumber or LocalVariable 100 * @param constantPool Array of constants 101 */ 102 public Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable, 103 final Attribute[] attributes, final ConstantPool constantPool) { 104 super(Const.ATTR_CODE, nameIndex, length, constantPool); 105 this.maxStack = maxStack; 106 this.maxLocals = maxLocals; 107 this.code = code != null ? code : ArrayUtils.EMPTY_BYTE_ARRAY; 108 this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_CODE_EXCEPTION_ARRAY; 109 this.attributes = attributes != null ? attributes : EMPTY_ARRAY; 110 super.setLength(calculateLength()); // Adjust length 111 } 112 113 /** 114 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. 115 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. 116 * 117 * @param v Visitor object 118 */ 119 @Override 120 public void accept(final Visitor v) { 121 v.visitCode(this); 122 } 123 124 /** 125 * @return the full size of this code attribute, minus its first 6 bytes, including the size of all its contained 126 * attributes 127 */ 128 private int calculateLength() { 129 int len = 0; 130 if (attributes != null) { 131 for (final Attribute attribute : attributes) { 132 len += attribute.getLength() + 6 /* attribute header size */; 133 } 134 } 135 return len + getInternalLength(); 136 } 137 138 /** 139 * @return deep copy of this attribute 140 * 141 * @param constantPool the constant pool to duplicate 142 */ 143 @Override 144 public Attribute copy(final ConstantPool constantPool) { 145 final Code c = (Code) clone(); 146 if (code != null) { 147 c.code = code.clone(); 148 } 149 c.setConstantPool(constantPool); 150 c.exceptionTable = new CodeException[exceptionTable.length]; 151 Arrays.setAll(c.exceptionTable, i -> exceptionTable[i].copy()); 152 c.attributes = new Attribute[attributes.length]; 153 Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool)); 154 return c; 155 } 156 157 /** 158 * Dump code attribute to file stream in binary format. 159 * 160 * @param file Output file stream 161 * @throws IOException if an I/O error occurs. 162 */ 163 @Override 164 public void dump(final DataOutputStream file) throws IOException { 165 super.dump(file); 166 file.writeShort(maxStack); 167 file.writeShort(maxLocals); 168 file.writeInt(code.length); 169 file.write(code, 0, code.length); 170 file.writeShort(exceptionTable.length); 171 for (final CodeException exception : exceptionTable) { 172 exception.dump(file); 173 } 174 file.writeShort(attributes.length); 175 for (final Attribute attribute : attributes) { 176 attribute.dump(file); 177 } 178 } 179 180 /** 181 * @return Collection of code attributes. 182 * @see Attribute 183 */ 184 public Attribute[] getAttributes() { 185 return attributes; 186 } 187 188 /** 189 * @return Actual byte code of the method. 190 */ 191 public byte[] getCode() { 192 return code; 193 } 194 195 /** 196 * @return Table of handled exceptions. 197 * @see CodeException 198 */ 199 public CodeException[] getExceptionTable() { 200 return exceptionTable; 201 } 202 203 /** 204 * @return the internal length of this code attribute (minus the first 6 bytes) and excluding all its attributes 205 */ 206 private int getInternalLength() { 207 return 2 /* maxStack */ + 2 /* maxLocals */ + 4 /* code length */ 208 + code.length /* byte-code */ 209 + 2 /* exception-table length */ 210 + 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */ 211 + 2 /* attributes count */; 212 } 213 214 /** 215 * @return LineNumberTable of Code, if it has one 216 */ 217 public LineNumberTable getLineNumberTable() { 218 for (final Attribute attribute : attributes) { 219 if (attribute instanceof LineNumberTable) { 220 return (LineNumberTable) attribute; 221 } 222 } 223 return null; 224 } 225 226 /** 227 * @return LocalVariableTable of Code, if it has one 228 */ 229 public LocalVariableTable getLocalVariableTable() { 230 for (final Attribute attribute : attributes) { 231 if (attribute instanceof LocalVariableTable) { 232 return (LocalVariableTable) attribute; 233 } 234 } 235 return null; 236 } 237 238 /** 239 * @return Number of local variables. 240 */ 241 public int getMaxLocals() { 242 return maxLocals; 243 } 244 245 /** 246 * @return Maximum size of stack used by this method. 247 */ 248 public int getMaxStack() { 249 return maxStack; 250 } 251 252 /** 253 * @param attributes the attributes to set for this Code 254 */ 255 public void setAttributes(final Attribute[] attributes) { 256 this.attributes = attributes != null ? attributes : EMPTY_ARRAY; 257 super.setLength(calculateLength()); // Adjust length 258 } 259 260 /** 261 * @param code byte code 262 */ 263 public void setCode(final byte[] code) { 264 this.code = code != null ? code : ArrayUtils.EMPTY_BYTE_ARRAY; 265 super.setLength(calculateLength()); // Adjust length 266 } 267 268 /** 269 * @param exceptionTable exception table 270 */ 271 public void setExceptionTable(final CodeException[] exceptionTable) { 272 this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_CODE_EXCEPTION_ARRAY; 273 super.setLength(calculateLength()); // Adjust length 274 } 275 276 /** 277 * @param maxLocals maximum number of local variables 278 */ 279 public void setMaxLocals(final int maxLocals) { 280 this.maxLocals = maxLocals; 281 } 282 283 /** 284 * @param maxStack maximum stack size 285 */ 286 public void setMaxStack(final int maxStack) { 287 this.maxStack = maxStack; 288 } 289 290 /** 291 * @return String representation of code chunk. 292 */ 293 @Override 294 public String toString() { 295 return toString(true); 296 } 297 298 /** 299 * @return String representation of code chunk. 300 */ 301 public String toString(final boolean verbose) { 302 final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber 303 buf.append("Code(maxStack = ").append(maxStack).append(", maxLocals = ").append(maxLocals).append(", code_length = ").append(code.length).append(")\n") 304 .append(Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose)); 305 if (exceptionTable.length > 0) { 306 buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n"); 307 for (final CodeException exception : exceptionTable) { 308 buf.append(exception.toString(super.getConstantPool(), verbose)).append("\n"); 309 } 310 } 311 if (attributes.length > 0) { 312 buf.append("\nAttribute(s) = "); 313 for (final Attribute attribute : attributes) { 314 buf.append("\n").append(attribute.getName()).append(":"); 315 buf.append("\n").append(attribute); 316 } 317 } 318 return buf.toString(); 319 } 320}