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