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