001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 */ 018package org.apache.bcel.generic; 019 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Set; 025 026import org.apache.bcel.classfile.Utility; 027 028/** 029 * Instances of this class give users a handle to the instructions contained in 030 * an InstructionList. Instruction objects may be used more than once within a 031 * list, this is useful because it saves memory and may be much faster. 032 * 033 * Within an InstructionList an InstructionHandle object is wrapped 034 * around all instructions, i.e., it implements a cell in a 035 * doubly-linked list. From the outside only the next and the 036 * previous instruction (handle) are accessible. One 037 * can traverse the list via an Enumeration returned by 038 * InstructionList.elements(). 039 * 040 * @version $Id$ 041 * @see Instruction 042 * @see BranchHandle 043 * @see InstructionList 044 */ 045public class InstructionHandle { 046 047 private InstructionHandle next; 048 private InstructionHandle prev; 049 private Instruction instruction; 050 051 /** 052 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 053 */ 054 @Deprecated 055 protected int i_position = -1; // byte code offset of instruction 056 057 private Set<InstructionTargeter> targeters; 058 private Map<Object, Object> attributes; 059 060 061 /** 062 * Does nothing. 063 * 064 * @deprecated Does nothing as of 6.3.1. 065 */ 066 @Deprecated 067 protected void addHandle() { 068 // noop 069 } 070 071 public final InstructionHandle getNext() { 072 return next; 073 } 074 075 076 public final InstructionHandle getPrev() { 077 return prev; 078 } 079 080 081 public final Instruction getInstruction() { 082 return instruction; 083 } 084 085 086 /** 087 * Replace current instruction contained in this handle. 088 * Old instruction is disposed using Instruction.dispose(). 089 */ 090 public void setInstruction( final Instruction i ) { // Overridden in BranchHandle TODO could be package-protected? 091 if (i == null) { 092 throw new ClassGenException("Assigning null to handle"); 093 } 094 if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) { 095 throw new ClassGenException("Assigning branch instruction " + i + " to plain handle"); 096 } 097 if (instruction != null) { 098 instruction.dispose(); 099 } 100 instruction = i; 101 } 102 103 104 /** 105 * Temporarily swap the current instruction, without disturbing 106 * anything. Meant to be used by a debugger, implementing 107 * breakpoints. Current instruction is returned. 108 * <p> 109 * Warning: if this is used on a BranchHandle then some methods such as 110 * getPosition() will still refer to the original cached instruction, whereas 111 * other BH methods may affect the cache and the replacement instruction. 112 */ 113 // See BCEL-273 114 // TODO remove this method in any redesign of BCEL 115 public Instruction swapInstruction( final Instruction i ) { 116 final Instruction oldInstruction = instruction; 117 instruction = i; 118 return oldInstruction; 119 } 120 121 122 /*private*/protected InstructionHandle(final Instruction i) { 123 setInstruction(i); 124 } 125 126 /** Factory method. 127 */ 128 static InstructionHandle getInstructionHandle( final Instruction i ) { 129 return new InstructionHandle(i); 130 } 131 132 133 /** 134 * Called by InstructionList.setPositions when setting the position for every 135 * instruction. In the presence of variable length instructions `setPositions()' 136 * performs multiple passes over the instruction list to calculate the 137 * correct (byte) positions and offsets by calling this function. 138 * 139 * @param offset additional offset caused by preceding (variable length) instructions 140 * @param max_offset the maximum offset that may be caused by these instructions 141 * @return additional offset caused by possible change of this instruction's length 142 */ 143 protected int updatePosition( final int offset, final int max_offset ) { 144 i_position += offset; 145 return 0; 146 } 147 148 149 /** @return the position, i.e., the byte code offset of the contained 150 * instruction. This is accurate only after 151 * InstructionList.setPositions() has been called. 152 */ 153 public int getPosition() { 154 return i_position; 155 } 156 157 158 /** Set the position, i.e., the byte code offset of the contained 159 * instruction. 160 */ 161 void setPosition( final int pos ) { 162 i_position = pos; 163 } 164 165 166 /** 167 * Delete contents, i.e., remove user access. 168 */ 169 void dispose() { 170 next = prev = null; 171 instruction.dispose(); 172 instruction = null; 173 i_position = -1; 174 attributes = null; 175 removeAllTargeters(); 176 } 177 178 179 /** Remove all targeters, if any. 180 */ 181 public void removeAllTargeters() { 182 if (targeters != null) { 183 targeters.clear(); 184 } 185 } 186 187 188 /** 189 * Denote this handle isn't referenced anymore by t. 190 */ 191 public void removeTargeter( final InstructionTargeter t ) { 192 if (targeters != null) { 193 targeters.remove(t); 194 } 195 } 196 197 198 /** 199 * Denote this handle is being referenced by t. 200 */ 201 public void addTargeter( final InstructionTargeter t ) { 202 if (targeters == null) { 203 targeters = new HashSet<>(); 204 } 205 //if(!targeters.contains(t)) 206 targeters.add(t); 207 } 208 209 210 public boolean hasTargeters() { 211 return (targeters != null) && (targeters.size() > 0); 212 } 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 /** 299 * @param next the next to set 300 * @ since 6.0 301 */ 302 final InstructionHandle setNext(final InstructionHandle next) { 303 this.next = next; 304 return next; 305 } 306 307 308 /** 309 * @param prev the prev to set 310 * @ since 6.0 311 */ 312 final InstructionHandle setPrev(final InstructionHandle prev) { 313 this.prev = prev; 314 return prev; 315 } 316}