1 /*
   2  * Copyright (c) 2017, 2019, 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
  36  * around all instructions, i.e., it implements a cell in a
  37  * doubly-linked list. From the outside only the next and the
  38  * previous instruction (handle) are accessible. One
  39  * can traverse the list via an Enumeration returned by
  40  * InstructionList.elements().
  41  *
  42  * @version $Id$
  43  * @see Instruction
  44  * @see BranchHandle
  45  * @see InstructionList
  46  */
  47 public class InstructionHandle {
  48 
  49     private InstructionHandle next;
  50     private InstructionHandle prev;
  51     private Instruction instruction;
  52 
  53     private int i_position = -1; // byte code offset of instruction
  54 
  55     private Set<InstructionTargeter> targeters;
  56     private Map<Object, Object> attributes;
  57 
  58 
  59     /**
  60      * Does nothing.
  61      *
  62      * @deprecated Does nothing as of 6.3.1.
  63      */
  64     @Deprecated
  65     protected void addHandle() {
  66         // noop
  67     }
  68 
  69     public final InstructionHandle getNext() {
  70         return next;
  71     }
  72 
  73 
  74     public final InstructionHandle getPrev() {
  75         return prev;
  76     }
  77 
  78 
  79     public final Instruction getInstruction() {
  80         return instruction;
  81     }
  82 
  83 
  84     /**
  85      * Replace current instruction contained in this handle.
  86      * Old instruction is disposed using Instruction.dispose().
  87      */
  88     public void setInstruction( final Instruction i ) { // Overridden in BranchHandle TODO could be package-protected?
  89         if (i == null) {
  90             throw new ClassGenException("Assigning null to handle");
  91         }
  92         if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) {
  93             throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
  94         }
  95         if (instruction != null) {
  96             instruction.dispose();
  97         }
  98         instruction = i;
  99     }
 100 
 101 
 102     /**
 103      * Temporarily swap the current instruction, without disturbing
 104      * anything. Meant to be used by a debugger, implementing
 105      * breakpoints. Current instruction is returned.
 106      * <p>
 107      * Warning: if this is used on a BranchHandle then some methods such as
 108      * getPosition() will still refer to the original cached instruction, whereas
 109      * other BH methods may affect the cache and the replacement instruction.
 110      */
 111     // See BCEL-273
 112     // TODO remove this method in any redesign of BCEL
 113     public Instruction swapInstruction( final Instruction i ) {
 114         final Instruction oldInstruction = instruction;
 115         instruction = i;
 116         return oldInstruction;
 117     }
 118 
 119 
 120     /*private*/protected InstructionHandle(final Instruction i) {
 121         setInstruction(i);
 122     }
 123 
 124     /** Factory method.
 125      */
 126     static InstructionHandle getInstructionHandle( final Instruction i ) {
 127         return new InstructionHandle(i);
 128     }
 129 
 130 
 131     /**
 132      * Called by InstructionList.setPositions when setting the position for every
 133      * instruction. In the presence of variable length instructions `setPositions()'
 134      * performs multiple passes over the instruction list to calculate the
 135      * correct (byte) positions and offsets by calling this function.
 136      *
 137      * @param offset additional offset caused by preceding (variable length) instructions
 138      * @param max_offset the maximum offset that may be caused by these instructions
 139      * @return additional offset caused by possible change of this instruction's length
 140      */
 141     protected int updatePosition( final int offset, final int max_offset ) {
 142         i_position += offset;
 143         return 0;
 144     }
 145 
 146 
 147     /** @return the position, i.e., the byte code offset of the contained
 148      * instruction. This is accurate only after
 149      * InstructionList.setPositions() has been called.
 150      */
 151     public int getPosition() {
 152         return i_position;
 153     }
 154 
 155 
 156     /** Set the position, i.e., the byte code offset of the contained
 157      * instruction.
 158      */
 159     void setPosition( final int pos ) {
 160         i_position = pos;
 161     }
 162 
 163 
 164     /**
 165      * Delete contents, i.e., remove user access.
 166      */
 167     void dispose() {
 168         next = prev = null;
 169         instruction.dispose();
 170         instruction = null;
 171         i_position = -1;
 172         attributes = null;
 173         removeAllTargeters();
 174     }
 175 
 176 
 177     /** Remove all targeters, if any.
 178      */
 179     public void removeAllTargeters() {
 180         if (targeters != null) {
 181             targeters.clear();
 182         }
 183     }
 184 
 185 
 186     /**
 187      * Denote this handle isn't referenced anymore by t.
 188      */
 189     public void removeTargeter( final InstructionTargeter t ) {
 190         if (targeters != null) {
 191             targeters.remove(t);
 192         }
 193     }
 194 
 195 
 196     /**
 197      * Denote this handle is being referenced by t.
 198      */
 199     public void addTargeter( final InstructionTargeter t ) {
 200         if (targeters == null) {
 201             targeters = new HashSet<>();
 202         }
 203         //if(!targeters.contains(t))
 204         targeters.add(t);
 205     }
 206 
 207 
 208     public boolean hasTargeters() {
 209         return (targeters != null) && (targeters.size() > 0);
 210     }
 211 
 212 
 213     /**
 214      * @return null, if there are no targeters
 215      */
 216     public InstructionTargeter[] getTargeters() {
 217         if (!hasTargeters()) {
 218             return new InstructionTargeter[0];
 219         }
 220         final InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
 221         targeters.toArray(t);
 222         return t;
 223     }
 224 
 225 
 226     /** @return a (verbose) string representation of the contained instruction.
 227      */
 228     public String toString( final boolean verbose ) {
 229         return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
 230     }
 231 
 232 
 233     /** @return a string representation of the contained instruction.
 234      */
 235     @Override
 236     public String toString() {
 237         return toString(true);
 238     }
 239 
 240 
 241     /** Add an attribute to an instruction handle.
 242      *
 243      * @param key the key object to store/retrieve the attribute
 244      * @param attr the attribute to associate with this handle
 245      */
 246     public void addAttribute( final Object key, final Object attr ) {
 247         if (attributes == null) {
 248             attributes = new HashMap<>(3);
 249         }
 250         attributes.put(key, attr);
 251     }
 252 
 253 
 254     /** Delete an attribute of an instruction handle.
 255      *
 256      * @param key the key object to retrieve the attribute
 257      */
 258     public void removeAttribute( final Object key ) {
 259         if (attributes != null) {
 260             attributes.remove(key);
 261         }
 262     }
 263 
 264 
 265     /** Get attribute of an instruction handle.
 266      *
 267      * @param key the key object to store/retrieve the attribute
 268      */
 269     public Object getAttribute( final Object key ) {
 270         if (attributes != null) {
 271             return attributes.get(key);
 272         }
 273         return null;
 274     }
 275 
 276 
 277     /** @return all attributes associated with this handle
 278      */
 279     public Collection<Object> getAttributes() {
 280         if (attributes == null) {
 281             attributes = new HashMap<>(3);
 282         }
 283         return attributes.values();
 284     }
 285 
 286 
 287     /** Convenience method, simply calls accept() on the contained instruction.
 288      *
 289      * @param v Visitor object
 290      */
 291     public void accept( final Visitor v ) {
 292         instruction.accept(v);
 293     }
 294 
 295 
 296     /**
 297      * @param next the next to set
 298      * @ since 6.0
 299      */
 300     final InstructionHandle setNext(final InstructionHandle next) {
 301         this.next = next;
 302         return next;
 303     }
 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 }