StackMapEntry.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. /**
  24.  * This class represents a stack map entry recording the types of local variables and the of stack items at a given
  25.  * byte code offset. See CLDC specification 5.3.1.2.
  26.  *
  27.  * See also https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.4
  28.  *
  29.  * <pre>
  30.  * union stack_map_frame {
  31.  *   same_frame;
  32.  *   same_locals_1_stack_item_frame;
  33.  *   same_locals_1_stack_item_frame_extended;
  34.  *   chop_frame;
  35.  *   same_frame_extended;
  36.  *   append_frame;
  37.  *   full_frame;
  38.  * }
  39.  * </pre>
  40.  * @see StackMap
  41.  * @see StackMapType
  42.  */
  43. public final class StackMapEntry implements Node, Cloneable {

  44.     static final StackMapEntry[] EMPTY_ARRAY = {};

  45.     private int frameType;
  46.     private int byteCodeOffset;
  47.     private StackMapType[] typesOfLocals;
  48.     private StackMapType[] typesOfStackItems;
  49.     private ConstantPool constantPool;

  50.     /**
  51.      * Constructs object from input stream.
  52.      *
  53.      * @param dataInput Input stream
  54.      * @throws IOException if an I/O error occurs.
  55.      */
  56.     StackMapEntry(final DataInput dataInput, final ConstantPool constantPool) throws IOException {
  57.         this(dataInput.readByte() & 0xFF, -1, null, null, constantPool);

  58.         if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) {
  59.             byteCodeOffset = frameType - Const.SAME_FRAME;
  60.         } else if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
  61.             byteCodeOffset = frameType - Const.SAME_LOCALS_1_STACK_ITEM_FRAME;
  62.             typesOfStackItems = new StackMapType[] { new StackMapType(dataInput, constantPool) };
  63.         } else if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
  64.             byteCodeOffset = dataInput.readUnsignedShort();
  65.             typesOfStackItems = new StackMapType[] { new StackMapType(dataInput, constantPool) };
  66.         } else if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX || frameType == Const.SAME_FRAME_EXTENDED) {
  67.             byteCodeOffset = dataInput.readUnsignedShort();
  68.         } else if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) {
  69.             byteCodeOffset = dataInput.readUnsignedShort();
  70.             final int numberOfLocals = frameType - 251;
  71.             typesOfLocals = new StackMapType[numberOfLocals];
  72.             for (int i = 0; i < numberOfLocals; i++) {
  73.                 typesOfLocals[i] = new StackMapType(dataInput, constantPool);
  74.             }
  75.         } else if (frameType == Const.FULL_FRAME) {
  76.             byteCodeOffset = dataInput.readUnsignedShort();
  77.             final int numberOfLocals = dataInput.readUnsignedShort();
  78.             typesOfLocals = new StackMapType[numberOfLocals];
  79.             for (int i = 0; i < numberOfLocals; i++) {
  80.                 typesOfLocals[i] = new StackMapType(dataInput, constantPool);
  81.             }
  82.             final int numberOfStackItems = dataInput.readUnsignedShort();
  83.             typesOfStackItems = new StackMapType[numberOfStackItems];
  84.             for (int i = 0; i < numberOfStackItems; i++) {
  85.                 typesOfStackItems[i] = new StackMapType(dataInput, constantPool);
  86.             }
  87.         } else {
  88.             /* Can't happen */
  89.             throw new ClassFormatException("Invalid frame type found while parsing stack map table: " + frameType);
  90.         }
  91.     }

  92.     /**
  93.      * DO NOT USE
  94.      *
  95.      * @param byteCodeOffset
  96.      * @param numberOfLocals NOT USED
  97.      * @param typesOfLocals array of {@link StackMapType}s of locals
  98.      * @param numberOfStackItems NOT USED
  99.      * @param typesOfStackItems array ot {@link StackMapType}s of stack items
  100.      * @param constantPool the constant pool
  101.      * @deprecated Since 6.0, use {@link #StackMapEntry(int, int, StackMapType[], StackMapType[], ConstantPool)} instead
  102.      */
  103.     @java.lang.Deprecated
  104.     public StackMapEntry(final int byteCodeOffset, final int numberOfLocals, final StackMapType[] typesOfLocals, final int numberOfStackItems,
  105.         final StackMapType[] typesOfStackItems, final ConstantPool constantPool) {
  106.         this.byteCodeOffset = byteCodeOffset;
  107.         this.typesOfLocals = typesOfLocals != null ? typesOfLocals : StackMapType.EMPTY_ARRAY;
  108.         this.typesOfStackItems = typesOfStackItems != null ? typesOfStackItems : StackMapType.EMPTY_ARRAY;
  109.         this.constantPool = constantPool;
  110.         if (numberOfLocals < 0) {
  111.             throw new IllegalArgumentException("numberOfLocals < 0");
  112.         }
  113.         if (numberOfStackItems < 0) {
  114.             throw new IllegalArgumentException("numberOfStackItems < 0");
  115.         }
  116.     }

  117.     /**
  118.      * Create an instance
  119.      *
  120.      * @param tag the frameType to use
  121.      * @param byteCodeOffset
  122.      * @param typesOfLocals array of {@link StackMapType}s of locals
  123.      * @param typesOfStackItems array ot {@link StackMapType}s of stack items
  124.      * @param constantPool the constant pool
  125.      */
  126.     public StackMapEntry(final int tag, final int byteCodeOffset, final StackMapType[] typesOfLocals, final StackMapType[] typesOfStackItems,
  127.         final ConstantPool constantPool) {
  128.         this.frameType = tag;
  129.         this.byteCodeOffset = byteCodeOffset;
  130.         this.typesOfLocals = typesOfLocals != null ? typesOfLocals : StackMapType.EMPTY_ARRAY;
  131.         this.typesOfStackItems = typesOfStackItems != null ? typesOfStackItems : StackMapType.EMPTY_ARRAY;
  132.         this.constantPool = constantPool;
  133.     }

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

  144.     /**
  145.      * @return deep copy of this object
  146.      */
  147.     public StackMapEntry copy() {
  148.         StackMapEntry e;
  149.         try {
  150.             e = (StackMapEntry) clone();
  151.         } catch (final CloneNotSupportedException ex) {
  152.             throw new UnsupportedOperationException("Clone Not Supported", ex);
  153.         }

  154.         e.typesOfLocals = new StackMapType[typesOfLocals.length];
  155.         Arrays.setAll(e.typesOfLocals, i -> typesOfLocals[i].copy());
  156.         e.typesOfStackItems = new StackMapType[typesOfStackItems.length];
  157.         Arrays.setAll(e.typesOfStackItems, i -> typesOfStackItems[i].copy());
  158.         return e;
  159.     }

  160.     /**
  161.      * Dump stack map entry
  162.      *
  163.      * @param file Output file stream
  164.      * @throws IOException if an I/O error occurs.
  165.      */
  166.     public void dump(final DataOutputStream file) throws IOException {
  167.         file.write(frameType);
  168.         if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
  169.             typesOfStackItems[0].dump(file);
  170.         } else if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
  171.             file.writeShort(byteCodeOffset);
  172.             typesOfStackItems[0].dump(file);
  173.         } else if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX || frameType == Const.SAME_FRAME_EXTENDED) {
  174.             file.writeShort(byteCodeOffset);
  175.         } else if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) {
  176.             file.writeShort(byteCodeOffset);
  177.             for (final StackMapType type : typesOfLocals) {
  178.                 type.dump(file);
  179.             }
  180.         } else if (frameType == Const.FULL_FRAME) {
  181.             file.writeShort(byteCodeOffset);
  182.             file.writeShort(typesOfLocals.length);
  183.             for (final StackMapType type : typesOfLocals) {
  184.                 type.dump(file);
  185.             }
  186.             file.writeShort(typesOfStackItems.length);
  187.             for (final StackMapType type : typesOfStackItems) {
  188.                 type.dump(file);
  189.             }
  190.         } else if (!(frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX)) {
  191.             /* Can't happen */
  192.             throw new ClassFormatException("Invalid Stack map table tag: " + frameType);
  193.         }
  194.     }

  195.     public int getByteCodeOffset() {
  196.         return byteCodeOffset;
  197.     }

  198.     /**
  199.      * @return Constant pool used by this object.
  200.      */
  201.     public ConstantPool getConstantPool() {
  202.         return constantPool;
  203.     }

  204.     public int getFrameType() {
  205.         return frameType;
  206.     }

  207.     /**
  208.      * Calculate stack map entry size
  209.      */
  210.     int getMapEntrySize() {
  211.         if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) {
  212.             return 1;
  213.         }
  214.         if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
  215.             return 1 + (typesOfStackItems[0].hasIndex() ? 3 : 1);
  216.         }
  217.         if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
  218.             return 3 + (typesOfStackItems[0].hasIndex() ? 3 : 1);
  219.         }
  220.         if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX || frameType == Const.SAME_FRAME_EXTENDED) {
  221.             return 3;
  222.         }
  223.         if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) {
  224.             int len = 3;
  225.             for (final StackMapType typesOfLocal : typesOfLocals) {
  226.                 len += typesOfLocal.hasIndex() ? 3 : 1;
  227.             }
  228.             return len;
  229.         }
  230.         if (frameType != Const.FULL_FRAME) {
  231.             throw new IllegalStateException("Invalid StackMap frameType: " + frameType);
  232.         }
  233.         int len = 7;
  234.         for (final StackMapType typesOfLocal : typesOfLocals) {
  235.             len += typesOfLocal.hasIndex() ? 3 : 1;
  236.         }
  237.         for (final StackMapType typesOfStackItem : typesOfStackItems) {
  238.             len += typesOfStackItem.hasIndex() ? 3 : 1;
  239.         }
  240.         return len;
  241.     }

  242.     public int getNumberOfLocals() {
  243.         return typesOfLocals.length;
  244.     }

  245.     public int getNumberOfStackItems() {
  246.         return typesOfStackItems.length;
  247.     }

  248.     public StackMapType[] getTypesOfLocals() {
  249.         return typesOfLocals;
  250.     }

  251.     public StackMapType[] getTypesOfStackItems() {
  252.         return typesOfStackItems;
  253.     }

  254.     private boolean invalidFrameType(final int f) {
  255.         // @formatter:off
  256.         return f != Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED
  257.             && !(f >= Const.CHOP_FRAME && f <= Const.CHOP_FRAME_MAX)
  258.             && f != Const.SAME_FRAME_EXTENDED
  259.             && !(f >= Const.APPEND_FRAME && f <= Const.APPEND_FRAME_MAX)
  260.             && f != Const.FULL_FRAME;
  261.         // @formatter:on
  262.     }

  263.     public void setByteCodeOffset(final int newOffset) {
  264.         if (newOffset < 0 || newOffset > 32767) {
  265.             throw new IllegalArgumentException("Invalid StackMap offset: " + newOffset);
  266.         }

  267.         if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) {
  268.             if (newOffset > Const.SAME_FRAME_MAX) {
  269.                 frameType = Const.SAME_FRAME_EXTENDED;
  270.             } else {
  271.                 frameType = newOffset;
  272.             }
  273.         } else if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
  274.             if (newOffset > Const.SAME_FRAME_MAX) {
  275.                 frameType = Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
  276.             } else {
  277.                 frameType = Const.SAME_LOCALS_1_STACK_ITEM_FRAME + newOffset;
  278.             }
  279.         } else if (invalidFrameType(frameType)) {
  280.             throw new IllegalStateException("Invalid StackMap frameType: " + frameType);
  281.         }
  282.         byteCodeOffset = newOffset;
  283.     }

  284.     /**
  285.      * @param constantPool Constant pool to be used for this object.
  286.      */
  287.     public void setConstantPool(final ConstantPool constantPool) {
  288.         this.constantPool = constantPool;
  289.     }

  290.     public void setFrameType(final int ft) {
  291.         if (ft >= Const.SAME_FRAME && ft <= Const.SAME_FRAME_MAX) {
  292.             byteCodeOffset = ft - Const.SAME_FRAME;
  293.         } else if (ft >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && ft <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
  294.             byteCodeOffset = ft - Const.SAME_LOCALS_1_STACK_ITEM_FRAME;
  295.         } else if (invalidFrameType(ft)) {
  296.             throw new IllegalArgumentException("Invalid StackMap frameType");
  297.         }
  298.         frameType = ft;
  299.     }

  300.     /**
  301.      *
  302.      * @deprecated since 6.0
  303.      */
  304.     @java.lang.Deprecated
  305.     public void setNumberOfLocals(final int n) { // TODO unused
  306.     }

  307.     /**
  308.      *
  309.      * @deprecated since 6.0
  310.      */
  311.     @java.lang.Deprecated
  312.     public void setNumberOfStackItems(final int n) { // TODO unused
  313.     }

  314.     public void setTypesOfLocals(final StackMapType[] types) {
  315.         typesOfLocals = types != null ? types : StackMapType.EMPTY_ARRAY;
  316.     }

  317.     public void setTypesOfStackItems(final StackMapType[] types) {
  318.         typesOfStackItems = types != null ? types : StackMapType.EMPTY_ARRAY;
  319.     }

  320.     /**
  321.      * @return String representation.
  322.      */
  323.     @Override
  324.     public String toString() {
  325.         final StringBuilder buf = new StringBuilder(64);
  326.         buf.append("(");
  327.         if (frameType >= Const.SAME_FRAME && frameType <= Const.SAME_FRAME_MAX) {
  328.             buf.append("SAME");
  329.         } else if (frameType >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && frameType <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
  330.             buf.append("SAME_LOCALS_1_STACK");
  331.         } else if (frameType == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
  332.             buf.append("SAME_LOCALS_1_STACK_EXTENDED");
  333.         } else if (frameType >= Const.CHOP_FRAME && frameType <= Const.CHOP_FRAME_MAX) {
  334.             buf.append("CHOP ").append(String.valueOf(251 - frameType));
  335.         } else if (frameType == Const.SAME_FRAME_EXTENDED) {
  336.             buf.append("SAME_EXTENDED");
  337.         } else if (frameType >= Const.APPEND_FRAME && frameType <= Const.APPEND_FRAME_MAX) {
  338.             buf.append("APPEND ").append(String.valueOf(frameType - 251));
  339.         } else if (frameType == Const.FULL_FRAME) {
  340.             buf.append("FULL");
  341.         } else {
  342.             buf.append("UNKNOWN (").append(frameType).append(")");
  343.         }
  344.         buf.append(", offset delta=").append(byteCodeOffset);
  345.         if (typesOfLocals.length > 0) {
  346.             buf.append(", locals={");
  347.             for (int i = 0; i < typesOfLocals.length; i++) {
  348.                 buf.append(typesOfLocals[i]);
  349.                 if (i < typesOfLocals.length - 1) {
  350.                     buf.append(", ");
  351.                 }
  352.             }
  353.             buf.append("}");
  354.         }
  355.         if (typesOfStackItems.length > 0) {
  356.             buf.append(", stack items={");
  357.             for (int i = 0; i < typesOfStackItems.length; i++) {
  358.                 buf.append(typesOfStackItems[i]);
  359.                 if (i < typesOfStackItems.length - 1) {
  360.                     buf.append(", ");
  361.                 }
  362.             }
  363.             buf.append("}");
  364.         }
  365.         buf.append(")");
  366.         return buf.toString();
  367.     }

  368.     /**
  369.      * Update the distance (as an offset delta) from this StackMap entry to the next. Note that this might cause the
  370.      * frame type to change. Note also that delta may be negative.
  371.      *
  372.      * @param delta offset delta
  373.      */
  374.     public void updateByteCodeOffset(final int delta) {
  375.         setByteCodeOffset(byteCodeOffset + delta);
  376.     }
  377. }