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 * @see Instruction 041 * @see BranchHandle 042 * @see InstructionList 043 */ 044public class InstructionHandle { 045 046 private InstructionHandle next; 047 private InstructionHandle prev; 048 private Instruction instruction; 049 050 /** 051 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 052 */ 053 @Deprecated 054 protected int i_position = -1; // byte code offset of instruction 055 056 private Set<InstructionTargeter> targeters; 057 private Map<Object, Object> attributes; 058 059 060 /** 061 * Does nothing. 062 * 063 * @deprecated Does nothing as of 6.3.1. 064 */ 065 @Deprecated 066 protected void addHandle() { 067 // noop 068 } 069 070 public final InstructionHandle getNext() { 071 return next; 072 } 073 074 075 public final InstructionHandle getPrev() { 076 return prev; 077 } 078 079 080 public final Instruction getInstruction() { 081 return instruction; 082 } 083 084 085 /** 086 * Replace current instruction contained in this handle. 087 * Old instruction is disposed using Instruction.dispose(). 088 */ 089 public void setInstruction( final Instruction i ) { // Overridden in BranchHandle TODO could be package-protected? 090 if (i == null) { 091 throw new ClassGenException("Assigning null to handle"); 092 } 093 if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) { 094 throw new ClassGenException("Assigning branch instruction " + i + " to plain handle"); 095 } 096 if (instruction != null) { 097 instruction.dispose(); 098 } 099 instruction = i; 100 } 101 102 103 /** 104 * Temporarily swap the current instruction, without disturbing 105 * anything. Meant to be used by a debugger, implementing 106 * breakpoints. Current instruction is returned. 107 * <p> 108 * Warning: if this is used on a BranchHandle then some methods such as 109 * getPosition() will still refer to the original cached instruction, whereas 110 * other BH methods may affect the cache and the replacement instruction. 111 */ 112 // See BCEL-273 113 // TODO remove this method in any redesign of BCEL 114 public Instruction swapInstruction( final Instruction i ) { 115 final Instruction oldInstruction = instruction; 116 instruction = i; 117 return oldInstruction; 118 } 119 120 121 /*private*/protected InstructionHandle(final Instruction i) { 122 setInstruction(i); 123 } 124 125 /** Factory method. 126 */ 127 static InstructionHandle getInstructionHandle( final Instruction i ) { 128 return new InstructionHandle(i); 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 */ 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 */ 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 */ 160 void setPosition( final int pos ) { 161 i_position = pos; 162 } 163 164 165 /** 166 * Delete contents, i.e., remove user access. 167 */ 168 void dispose() { 169 next = prev = null; 170 instruction.dispose(); 171 instruction = null; 172 i_position = -1; 173 attributes = null; 174 removeAllTargeters(); 175 } 176 177 178 /** Remove all targeters, if any. 179 */ 180 public void removeAllTargeters() { 181 if (targeters != null) { 182 targeters.clear(); 183 } 184 } 185 186 187 /** 188 * Denote this handle isn't referenced anymore by t. 189 */ 190 public void removeTargeter( final InstructionTargeter t ) { 191 if (targeters != null) { 192 targeters.remove(t); 193 } 194 } 195 196 197 /** 198 * Denote this handle is being referenced by t. 199 */ 200 public void addTargeter( final InstructionTargeter t ) { 201 if (targeters == null) { 202 targeters = new HashSet<>(); 203 } 204 //if(!targeters.contains(t)) 205 targeters.add(t); 206 } 207 208 209 public boolean hasTargeters() { 210 return (targeters != null) && (targeters.size() > 0); 211 } 212 213 214 /** 215 * @return null, if there are no targeters 216 */ 217 public InstructionTargeter[] getTargeters() { 218 if (!hasTargeters()) { 219 return new InstructionTargeter[0]; 220 } 221 final InstructionTargeter[] t = new InstructionTargeter[targeters.size()]; 222 targeters.toArray(t); 223 return t; 224 } 225 226 227 /** @return a (verbose) string representation of the contained instruction. 228 */ 229 public String toString( final boolean verbose ) { 230 return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose); 231 } 232 233 234 /** @return a string representation of the contained instruction. 235 */ 236 @Override 237 public String toString() { 238 return toString(true); 239 } 240 241 242 /** Add an attribute to an instruction handle. 243 * 244 * @param key the key object to store/retrieve the attribute 245 * @param attr the attribute to associate with this handle 246 */ 247 public void addAttribute( final Object key, final Object attr ) { 248 if (attributes == null) { 249 attributes = new HashMap<>(3); 250 } 251 attributes.put(key, attr); 252 } 253 254 255 /** Delete an attribute of an instruction handle. 256 * 257 * @param key the key object to retrieve the attribute 258 */ 259 public void removeAttribute( final Object key ) { 260 if (attributes != null) { 261 attributes.remove(key); 262 } 263 } 264 265 266 /** Get attribute of an instruction handle. 267 * 268 * @param key the key object to store/retrieve the attribute 269 */ 270 public Object getAttribute( final Object key ) { 271 if (attributes != null) { 272 return attributes.get(key); 273 } 274 return null; 275 } 276 277 278 /** @return all attributes associated with this handle 279 */ 280 public Collection<Object> getAttributes() { 281 if (attributes == null) { 282 attributes = new HashMap<>(3); 283 } 284 return attributes.values(); 285 } 286 287 288 /** Convenience method, simply calls accept() on the contained instruction. 289 * 290 * @param v Visitor object 291 */ 292 public void accept( final Visitor v ) { 293 instruction.accept(v); 294 } 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 /** 308 * @param prev the prev to set 309 * @ since 6.0 310 */ 311 final InstructionHandle setPrev(final InstructionHandle prev) { 312 this.prev = prev; 313 return prev; 314 } 315}