Class StackMapUtils

  • Direct Known Subclasses:
    InstructionListUtils

    public abstract class StackMapUtils
    extends java.lang.Object
    This class provides utility methods to maintain and modify a method's StackMapTable within a Java class file. It can be thought of as an extension to BCEL.

    BCEL ought to automatically build and maintain the StackMapTable in a manner similar to the LineNumberTable and the LocalVariableTable. However, for historical reasons, it does not.

    This class cannot be a set of static methods (like BcelUtil) as it maintains state during the client's processing of a method that must be available on a per thread basis. Thus it is an abstract class extended by InstructionListUtils. A client would not normally extend this class directly.

    • Field Summary

      Fields 
      Modifier and Type Field Description
      protected SimpleLog debugInstrument
      A log to which to print debugging information about program instrumentation.
      private org.apache.bcel.classfile.StackMapEntry @InternedDistinct [] emptyStackmaptable
      An empty StackMap used for initialization.
      protected int firstLocalIndex
      The index of the first 'true' local in the local variable table.
      protected int initialLocalsCount
      The number of local variables in the current method prior to any modifications.
      protected org.apache.bcel.classfile.StackMapType @MonotonicNonNull [] initialTypeList
      Initial state of StackMapTypes for locals on method entry.
      protected org.apache.bcel.generic.InstructionHandle liveRangeEnd
      The end of a local variable's live range: the first instruction after the range.
      protected int liveRangeOperandSize
      The storage size of local variable during its live range.
      protected org.apache.bcel.generic.InstructionHandle liveRangeStart
      The start of a local variable's live range: the first instruction in the range.
      protected org.apache.bcel.generic.Type liveRangeType
      The type of a local variable during its live range.
      protected boolean needStackMap
      Whether or not the current method needs a StackMap; set by setCurrentStackMapTable.
      protected @org.checkerframework.checker.index.qual.NonNegative int numberActiveLocals
      A number of methods in this class search and locate a particular StackMap within the current method.
      protected @Nullable org.apache.bcel.generic.ConstantPoolGen pool
      The pool for the method currently being processed.
      protected int runningOffset
      Offset into code that corresponds to the current StackMap of interest.
      protected @Nullable org.apache.bcel.classfile.StackMap smta
      Original stack map table attribute; set by setCurrentStackMapTable.
      protected org.apache.bcel.classfile.StackMapEntry @Nullable [] stackMapTable
      Working copy of StackMapTable; set by setCurrentStackMapTable.
      protected StackTypes stackTypes
      The types of elements on the operand stack for current method.
      private java.util.Map<org.apache.bcel.generic.InstructionHandle,​java.lang.Integer> uninitializedNewMap
      A map from instructions that create uninitialized NEW objects to the corresponding StackMap entry.
    • Constructor Summary

      Constructors 
      Constructor Description
      StackMapUtils()
      Create a new StackMapUtils object.
    • Method Summary

      All Methods Static Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      protected org.apache.bcel.generic.LocalVariableGen addNewParameter​(org.apache.bcel.generic.MethodGen mgen, java.lang.String argName, org.apache.bcel.generic.Type argType)
      Add a new parameter to the method.
      protected java.lang.String[] addString​(java.lang.String[] arr, java.lang.String newString)
      Returns a String array with newString added to the end of arr.
      protected void adjust_code_for_locals_change​(org.apache.bcel.generic.MethodGen mgen, int indexFirstMovedlocal, int size)
      Process the instruction list, adding size (1 or 2) to the index of each Instruction that references a local that is equal or higher in the local map than indexFirstMovedlocal.
      protected StackTypes bcelCalcStackTypes​(org.apache.bcel.generic.MethodGen mg)
      Calculates the types on the stack for each instruction using the BCEL stack verification routines.
      protected void buildUninitializedNewMap​(org.apache.bcel.generic.InstructionList il)
      We need to locate and remember any NEW instructions that create uninitialized objects.
      protected void create_local_from_live_range​(org.apache.bcel.generic.MethodGen mgen, int offset)
      Create a new LocalVariable from the live_range data.
      protected org.apache.bcel.generic.LocalVariableGen create_method_scope_local​(org.apache.bcel.generic.MethodGen mgen, java.lang.String localName, org.apache.bcel.generic.Type localType)
      Create a new local with a scope of the full method.
      protected void createNewStackMapAttribute​(org.apache.bcel.generic.MethodGen mgen)
      Create a new StackMap code attribute from stackMapTable.
      protected org.apache.bcel.classfile.StackMapEntry findStackMapEqual​(int offset)
      Find the StackMap entry whose offset matches the input argument.
      protected @org.checkerframework.checker.index.qual.IndexOrLow({"stackMapTable"}) int findStackMapIndexAfter​(int offset)
      Find the index of the StackMap entry whose offset is the first one after the input argument.
      protected int findStackMapIndexBefore​(int offset)
      Find the index of the StackMap entry whose offset is the last one before the input argument.
      protected void fixLocalVariableTable​(org.apache.bcel.generic.MethodGen mgen)
      Under some circumstances, there may be gaps in the LocalVariable table.
      protected int gen_locals​(org.apache.bcel.generic.MethodGen mgen, int offset)
      Find the live range of the compiler temp(s) and/or user declared local(s) at the given offset and create a LocalVariableGen for each.
      protected int gen_locals_from_byte_codes​(org.apache.bcel.generic.MethodGen mgen, int offset)
      Calculate the live range of a local variable (or variables).
      protected int gen_locals_from_byte_codes​(org.apache.bcel.generic.MethodGen mgen, int offset, org.apache.bcel.generic.InstructionHandle start)
      Calculate the live range of a local variable starting from the given InstructionHandle.
      protected org.apache.bcel.generic.Type generate_Type_from_StackMapType​(org.apache.bcel.classfile.StackMapType smt)
      Convert a StackMapType to a Type.
      protected org.apache.bcel.classfile.StackMapType generateStackMapTypeFromType​(org.apache.bcel.generic.Type t)
      Convert a Type to a StackMapType.
      protected java.lang.String get_attribute_name​(org.apache.bcel.classfile.Attribute a)
      Return the attribute name for the specified attribute.
      protected @Nullable org.apache.bcel.classfile.Attribute get_local_variable_type_table_attribute​(org.apache.bcel.generic.MethodGen mgen)
      Find the LocalVariableTypeTable attribute for a method.
      protected int getSize​(org.apache.bcel.classfile.StackMapType smt)
      Return the operand size of this type (2 for long and double, 1 otherwise).
      protected @Nullable org.apache.bcel.classfile.Attribute getStackMapTable_attribute​(org.apache.bcel.generic.MethodGen mgen)
      Find the StackMapTable attribute for a method.
      protected boolean is_local_variable_type_table​(org.apache.bcel.classfile.Attribute a)
      Returns whether or not the specified attribute is a LocalVariableTypeTable.
      protected boolean isStackMapTable​(org.apache.bcel.classfile.Attribute a)
      Returns whether or not the specified attribute is a StackMapTable.
      protected void modifyStackMapsForSwitches​(org.apache.bcel.generic.InstructionHandle ih, org.apache.bcel.generic.InstructionList il)
      Check to see if (due to some instruction modifications) there have been any changes in a switch statement's padding bytes.
      protected void printStackMapTable​(java.lang.String prefix)
      Print the contents of the StackMapTable to the debugInstrument.log.
      protected void remove_local_variable_type_table​(org.apache.bcel.generic.MethodGen mgen)
      Remove the local variable type table attribute (LVTT) from mgen.
      protected void set_method_stackTypes​(org.apache.bcel.generic.MethodGen mgen)
      Calculates the stack types for each byte code offset of the current method, and stores them in variable stackTypes.
      protected void setCurrentStackMapTable​(org.apache.bcel.generic.MethodGen mgen, int javaClassVersion)
      Get existing StackMapTable from the MethodGen argument.
      protected static @ClassGetName java.lang.String typeToClassGetName​(org.apache.bcel.generic.Type t)
      Convert a Type name to a Class name.
      protected void update_full_frameStackMap_entries​(int offset, org.apache.bcel.generic.Type typeNewVar, org.apache.bcel.generic.LocalVariableGen[] locals)
      Update any FULL_FRAME StackMap entries to include a new local var.
      private void updateNewObjectStackMapEntries​(int oldOffset, int newOffset)
      One of uninitialized NEW instructions has moved.
      protected void updateStackMapOffset​(int position, int delta)
      We have inserted additional byte(s) into the instruction list; update the StackMaps, if required.
      protected void updateUninitializedNewOffsets​(org.apache.bcel.generic.InstructionList il)
      Check to see if any of the uninitialized NEW instructions have moved.
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Field Detail

      • pool

        protected @Nullable org.apache.bcel.generic.ConstantPoolGen pool
        The pool for the method currently being processed. Must be set by the client. See the sample code in InstructionListUtils for when and how to set this value.
      • debugInstrument

        protected SimpleLog debugInstrument
        A log to which to print debugging information about program instrumentation.
      • needStackMap

        protected boolean needStackMap
        Whether or not the current method needs a StackMap; set by setCurrentStackMapTable.
      • stackMapTable

        protected org.apache.bcel.classfile.StackMapEntry @Nullable [] stackMapTable
        Working copy of StackMapTable; set by setCurrentStackMapTable.
      • smta

        protected @Nullable org.apache.bcel.classfile.StackMap smta
        Original stack map table attribute; set by setCurrentStackMapTable.
      • initialTypeList

        protected org.apache.bcel.classfile.StackMapType @MonotonicNonNull [] initialTypeList
        Initial state of StackMapTypes for locals on method entry.
      • initialLocalsCount

        protected int initialLocalsCount
        The number of local variables in the current method prior to any modifications.
      • numberActiveLocals

        protected @org.checkerframework.checker.index.qual.NonNegative int numberActiveLocals
        A number of methods in this class search and locate a particular StackMap within the current method. This variable contains the number of live local variables within the range of byte code instructions covered by this StackMap. Set by updateStackMapOffset, findStackMapEqual, findStackMapIndexBefore, or findStackMapIndexAfter.
      • runningOffset

        protected int runningOffset
        Offset into code that corresponds to the current StackMap of interest. Set by findStackMapIndexBefore.
      • firstLocalIndex

        protected int firstLocalIndex
        The index of the first 'true' local in the local variable table. That is, after 'this' and any parameters.
      • emptyStackmaptable

        private org.apache.bcel.classfile.StackMapEntry @InternedDistinct [] emptyStackmaptable
        An empty StackMap used for initialization.
      • uninitializedNewMap

        private java.util.Map<org.apache.bcel.generic.InstructionHandle,​java.lang.Integer> uninitializedNewMap
        A map from instructions that create uninitialized NEW objects to the corresponding StackMap entry. Set by buildUninitializedNewMap.
      • liveRangeStart

        protected org.apache.bcel.generic.InstructionHandle liveRangeStart
        The start of a local variable's live range: the first instruction in the range.
      • liveRangeEnd

        protected org.apache.bcel.generic.InstructionHandle liveRangeEnd
        The end of a local variable's live range: the first instruction after the range.
      • liveRangeType

        protected org.apache.bcel.generic.Type liveRangeType
        The type of a local variable during its live range.
      • liveRangeOperandSize

        protected int liveRangeOperandSize
        The storage size of local variable during its live range.
      • stackTypes

        protected StackTypes stackTypes
        The types of elements on the operand stack for current method.
    • Constructor Detail

      • StackMapUtils

        public StackMapUtils()
        Create a new StackMapUtils object.
    • Method Detail

      • addString

        protected java.lang.String[] addString​(java.lang.String[] arr,
                                               java.lang.String newString)
        Returns a String array with newString added to the end of arr.
        Parameters:
        arr - original string array
        newString - string to be added
        Returns:
        the new string array
      • get_attribute_name

        @Pure
        protected final java.lang.String get_attribute_name​(org.apache.bcel.classfile.Attribute a)
        Return the attribute name for the specified attribute.
        Parameters:
        a - the attribute
        Returns:
        the attribute name for the specified attribute
      • is_local_variable_type_table

        @Pure
        protected final boolean is_local_variable_type_table​(org.apache.bcel.classfile.Attribute a)
        Returns whether or not the specified attribute is a LocalVariableTypeTable.
        Parameters:
        a - the attribute
        Returns:
        true iff the attribute is a LocalVariableTypeTable
      • isStackMapTable

        @Pure
        protected final boolean isStackMapTable​(org.apache.bcel.classfile.Attribute a)
        Returns whether or not the specified attribute is a StackMapTable.
        Parameters:
        a - the attribute
        Returns:
        true iff the attribute is a StackMapTable
      • getStackMapTable_attribute

        @Pure
        protected final @Nullable org.apache.bcel.classfile.Attribute getStackMapTable_attribute​(org.apache.bcel.generic.MethodGen mgen)
        Find the StackMapTable attribute for a method. Return null if there isn't one.
        Parameters:
        mgen - the method
        Returns:
        the StackMapTable attribute for the method (or null if not present)
      • get_local_variable_type_table_attribute

        @Pure
        protected final @Nullable org.apache.bcel.classfile.Attribute get_local_variable_type_table_attribute​(org.apache.bcel.generic.MethodGen mgen)
        Find the LocalVariableTypeTable attribute for a method. Return null if there isn't one.
        Parameters:
        mgen - the method
        Returns:
        the LocalVariableTypeTable attribute for the method (or null if not present)
      • remove_local_variable_type_table

        protected final void remove_local_variable_type_table​(org.apache.bcel.generic.MethodGen mgen)
        Remove the local variable type table attribute (LVTT) from mgen. Some instrumentation changes require this to be updated, but without BCEL support that would be hard to do. It should be safe to just delete it since it is optional and really only of use to a debugger.
        Parameters:
        mgen - the method to clear out
      • updateStackMapOffset

        protected final void updateStackMapOffset​(int position,
                                                  int delta)
        We have inserted additional byte(s) into the instruction list; update the StackMaps, if required. Also sets runningOffset.
        Parameters:
        position - the location of insertion
        delta - the size of the insertion
      • findStackMapEqual

        protected final org.apache.bcel.classfile.StackMapEntry findStackMapEqual​(int offset)
        Find the StackMap entry whose offset matches the input argument. Also sets runningOffset.
        Parameters:
        offset - byte code offset
        Returns:
        the corresponding StackMapEntry
      • findStackMapIndexBefore

        protected final int findStackMapIndexBefore​(int offset)
        Find the index of the StackMap entry whose offset is the last one before the input argument. Return -1 if there isn't one. Also sets runningOffset and numberActiveLocals.
        Parameters:
        offset - byte code offset
        Returns:
        the corresponding StackMapEntry index
      • findStackMapIndexAfter

        protected final @org.checkerframework.checker.index.qual.IndexOrLow({"stackMapTable"}) int findStackMapIndexAfter​(int offset)
        Find the index of the StackMap entry whose offset is the first one after the input argument. Return -1 if there isn't one. Also sets runningOffset.
        Parameters:
        offset - byte code offset
        Returns:
        the corresponding StackMapEntry index
      • modifyStackMapsForSwitches

        protected final void modifyStackMapsForSwitches​(org.apache.bcel.generic.InstructionHandle ih,
                                                        org.apache.bcel.generic.InstructionList il)
        Check to see if (due to some instruction modifications) there have been any changes in a switch statement's padding bytes. If so, then update the corresponding StackMap to reflect this change.
        Parameters:
        ih - where to start looking for a switch instruction
        il - instruction list to search
      • buildUninitializedNewMap

        protected final void buildUninitializedNewMap​(org.apache.bcel.generic.InstructionList il)
        We need to locate and remember any NEW instructions that create uninitialized objects. Their offset may be contained in a StackMap entry and will probably need to be updated as we add instrumentation code. Note that these instructions are fairly rare.
        Parameters:
        il - instruction list to search
      • updateNewObjectStackMapEntries

        private final void updateNewObjectStackMapEntries​(int oldOffset,
                                                          int newOffset)
        One of uninitialized NEW instructions has moved. Update its offset in StackMap entries. Note that more than one entry could refer to the same instruction. This is a helper routine used by updateUninitializedNewOffsets.
        Parameters:
        oldOffset - original location of NEW instruction
        newOffset - new location of NEW instruction
      • updateUninitializedNewOffsets

        protected final void updateUninitializedNewOffsets​(org.apache.bcel.generic.InstructionList il)
        Check to see if any of the uninitialized NEW instructions have moved. Again, these are rare, so a linear pass is fine.
        Parameters:
        il - instruction list to search
      • adjust_code_for_locals_change

        protected final void adjust_code_for_locals_change​(org.apache.bcel.generic.MethodGen mgen,
                                                           int indexFirstMovedlocal,
                                                           int size)
        Process the instruction list, adding size (1 or 2) to the index of each Instruction that references a local that is equal or higher in the local map than indexFirstMovedlocal. Size should be the size of the new local that was just inserted at indexFirstMovedlocal.
        Parameters:
        mgen - MethodGen to be modified
        indexFirstMovedlocal - original index of first local moved "up"
        size - size of new local added (1 or 2)
      • setCurrentStackMapTable

        @EnsuresNonNull("stackMapTable")
        protected final void setCurrentStackMapTable​(org.apache.bcel.generic.MethodGen mgen,
                                                     int javaClassVersion)
        Get existing StackMapTable from the MethodGen argument. If there is none, create a new empty one. Sets both smta and stackMapTable. Must be called prior to any other methods that manipulate the stackMapTable!
        Parameters:
        mgen - MethodGen to search
        javaClassVersion - Java version for the classfile; stackMapTable is optional before Java 1.7 (= classfile version 51)
      • printStackMapTable

        protected final void printStackMapTable​(java.lang.String prefix)
        Print the contents of the StackMapTable to the debugInstrument.log.
        Parameters:
        prefix - label to display with table
      • createNewStackMapAttribute

        protected final void createNewStackMapAttribute​(org.apache.bcel.generic.MethodGen mgen)
                                                 throws java.io.IOException
        Create a new StackMap code attribute from stackMapTable.
        Parameters:
        mgen - MethodGen to add attribute to
        Throws:
        java.io.IOException - if cannot create the attribute
      • typeToClassGetName

        protected static @ClassGetName java.lang.String typeToClassGetName​(org.apache.bcel.generic.Type t)
        Convert a Type name to a Class name.
        Parameters:
        t - type whose name is to be converted
        Returns:
        a String containing the class name
      • generateStackMapTypeFromType

        protected final org.apache.bcel.classfile.StackMapType generateStackMapTypeFromType​(org.apache.bcel.generic.Type t)
        Convert a Type to a StackMapType.
        Parameters:
        t - Type to be converted
        Returns:
        result StackMapType
      • generate_Type_from_StackMapType

        protected final org.apache.bcel.generic.Type generate_Type_from_StackMapType​(org.apache.bcel.classfile.StackMapType smt)
        Convert a StackMapType to a Type.
        Parameters:
        smt - StackMapType to be converted
        Returns:
        result Type
      • getSize

        protected final int getSize​(org.apache.bcel.classfile.StackMapType smt)
        Return the operand size of this type (2 for long and double, 1 otherwise).
        Parameters:
        smt - a StackMapType object
        Returns:
        the operand size of this type
      • update_full_frameStackMap_entries

        protected final void update_full_frameStackMap_entries​(int offset,
                                                               org.apache.bcel.generic.Type typeNewVar,
                                                               org.apache.bcel.generic.LocalVariableGen[] locals)
        Update any FULL_FRAME StackMap entries to include a new local var. The locals array is a copy of the local variables PRIOR to the addition of the new local in question.
        Parameters:
        offset - offset into stack of the new variable we are adding
        typeNewVar - type of new variable we are adding
        locals - a copy of the local variable table prior to this modification
      • addNewParameter

        protected final org.apache.bcel.generic.LocalVariableGen addNewParameter​(org.apache.bcel.generic.MethodGen mgen,
                                                                                 java.lang.String argName,
                                                                                 org.apache.bcel.generic.Type argType)
        Add a new parameter to the method. This will be added after last current parameter and before the first local variable. This might have the side effect of causing us to rewrite the method byte codes to adjust the offsets for the local variables - see below for details.

        Must call fixLocalVariableTable (just once per method) before calling this routine.

        Parameters:
        mgen - MethodGen to be modified
        argName - name of new parameter
        argType - type of new parameter
        Returns:
        a LocalVariableGen for the new parameter
      • create_method_scope_local

        protected final org.apache.bcel.generic.LocalVariableGen create_method_scope_local​(org.apache.bcel.generic.MethodGen mgen,
                                                                                           java.lang.String localName,
                                                                                           org.apache.bcel.generic.Type localType)
        Create a new local with a scope of the full method. This means we need to search the existing locals to find the proper index for our new local. This might have the side effect of causing us to rewrite the method byte codes to adjust the offsets for the existing local variables - see below for details.

        Must call fixLocalVariableTable (just once per method) before calling this routine.

        Parameters:
        mgen - MethodGen to be modified
        localName - name of new local
        localType - type of new local
        Returns:
        a LocalVariableGen for the new local
      • fixLocalVariableTable

        @EnsuresNonNull("initialTypeList")
        protected final void fixLocalVariableTable​(org.apache.bcel.generic.MethodGen mgen)
        Under some circumstances, there may be gaps in the LocalVariable table. These gaps occur when the Java compiler adds unnamed parameters and/or unnamed local variables. A gap may also occur for a local variable declared in the source whose lifetime does not cross a StackMap location. This routine creates LocalVariable entries for these missing items.
        1. The java Compiler allocates a hidden parameter for the constructor of an inner class. These items are given the name $hidden$ appended with their offset.
        2. The Java compiler allocates unnamed local temps for:
          • saving the exception in a finally clause
          • the lock for a synchronized block
          • interators
          • user declared locals that never appear in a StackMap
          • (others?)
          These items are given the name DaIkOnTeMp appended with their offset.
        Parameters:
        mgen - MethodGen to be modified
      • gen_locals

        @RequiresNonNull("initialTypeList")
        protected final int gen_locals​(org.apache.bcel.generic.MethodGen mgen,
                                       int offset)
        Find the live range of the compiler temp(s) and/or user declared local(s) at the given offset and create a LocalVariableGen for each. Note the compiler might generate temps of different sizes at the same offset (must have disjoint lifetimes). In general, these variables will not have a live range of the entire method. We try to calculate the true live range so if, at some later point, we need to generate a new StackMap we can include the correct list of active locals.
        Parameters:
        mgen - the method
        offset - compiler assigned local offset of hidden temp(s) or local(s)
        Returns:
        offset incremented by size of smallest variable found at offset
      • gen_locals_from_byte_codes

        protected final int gen_locals_from_byte_codes​(org.apache.bcel.generic.MethodGen mgen,
                                                       int offset)
        Calculate the live range of a local variable (or variables).
        Parameters:
        mgen - MethodGen of method to search
        offset - offset of the local
        Returns:
        minimum size of local(s) found at offset
      • gen_locals_from_byte_codes

        protected final int gen_locals_from_byte_codes​(org.apache.bcel.generic.MethodGen mgen,
                                                       int offset,
                                                       org.apache.bcel.generic.InstructionHandle start)
        Calculate the live range of a local variable starting from the given InstructionHandle. The following live_range globals must be set:
        • liveRangeStart
        • liveRangeEnd
        • liveRangeType
        • liveRangeOperandSize
        Parameters:
        mgen - MethodGen of method to search
        offset - offset of the local
        start - search forward from this instruction
        Returns:
        minimum size of local(s) found at offset
      • create_local_from_live_range

        protected final void create_local_from_live_range​(org.apache.bcel.generic.MethodGen mgen,
                                                          int offset)
        Create a new LocalVariable from the live_range data. Does nothing if liveRangeStart is null.
        Parameters:
        mgen - MethodGen of method to search
        offset - offset of the local
      • set_method_stackTypes

        protected final void set_method_stackTypes​(org.apache.bcel.generic.MethodGen mgen)
        Calculates the stack types for each byte code offset of the current method, and stores them in variable stackTypes. Does nothing if stackTypes is already set.
        Parameters:
        mgen - MethodGen of method whose stack types to compute
      • bcelCalcStackTypes

        protected final StackTypes bcelCalcStackTypes​(org.apache.bcel.generic.MethodGen mg)
        Calculates the types on the stack for each instruction using the BCEL stack verification routines.
        Parameters:
        mg - MethodGen for the method to be analyzed
        Returns:
        a StackTypes object for the method