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