Code.java

  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. package org.apache.bcel.classfile;

  18. import java.io.DataInput;
  19. import java.io.DataOutputStream;
  20. import java.io.IOException;
  21. import java.util.Arrays;

  22. import org.apache.bcel.Const;
  23. import org.apache.bcel.util.Args;
  24. import org.apache.commons.lang3.ArrayUtils;

  25. /**
  26.  * This class represents a chunk of Java byte code contained in a method. It is instantiated by the
  27.  * <em>Attribute.readAttribute()</em> method. A <em>Code</em> attribute contains informations about operand stack, local
  28.  * variables, byte code and the exceptions handled within this method.
  29.  *
  30.  * This attribute has attributes itself, namely <em>LineNumberTable</em> which is used for debugging purposes and
  31.  * <em>LocalVariableTable</em> which contains information about the local variables.
  32.  *
  33.  * <pre>
  34.  * Code_attribute {
  35.  *   u2 attribute_name_index;
  36.  *   u4 attribute_length;
  37.  *   u2 max_stack;
  38.  *   u2 max_locals;
  39.  *   u4 code_length;
  40.  *   u1 code[code_length];
  41.  *   u2 exception_table_length;
  42.  *   {
  43.  *     u2 start_pc;
  44.  *     u2 end_pc;
  45.  *     u2 handler_pc;
  46.  *     u2 catch_type;
  47.  *   } exception_table[exception_table_length];
  48.  *   u2 attributes_count;
  49.  *   attribute_info attributes[attributes_count];
  50.  * }
  51.  * </pre>
  52.  * @see Attribute
  53.  * @see CodeException
  54.  * @see LineNumberTable
  55.  * @see LocalVariableTable
  56.  */
  57. public final class Code extends Attribute {

  58.     private int maxStack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used)
  59.     private int maxLocals; // Number of local variables // TODO this could be made final (setter is not used)
  60.     private byte[] code; // Actual byte code
  61.     private CodeException[] exceptionTable; // Table of handled exceptions
  62.     private Attribute[] attributes; // or LocalVariable

  63.     /**
  64.      * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a
  65.      * physical copy.
  66.      *
  67.      * @param code The source Code.
  68.      */
  69.     public Code(final Code code) {
  70.         this(code.getNameIndex(), code.getLength(), code.getMaxStack(), code.getMaxLocals(), code.getCode(), code.getExceptionTable(), code.getAttributes(),
  71.                 code.getConstantPool());
  72.     }

  73.     /**
  74.      * @param nameIndex Index pointing to the name <em>Code</em>
  75.      * @param length Content length in bytes
  76.      * @param file Input stream
  77.      * @param constantPool Array of constants
  78.      */
  79.     Code(final int nameIndex, final int length, final DataInput file, final ConstantPool constantPool) throws IOException {
  80.         // Initialize with some default values which will be overwritten later
  81.         this(nameIndex, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null, (CodeException[]) null, (Attribute[]) null, constantPool);
  82.         final int codeLength = Args.requireU4(file.readInt(), 1, "Code length attribute");
  83.         code = new byte[codeLength]; // Read byte code
  84.         file.readFully(code);
  85.         /*
  86.          * Read exception table that contains all regions where an exception handler is active, i.e., a try { ... } catch ()
  87.          * block.
  88.          */
  89.         final int exceptionTableLength = file.readUnsignedShort();
  90.         exceptionTable = new CodeException[exceptionTableLength];
  91.         for (int i = 0; i < exceptionTableLength; i++) {
  92.             exceptionTable[i] = new CodeException(file);
  93.         }
  94.         /*
  95.          * Read all attributes, currently 'LineNumberTable' and 'LocalVariableTable'
  96.          */
  97.         final int attributesCount = file.readUnsignedShort();
  98.         attributes = new Attribute[attributesCount];
  99.         for (int i = 0; i < attributesCount; i++) {
  100.             attributes[i] = readAttribute(file, constantPool);
  101.         }
  102.         /*
  103.          * Adjust length, because of setAttributes in this(), s.b. length is incorrect, because it didn't take the internal
  104.          * attributes into account yet! Very subtle bug, fixed in 3.1.1.
  105.          */
  106.         super.setLength(length);
  107.     }

  108.     /**
  109.      * @param nameIndex Index pointing to the name <em>Code</em>
  110.      * @param length Content length in bytes
  111.      * @param maxStack Maximum size of stack
  112.      * @param maxLocals Number of local variables
  113.      * @param code Actual byte code
  114.      * @param exceptionTable of handled exceptions
  115.      * @param attributes Attributes of code: LineNumber or LocalVariable
  116.      * @param constantPool Array of constants
  117.      */
  118.     public Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable,
  119.         final Attribute[] attributes, final ConstantPool constantPool) {
  120.         super(Const.ATTR_CODE, nameIndex, length, constantPool);
  121.         this.maxStack = Args.requireU2(maxStack, "maxStack");
  122.         this.maxLocals = Args.requireU2(maxLocals, "maxLocals");
  123.         this.code = ArrayUtils.nullToEmpty(code);
  124.         this.exceptionTable = ArrayUtils.nullToEmpty(exceptionTable, CodeException[].class);
  125.         Args.requireU2(this.exceptionTable.length, "exceptionTable.length");
  126.         this.attributes = attributes != null ? attributes : EMPTY_ARRAY;
  127.         super.setLength(calculateLength()); // Adjust length
  128.     }

  129.     /**
  130.      * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
  131.      * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
  132.      *
  133.      * @param v Visitor object
  134.      */
  135.     @Override
  136.     public void accept(final Visitor v) {
  137.         v.visitCode(this);
  138.     }

  139.     /**
  140.      * @return the full size of this code attribute, minus its first 6 bytes, including the size of all its contained
  141.      *         attributes
  142.      */
  143.     private int calculateLength() {
  144.         int len = 0;
  145.         if (attributes != null) {
  146.             for (final Attribute attribute : attributes) {
  147.                 len += attribute.getLength() + 6 /* attribute header size */;
  148.             }
  149.         }
  150.         return len + getInternalLength();
  151.     }

  152.     /**
  153.      * @return deep copy of this attribute
  154.      *
  155.      * @param constantPool the constant pool to duplicate
  156.      */
  157.     @Override
  158.     public Attribute copy(final ConstantPool constantPool) {
  159.         final Code c = (Code) clone();
  160.         if (code != null) {
  161.             c.code = code.clone();
  162.         }
  163.         c.setConstantPool(constantPool);
  164.         c.exceptionTable = new CodeException[exceptionTable.length];
  165.         Arrays.setAll(c.exceptionTable, i -> exceptionTable[i].copy());
  166.         c.attributes = new Attribute[attributes.length];
  167.         Arrays.setAll(c.attributes, i -> attributes[i].copy(constantPool));
  168.         return c;
  169.     }

  170.     /**
  171.      * Dump code attribute to file stream in binary format.
  172.      *
  173.      * @param file Output file stream
  174.      * @throws IOException if an I/O error occurs.
  175.      */
  176.     @Override
  177.     public void dump(final DataOutputStream file) throws IOException {
  178.         super.dump(file);
  179.         file.writeShort(maxStack);
  180.         file.writeShort(maxLocals);
  181.         file.writeInt(code.length);
  182.         file.write(code, 0, code.length);
  183.         file.writeShort(exceptionTable.length);
  184.         for (final CodeException exception : exceptionTable) {
  185.             exception.dump(file);
  186.         }
  187.         file.writeShort(attributes.length);
  188.         for (final Attribute attribute : attributes) {
  189.             attribute.dump(file);
  190.         }
  191.     }

  192.     /**
  193.      * @return Collection of code attributes.
  194.      * @see Attribute
  195.      */
  196.     public Attribute[] getAttributes() {
  197.         return attributes;
  198.     }

  199.     /**
  200.      * @return Actual byte code of the method.
  201.      */
  202.     public byte[] getCode() {
  203.         return code;
  204.     }

  205.     /**
  206.      * @return Table of handled exceptions.
  207.      * @see CodeException
  208.      */
  209.     public CodeException[] getExceptionTable() {
  210.         return exceptionTable;
  211.     }

  212.     /**
  213.      * @return the internal length of this code attribute (minus the first 6 bytes) and excluding all its attributes
  214.      */
  215.     private int getInternalLength() {
  216.         return 2 /* maxStack */ + 2 /* maxLocals */ + 4 /* code length */
  217.             + code.length /* byte-code */
  218.             + 2 /* exception-table length */
  219.             + 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */
  220.             + 2 /* attributes count */;
  221.     }

  222.     /**
  223.      * @return LineNumberTable of Code, if it has one
  224.      */
  225.     public LineNumberTable getLineNumberTable() {
  226.         for (final Attribute attribute : attributes) {
  227.             if (attribute instanceof LineNumberTable) {
  228.                 return (LineNumberTable) attribute;
  229.             }
  230.         }
  231.         return null;
  232.     }

  233.     /**
  234.      * @return LocalVariableTable of Code, if it has one
  235.      */
  236.     public LocalVariableTable getLocalVariableTable() {
  237.         for (final Attribute attribute : attributes) {
  238.             if (attribute instanceof LocalVariableTable) {
  239.                 return (LocalVariableTable) attribute;
  240.             }
  241.         }
  242.         return null;
  243.     }

  244.     /**
  245.      * Gets the local variable type table attribute {@link LocalVariableTypeTable}.
  246.      * @return LocalVariableTypeTable of Code, if it has one, null otherwise.
  247.      * @since 6.10.0
  248.      */
  249.     public LocalVariableTypeTable getLocalVariableTypeTable() {
  250.         for (final Attribute attribute : attributes) {
  251.             if (attribute instanceof LocalVariableTypeTable) {
  252.                 return (LocalVariableTypeTable) attribute;
  253.             }
  254.         }
  255.         return null;
  256.     }

  257.     /**
  258.      * @return Number of local variables.
  259.      */
  260.     public int getMaxLocals() {
  261.         return maxLocals;
  262.     }

  263.     /**
  264.      * @return Maximum size of stack used by this method.
  265.      */
  266.     public int getMaxStack() {
  267.         return maxStack;
  268.     }

  269.     /**
  270.      * Finds the attribute of {@link StackMap} instance.
  271.      * @return StackMap of Code, if it has one, else null.
  272.      * @since 6.8.0
  273.      */
  274.     public StackMap getStackMap() {
  275.         for (final Attribute attribute : attributes) {
  276.             if (attribute instanceof StackMap) {
  277.                 return (StackMap) attribute;
  278.             }
  279.         }
  280.         return null;
  281.     }

  282.     /**
  283.      * @param attributes the attributes to set for this Code
  284.      */
  285.     public void setAttributes(final Attribute[] attributes) {
  286.         this.attributes = attributes != null ? attributes : EMPTY_ARRAY;
  287.         super.setLength(calculateLength()); // Adjust length
  288.     }

  289.     /**
  290.      * @param code byte code
  291.      */
  292.     public void setCode(final byte[] code) {
  293.         this.code = ArrayUtils.nullToEmpty(code);
  294.         super.setLength(calculateLength()); // Adjust length
  295.     }

  296.     /**
  297.      * @param exceptionTable exception table
  298.      */
  299.     public void setExceptionTable(final CodeException[] exceptionTable) {
  300.         this.exceptionTable = exceptionTable != null ? exceptionTable : CodeException.EMPTY_ARRAY;
  301.         super.setLength(calculateLength()); // Adjust length
  302.     }

  303.     /**
  304.      * @param maxLocals maximum number of local variables
  305.      */
  306.     public void setMaxLocals(final int maxLocals) {
  307.         this.maxLocals = maxLocals;
  308.     }

  309.     /**
  310.      * @param maxStack maximum stack size
  311.      */
  312.     public void setMaxStack(final int maxStack) {
  313.         this.maxStack = maxStack;
  314.     }

  315.     /**
  316.      * @return String representation of code chunk.
  317.      */
  318.     @Override
  319.     public String toString() {
  320.         return toString(true);
  321.     }

  322.     /**
  323.      * Converts this object to a String.
  324.      *
  325.      * @param verbose Provides verbose output when true.
  326.      * @return String representation of code chunk.
  327.      */
  328.     public String toString(final boolean verbose) {
  329.         final StringBuilder buf = new StringBuilder(100); // CHECKSTYLE IGNORE MagicNumber
  330.         buf.append("Code(maxStack = ").append(maxStack).append(", maxLocals = ").append(maxLocals).append(", code_length = ").append(code.length).append(")\n")
  331.             .append(Utility.codeToString(code, super.getConstantPool(), 0, -1, verbose));
  332.         if (exceptionTable.length > 0) {
  333.             buf.append("\nException handler(s) = \n").append("From\tTo\tHandler\tType\n");
  334.             for (final CodeException exception : exceptionTable) {
  335.                 buf.append(exception.toString(super.getConstantPool(), verbose)).append("\n");
  336.             }
  337.         }
  338.         if (attributes.length > 0) {
  339.             buf.append("\nAttribute(s) = ");
  340.             for (final Attribute attribute : attributes) {
  341.                 buf.append("\n").append(attribute.getName()).append(":");
  342.                 buf.append("\n").append(attribute);
  343.             }
  344.         }
  345.         return buf.toString();
  346.     }
  347. }