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