1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 package com.sun.org.apache.bcel.internal.generic;
  21 
  22 import java.util.Collection;
  23 import java.util.HashMap;
  24 import java.util.HashSet;
  25 import java.util.Map;
  26 import java.util.Set;
  27 
  28 import com.sun.org.apache.bcel.internal.classfile.Utility;
  29 
  30 /**
  31  * Instances of this class give users a handle to the instructions contained in
  32  * an InstructionList. Instruction objects may be used more than once within a
  33  * list, this is useful because it saves memory and may be much faster.
  34  *
  35  * Within an InstructionList an InstructionHandle object is wrapped around all
  36  * instructions, i.e., it implements a cell in a doubly-linked list. From the
  37  * outside only the next and the previous instruction (handle) are accessible.
  38  * One can traverse the list via an Enumeration returned by
  39  * InstructionList.elements().
  40  *
  41  * @version $Id: InstructionHandle.java 1749603 2016-06-21 20:50:19Z ggregory $
  42  * @see Instruction
  43  * @see BranchHandle
  44  * @see InstructionList
  45  */
  46 public class InstructionHandle {
  47 
  48     private InstructionHandle next;
  49     private InstructionHandle prev;
  50     private Instruction instruction;
  51 
  52     private int i_position = -1; // byte code offset of instruction
  53 
  54     private Set<InstructionTargeter> targeters;
  55     private Map<Object, Object> attributes;
  56 
  57     public final InstructionHandle getNext() {
  58         return next;
  59     }
  60 
  61     public final InstructionHandle getPrev() {
  62         return prev;
  63     }
  64 
  65     public final Instruction getInstruction() {
  66         return instruction;
  67     }
  68 
  69     /**
  70      * Replace current instruction contained in this handle. Old instruction is
  71      * disposed using Instruction.dispose().
  72      */
  73     public void setInstruction(final Instruction i) { // Overridden in BranchHandle TODO could be package-protected?
  74         if (i == null) {
  75             throw new ClassGenException("Assigning null to handle");
  76         }
  77         if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) {
  78             throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
  79         }
  80         if (instruction != null) {
  81             instruction.dispose();
  82         }
  83         instruction = i;
  84     }
  85 
  86     /**
  87      * Temporarily swap the current instruction, without disturbing anything.
  88      * Meant to be used by a debugger, implementing breakpoints. Current
  89      * instruction is returned.
  90      * <p>
  91      * Warning: if this is used on a BranchHandle then some methods such as
  92      * getPosition() will still refer to the original cached instruction,
  93      * whereas other BH methods may affect the cache and the replacement
  94      * instruction.
  95      */
  96     // See BCEL-273
  97     // TODO remove this method in any redesign of BCEL
  98     public Instruction swapInstruction(final Instruction i) {
  99         final Instruction oldInstruction = instruction;
 100         instruction = i;
 101         return oldInstruction;
 102     }
 103 
 104 
 105     /*private*/
 106     protected InstructionHandle(final Instruction i) {
 107         setInstruction(i);
 108     }
 109 
 110     private static InstructionHandle ih_list = null; // List of reusable handles
 111 
 112     /**
 113      * Factory method.
 114      */
 115     static InstructionHandle getInstructionHandle(final Instruction i) {
 116         if (ih_list == null) {
 117             return new InstructionHandle(i);
 118         }
 119         final InstructionHandle ih = ih_list;
 120         ih_list = ih.next;
 121         ih.setInstruction(i);
 122         return ih;
 123     }
 124 
 125     /**
 126      * Called by InstructionList.setPositions when setting the position for
 127      * every instruction. In the presence of variable length instructions
 128      * `setPositions()' performs multiple passes over the instruction list to
 129      * calculate the correct (byte) positions and offsets by calling this
 130      * function.
 131      *
 132      * @param offset additional offset caused by preceding (variable length)
 133      * instructions
 134      * @param max_offset the maximum offset that may be caused by these
 135      * instructions
 136      * @return additional offset caused by possible change of this instruction's
 137      * length
 138      */
 139     protected int updatePosition(final int offset, final int max_offset) {
 140         i_position += offset;
 141         return 0;
 142     }
 143 
 144     /**
 145      * @return the position, i.e., the byte code offset of the contained
 146      * instruction. This is accurate only after InstructionList.setPositions()
 147      * has been called.
 148      */
 149     public int getPosition() {
 150         return i_position;
 151     }
 152 
 153     /**
 154      * Set the position, i.e., the byte code offset of the contained
 155      * instruction.
 156      */
 157     void setPosition(final int pos) {
 158         i_position = pos;
 159     }
 160 
 161     /**
 162      * Overridden in BranchHandle
 163      */
 164     protected void addHandle() {
 165         next = ih_list;
 166         ih_list = this;
 167     }
 168 
 169     /**
 170      * Delete contents, i.e., remove user access and make handle reusable.
 171      */
 172     void dispose() {
 173         next = prev = null;
 174         instruction.dispose();
 175         instruction = null;
 176         i_position = -1;
 177         attributes = null;
 178         removeAllTargeters();
 179         addHandle();
 180     }
 181 
 182     /**
 183      * Remove all targeters, if any.
 184      */
 185     public void removeAllTargeters() {
 186         if (targeters != null) {
 187             targeters.clear();
 188         }
 189     }
 190 
 191     /**
 192      * Denote this handle isn't referenced anymore by t.
 193      */
 194     public void removeTargeter(final InstructionTargeter t) {
 195         if (targeters != null) {
 196             targeters.remove(t);
 197         }
 198     }
 199 
 200     /**
 201      * Denote this handle is being referenced by t.
 202      */
 203     public void addTargeter(final InstructionTargeter t) {
 204         if (targeters == null) {
 205             targeters = new HashSet<>();
 206         }
 207         //if(!targeters.contains(t))
 208         targeters.add(t);
 209     }
 210 
 211     public boolean hasTargeters() {
 212         return (targeters != null) && (targeters.size() > 0);
 213     }
 214 
 215     /**
 216      * @return null, if there are no targeters
 217      */
 218     public InstructionTargeter[] getTargeters() {
 219         if (!hasTargeters()) {
 220             return new InstructionTargeter[0];
 221         }
 222         final InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
 223         targeters.toArray(t);
 224         return t;
 225     }
 226 
 227     /**
 228      * @return a (verbose) string representation of the contained instruction.
 229      */
 230     public String toString(final boolean verbose) {
 231         return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
 232     }
 233 
 234     /**
 235      * @return a string representation of the contained instruction.
 236      */
 237     @Override
 238     public String toString() {
 239         return toString(true);
 240     }
 241 
 242     /**
 243      * Add an attribute to an instruction handle.
 244      *
 245      * @param key the key object to store/retrieve the attribute
 246      * @param attr the attribute to associate with this handle
 247      */
 248     public void addAttribute(final Object key, final Object attr) {
 249         if (attributes == null) {
 250             attributes = new HashMap<>(3);
 251         }
 252         attributes.put(key, attr);
 253     }
 254 
 255     /**
 256      * Delete an attribute of an instruction handle.
 257      *
 258      * @param key the key object to retrieve the attribute
 259      */
 260     public void removeAttribute(final Object key) {
 261         if (attributes != null) {
 262             attributes.remove(key);
 263         }
 264     }
 265 
 266     /**
 267      * Get attribute of an instruction handle.
 268      *
 269      * @param key the key object to store/retrieve the attribute
 270      */
 271     public Object getAttribute(final Object key) {
 272         if (attributes != null) {
 273             return attributes.get(key);
 274         }
 275         return null;
 276     }
 277 
 278     /**
 279      * @return all attributes associated with this handle
 280      */
 281     public Collection<Object> getAttributes() {
 282         if (attributes == null) {
 283             attributes = new HashMap<>(3);
 284         }
 285         return attributes.values();
 286     }
 287 
 288     /**
 289      * Convenience method, simply calls accept() on the contained instruction.
 290      *
 291      * @param v Visitor object
 292      */
 293     public void accept(final Visitor v) {
 294         instruction.accept(v);
 295     }
 296 
 297     /**
 298      * @param next the next to set
 299      * @ since 6.0
 300      */
 301     final InstructionHandle setNext(final InstructionHandle next) {
 302         this.next = next;
 303         return next;
 304     }
 305 
 306     /**
 307      * @param prev the prev to set
 308      * @ since 6.0
 309      */
 310     final InstructionHandle setPrev(final InstructionHandle prev) {
 311         this.prev = prev;
 312         return prev;
 313     }
 314 }