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