InstructionHandle.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.generic;

  18. import java.util.Collection;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.Map;
  22. import java.util.Set;

  23. import org.apache.bcel.classfile.Utility;

  24. /**
  25.  * Instances of this class give users a handle to the instructions contained in an InstructionList. Instruction objects
  26.  * may be used more than once within a list, this is useful because it saves memory and may be much faster.
  27.  *
  28.  * Within an InstructionList an InstructionHandle object is wrapped around all instructions, i.e., it implements a cell
  29.  * in a doubly-linked list. From the outside only the next and the previous instruction (handle) are accessible. One can
  30.  * traverse the list via an Enumeration returned by InstructionList.elements().
  31.  *
  32.  * @see Instruction
  33.  * @see BranchHandle
  34.  * @see InstructionList
  35.  */
  36. public class InstructionHandle {

  37.     /**
  38.      * Empty array.
  39.      *
  40.      * @since 6.6.0
  41.      */
  42.     public static final InstructionHandle[] EMPTY_ARRAY = {};

  43.     /**
  44.      * Empty array.
  45.      */
  46.     static final InstructionTargeter[] EMPTY_INSTRUCTION_TARGETER_ARRAY = {};

  47.     /**
  48.      * Factory method.
  49.      */
  50.     static InstructionHandle getInstructionHandle(final Instruction i) {
  51.         return new InstructionHandle(i);
  52.     }

  53.     private InstructionHandle next;
  54.     private InstructionHandle prev;

  55.     private Instruction instruction;

  56.     /**
  57.      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
  58.      */
  59.     @Deprecated
  60.     protected int i_position = -1; // byte code offset of instruction
  61.     private Set<InstructionTargeter> targeters;

  62.     private Map<Object, Object> attributes;

  63.     protected InstructionHandle(final Instruction i) {
  64.         setInstruction(i);
  65.     }

  66.     /**
  67.      * Convenience method, simply calls accept() on the contained instruction.
  68.      *
  69.      * @param v Visitor object
  70.      */
  71.     public void accept(final Visitor v) {
  72.         instruction.accept(v);
  73.     }

  74.     /**
  75.      * Add an attribute to an instruction handle.
  76.      *
  77.      * @param key the key object to store/retrieve the attribute
  78.      * @param attr the attribute to associate with this handle
  79.      */
  80.     public void addAttribute(final Object key, final Object attr) {
  81.         if (attributes == null) {
  82.             attributes = new HashMap<>(3);
  83.         }
  84.         attributes.put(key, attr);
  85.     }

  86.     /**
  87.      * Does nothing.
  88.      *
  89.      * @deprecated Does nothing as of 6.3.1.
  90.      */
  91.     @Deprecated
  92.     protected void addHandle() {
  93.         // noop
  94.     }

  95.     /**
  96.      * Denote this handle is being referenced by t.
  97.      */
  98.     public void addTargeter(final InstructionTargeter t) {
  99.         if (targeters == null) {
  100.             targeters = new HashSet<>();
  101.         }
  102.         // if (!targeters.contains(t))
  103.         targeters.add(t);
  104.     }

  105.     /**
  106.      * Delete contents, i.e., remove user access.
  107.      */
  108.     void dispose() {
  109.         next = prev = null;
  110.         instruction.dispose();
  111.         instruction = null;
  112.         i_position = -1;
  113.         attributes = null;
  114.         removeAllTargeters();
  115.     }

  116.     /**
  117.      * Gets attribute of an instruction handle.
  118.      *
  119.      * @param key the key object to store/retrieve the attribute
  120.      */
  121.     public Object getAttribute(final Object key) {
  122.         return attributes != null ? attributes.get(key) : null;
  123.     }

  124.     /**
  125.      * @return all attributes associated with this handle
  126.      */
  127.     public Collection<Object> getAttributes() {
  128.         if (attributes == null) {
  129.             attributes = new HashMap<>(3);
  130.         }
  131.         return attributes.values();
  132.     }

  133.     public final Instruction getInstruction() {
  134.         return instruction;
  135.     }

  136.     public final InstructionHandle getNext() {
  137.         return next;
  138.     }

  139.     /**
  140.      * @return the position, i.e., the byte code offset of the contained instruction. This is accurate only after
  141.      *         InstructionList.setPositions() has been called.
  142.      */
  143.     public int getPosition() {
  144.         return i_position;
  145.     }

  146.     public final InstructionHandle getPrev() {
  147.         return prev;
  148.     }

  149.     /**
  150.      * @return null, if there are no targeters
  151.      */
  152.     public InstructionTargeter[] getTargeters() {
  153.         if (!hasTargeters()) {
  154.             return EMPTY_INSTRUCTION_TARGETER_ARRAY;
  155.         }
  156.         final InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
  157.         targeters.toArray(t);
  158.         return t;
  159.     }

  160.     public boolean hasTargeters() {
  161.         return targeters != null && !targeters.isEmpty();
  162.     }

  163.     /**
  164.      * Remove all targeters, if any.
  165.      */
  166.     public void removeAllTargeters() {
  167.         if (targeters != null) {
  168.             targeters.clear();
  169.         }
  170.     }

  171.     /**
  172.      * Delete an attribute of an instruction handle.
  173.      *
  174.      * @param key the key object to retrieve the attribute
  175.      */
  176.     public void removeAttribute(final Object key) {
  177.         if (attributes != null) {
  178.             attributes.remove(key);
  179.         }
  180.     }

  181.     /**
  182.      * Denote this handle isn't referenced anymore by t.
  183.      */
  184.     public void removeTargeter(final InstructionTargeter t) {
  185.         if (targeters != null) {
  186.             targeters.remove(t);
  187.         }
  188.     }

  189.     /**
  190.      * Replace current instruction contained in this handle. Old instruction is disposed using Instruction.dispose().
  191.      */
  192.     public void setInstruction(final Instruction i) { // Overridden in BranchHandle TODO could be package-protected?
  193.         if (i == null) {
  194.             throw new ClassGenException("Assigning null to handle");
  195.         }
  196.         if (this.getClass() != BranchHandle.class && i instanceof BranchInstruction) {
  197.             throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
  198.         }
  199.         if (instruction != null) {
  200.             instruction.dispose();
  201.         }
  202.         instruction = i;
  203.     }

  204.     /**
  205.      * @param next the next to set
  206.      * @since 6.0
  207.      */
  208.     final InstructionHandle setNext(final InstructionHandle next) {
  209.         this.next = next;
  210.         return next;
  211.     }

  212.     /**
  213.      * Sets the position, i.e., the byte code offset of the contained instruction.
  214.      */
  215.     void setPosition(final int pos) {
  216.         i_position = pos;
  217.     }

  218.     /**
  219.      * @param prev the prev to set
  220.      * @since 6.0
  221.      */
  222.     final InstructionHandle setPrev(final InstructionHandle prev) {
  223.         this.prev = prev;
  224.         return prev;
  225.     }

  226.     /**
  227.      * Temporarily swap the current instruction, without disturbing anything. Meant to be used by a debugger, implementing
  228.      * breakpoints. Current instruction is returned.
  229.      * <p>
  230.      * Warning: if this is used on a BranchHandle then some methods such as getPosition() will still refer to the original
  231.      * cached instruction, whereas other BH methods may affect the cache and the replacement instruction.
  232.      */
  233.     // See BCEL-273
  234.     // TODO remove this method in any redesign of BCEL
  235.     public Instruction swapInstruction(final Instruction i) {
  236.         final Instruction oldInstruction = instruction;
  237.         instruction = i;
  238.         return oldInstruction;
  239.     }

  240.     /**
  241.      * @return a string representation of the contained instruction.
  242.      */
  243.     @Override
  244.     public String toString() {
  245.         return toString(true);
  246.     }

  247.     /**
  248.      * @return a (verbose) string representation of the contained instruction.
  249.      */
  250.     public String toString(final boolean verbose) {
  251.         return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
  252.     }

  253.     /**
  254.      * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable
  255.      * length instructions 'setPositions()' performs multiple passes over the instruction list to calculate the correct
  256.      * (byte) positions and offsets by calling this function.
  257.      *
  258.      * @param offset additional offset caused by preceding (variable length) instructions
  259.      * @param maxOffset the maximum offset that may be caused by these instructions
  260.      * @return additional offset caused by possible change of this instruction's length
  261.      */
  262.     protected int updatePosition(final int offset, final int maxOffset) {
  263.         i_position += offset;
  264.         return 0;
  265.     }
  266. }