1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 package org.apache.bcel.classfile; 19 20 import java.io.DataInput; 21 import java.io.DataOutputStream; 22 import java.io.IOException; 23 24 import org.apache.bcel.Const; 25 26 /** 27 * This class represents a chunk of Java byte code contained in a 28 * method. It is instantiated by the 29 * <em>Attribute.readAttribute()</em> method. A <em>Code</em> 30 * attribute contains informations about operand stack, local 31 * variables, byte code and the exceptions handled within this 32 * method. 33 * 34 * This attribute has attributes itself, namely <em>LineNumberTable</em> which 35 * is used for debugging purposes and <em>LocalVariableTable</em> which 36 * contains information about the local variables. 37 * 38 * @version $Id$ 39 * @see Attribute 40 * @see CodeException 41 * @see LineNumberTable 42 * @see LocalVariableTable 43 */ 44 public final class Code extends Attribute { 45 46 private int max_stack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used) 47 private int max_locals; // Number of local variables // TODO this could be made final (setter is not used) 48 private byte[] code; // Actual byte code 49 private CodeException[] exception_table; // Table of handled exceptions 50 private Attribute[] attributes; // or LocalVariable 51 52 53 /** 54 * Initialize from another object. Note that both objects use the same 55 * references (shallow copy). Use copy() for a physical copy. 56 */ Code(final Code c)57 public Code(final Code c) { 58 this(c.getNameIndex(), c.getLength(), c.getMaxStack(), c.getMaxLocals(), c.getCode(), c 59 .getExceptionTable(), c.getAttributes(), c.getConstantPool()); 60 } 61 62 63 /** 64 * @param name_index Index pointing to the name <em>Code</em> 65 * @param length Content length in bytes 66 * @param file Input stream 67 * @param constant_pool Array of constants 68 */ Code(final int name_index, final int length, final DataInput file, final ConstantPool constant_pool)69 Code(final int name_index, final int length, final DataInput file, final ConstantPool constant_pool) 70 throws IOException { 71 // Initialize with some default values which will be overwritten later 72 this(name_index, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null, 73 (CodeException[]) null, (Attribute[]) null, constant_pool); 74 final int code_length = file.readInt(); 75 code = new byte[code_length]; // Read byte code 76 file.readFully(code); 77 /* Read exception table that contains all regions where an exception 78 * handler is active, i.e., a try { ... } catch() block. 79 */ 80 final int exception_table_length = file.readUnsignedShort(); 81 exception_table = new CodeException[exception_table_length]; 82 for (int i = 0; i < exception_table_length; i++) { 83 exception_table[i] = new CodeException(file); 84 } 85 /* Read all attributes, currently `LineNumberTable' and 86 * `LocalVariableTable' 87 */ 88 final int attributes_count = file.readUnsignedShort(); 89 attributes = new Attribute[attributes_count]; 90 for (int i = 0; i < attributes_count; i++) { 91 attributes[i] = Attribute.readAttribute(file, constant_pool); 92 } 93 /* Adjust length, because of setAttributes in this(), s.b. length 94 * is incorrect, because it didn't take the internal attributes 95 * into account yet! Very subtle bug, fixed in 3.1.1. 96 */ 97 super.setLength(length); 98 } 99 100 101 /** 102 * @param name_index Index pointing to the name <em>Code</em> 103 * @param length Content length in bytes 104 * @param max_stack Maximum size of stack 105 * @param max_locals Number of local variables 106 * @param code Actual byte code 107 * @param exception_table Table of handled exceptions 108 * @param attributes Attributes of code: LineNumber or LocalVariable 109 * @param constant_pool Array of constants 110 */ Code(final int name_index, final int length, final int max_stack, final int max_locals, final byte[] code, final CodeException[] exception_table, final Attribute[] attributes, final ConstantPool constant_pool)111 public Code(final int name_index, final int length, final int max_stack, final int max_locals, final byte[] code, 112 final CodeException[] exception_table, final Attribute[] attributes, final ConstantPool constant_pool) { 113 super(Const.ATTR_CODE, name_index, length, constant_pool); 114 this.max_stack = max_stack; 115 this.max_locals = max_locals; 116 this.code = code != null ? code : new byte[0]; 117 this.exception_table = exception_table != null ? exception_table : new CodeException[0]; 118 this.attributes = attributes != null ? attributes : new Attribute[0]; 119 super.setLength(calculateLength()); // Adjust length 120 } 121 122 123 /** 124 * Called by objects that are traversing the nodes of the tree implicitely 125 * defined by the contents of a Java class. I.e., the hierarchy of methods, 126 * fields, attributes, etc. spawns a tree of objects. 127 * 128 * @param v Visitor object 129 */ 130 @Override accept( final Visitor v )131 public void accept( final Visitor v ) { 132 v.visitCode(this); 133 } 134 135 136 /** 137 * Dump code attribute to file stream in binary format. 138 * 139 * @param file Output file stream 140 * @throws IOException 141 */ 142 @Override dump( final DataOutputStream file )143 public final void dump( final DataOutputStream file ) throws IOException { 144 super.dump(file); 145 file.writeShort(max_stack); 146 file.writeShort(max_locals); 147 file.writeInt(code.length); 148 file.write(code, 0, code.length); 149 file.writeShort(exception_table.length); 150 for (final CodeException exception : exception_table) { 151 exception.dump(file); 152 } 153 file.writeShort(attributes.length); 154 for (final Attribute attribute : attributes) { 155 attribute.dump(file); 156 } 157 } 158 159 160 /** 161 * @return Collection of code attributes. 162 * @see Attribute 163 */ getAttributes()164 public final Attribute[] getAttributes() { 165 return attributes; 166 } 167 168 169 /** 170 * @return LineNumberTable of Code, if it has one 171 */ getLineNumberTable()172 public LineNumberTable getLineNumberTable() { 173 for (final Attribute attribute : attributes) { 174 if (attribute instanceof LineNumberTable) { 175 return (LineNumberTable) attribute; 176 } 177 } 178 return null; 179 } 180 181 182 /** 183 * @return LocalVariableTable of Code, if it has one 184 */ getLocalVariableTable()185 public LocalVariableTable getLocalVariableTable() { 186 for (final Attribute attribute : attributes) { 187 if (attribute instanceof LocalVariableTable) { 188 return (LocalVariableTable) attribute; 189 } 190 } 191 return null; 192 } 193 194 195 /** 196 * @return Actual byte code of the method. 197 */ getCode()198 public final byte[] getCode() { 199 return code; 200 } 201 202 203 /** 204 * @return Table of handled exceptions. 205 * @see CodeException 206 */ getExceptionTable()207 public final CodeException[] getExceptionTable() { 208 return exception_table; 209 } 210 211 212 /** 213 * @return Number of local variables. 214 */ getMaxLocals()215 public final int getMaxLocals() { 216 return max_locals; 217 } 218 219 220 /** 221 * @return Maximum size of stack used by this method. 222 */ getMaxStack()223 public final int getMaxStack() { 224 return max_stack; 225 } 226 227 228 /** 229 * @return the internal length of this code attribute (minus the first 6 bytes) 230 * and excluding all its attributes 231 */ getInternalLength()232 private int getInternalLength() { 233 return 2 /*max_stack*/+ 2 /*max_locals*/+ 4 /*code length*/ 234 + code.length /*byte-code*/ 235 + 2 /*exception-table length*/ 236 + 8 * (exception_table == null ? 0 : exception_table.length) /* exception table */ 237 + 2 /* attributes count */; 238 } 239 240 241 /** 242 * @return the full size of this code attribute, minus its first 6 bytes, 243 * including the size of all its contained attributes 244 */ calculateLength()245 private int calculateLength() { 246 int len = 0; 247 if (attributes != null) { 248 for (final Attribute attribute : attributes) { 249 len += attribute.getLength() + 6 /*attribute header size*/; 250 } 251 } 252 return len + getInternalLength(); 253 } 254 255 256 /** 257 * @param attributes the attributes to set for this Code 258 */ setAttributes( final Attribute[] attributes )259 public final void setAttributes( final Attribute[] attributes ) { 260 this.attributes = attributes != null ? attributes : new Attribute[0]; 261 super.setLength(calculateLength()); // Adjust length 262 } 263 264 265 /** 266 * @param code byte code 267 */ setCode( final byte[] code )268 public final void setCode( final byte[] code ) { 269 this.code = code != null ? code : new byte[0]; 270 super.setLength(calculateLength()); // Adjust length 271 } 272 273 274 /** 275 * @param exception_table exception table 276 */ setExceptionTable( final CodeException[] exception_table )277 public final void setExceptionTable( final CodeException[] exception_table ) { 278 this.exception_table = exception_table != null ? exception_table : new CodeException[0]; 279 super.setLength(calculateLength()); // Adjust length 280 } 281 282 283 /** 284 * @param max_locals maximum number of local variables 285 */ setMaxLocals( final int max_locals )286 public final void setMaxLocals( final int max_locals ) { 287 this.max_locals = max_locals; 288 } 289 290 291 /** 292 * @param max_stack maximum stack size 293 */ setMaxStack( final int max_stack )294 public final void setMaxStack( final int max_stack ) { 295 this.max_stack = max_stack; 296 } 297 298 299 /** 300 * @return String representation of code chunk. 301 */ toString( final boolean verbose )302 public final String toString( final boolean verbose ) { 303 final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber 304 buf.append("Code(max_stack = ").append(max_stack).append(", max_locals = ").append( 305 max_locals).append(", code_length = ").append(code.length).append(")\n").append( 306 Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose)); 307 if (exception_table.length > 0) { 308 buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n"); 309 for (final CodeException exception : exception_table) { 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); 317 } 318 } 319 return buf.toString(); 320 } 321 322 323 /** 324 * @return String representation of code chunk. 325 */ 326 @Override toString()327 public final String toString() { 328 return toString(true); 329 } 330 331 332 /** 333 * @return deep copy of this attribute 334 * 335 * @param _constant_pool the constant pool to duplicate 336 */ 337 @Override copy( final ConstantPool _constant_pool )338 public Attribute copy( final ConstantPool _constant_pool ) { 339 final Code c = (Code) clone(); 340 if (code != null) { 341 c.code = new byte[code.length]; 342 System.arraycopy(code, 0, c.code, 0, code.length); 343 } 344 c.setConstantPool(_constant_pool); 345 c.exception_table = new CodeException[exception_table.length]; 346 for (int i = 0; i < exception_table.length; i++) { 347 c.exception_table[i] = exception_table[i].copy(); 348 } 349 c.attributes = new Attribute[attributes.length]; 350 for (int i = 0; i < attributes.length; i++) { 351 c.attributes[i] = attributes[i].copy(_constant_pool); 352 } 353 return c; 354 } 355 } 356