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 }