001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.bcel.generic; 020 021import java.util.Collection; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.Map; 025import java.util.Set; 026 027import org.apache.bcel.classfile.Utility; 028 029/** 030 * Instances of this class give users a handle to the instructions contained in an InstructionList. Instruction objects 031 * may be used more than once within a list, this is useful because it saves memory and may be much faster. 032 * 033 * Within an InstructionList an InstructionHandle object is wrapped around all instructions, that is, it implements a cell 034 * in a doubly-linked list. From the outside only the next and the previous instruction (handle) are accessible. One can 035 * traverse the list via an Enumeration returned by InstructionList.elements(). 036 * 037 * @see Instruction 038 * @see BranchHandle 039 * @see InstructionList 040 */ 041public class InstructionHandle { 042 043 /** 044 * Empty array. 045 * 046 * @since 6.6.0 047 */ 048 public static final InstructionHandle[] EMPTY_ARRAY = {}; 049 050 /** 051 * Empty array. 052 */ 053 static final InstructionTargeter[] EMPTY_INSTRUCTION_TARGETER_ARRAY = {}; 054 055 /** 056 * Factory method. 057 */ 058 static InstructionHandle getInstructionHandle(final Instruction i) { 059 return new InstructionHandle(i); 060 } 061 062 private InstructionHandle next; 063 private InstructionHandle prev; 064 065 private Instruction instruction; 066 067 /** 068 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 069 */ 070 @Deprecated 071 protected int i_position = -1; // byte code offset of instruction 072 private Set<InstructionTargeter> targeters; 073 074 private Map<Object, Object> attributes; 075 076 /** 077 * Constructs an InstructionHandle. 078 * 079 * @param i the instruction. 080 */ 081 protected InstructionHandle(final Instruction i) { 082 setInstruction(i); 083 } 084 085 /** 086 * Convenience method, simply calls accept() on the contained instruction. 087 * 088 * @param v Visitor object. 089 */ 090 public void accept(final Visitor v) { 091 instruction.accept(v); 092 } 093 094 /** 095 * Add an attribute to an instruction handle. 096 * 097 * @param key the key object to store/retrieve the attribute. 098 * @param attr the attribute to associate with this handle. 099 */ 100 public void addAttribute(final Object key, final Object attr) { 101 if (attributes == null) { 102 attributes = new HashMap<>(3); 103 } 104 attributes.put(key, attr); 105 } 106 107 /** 108 * Does nothing. 109 * 110 * @deprecated Does nothing as of 6.3.1. 111 */ 112 @Deprecated 113 protected void addHandle() { 114 // noop 115 } 116 117 /** 118 * Denote this handle is being referenced by t. 119 * 120 * @param t the instruction targeter. 121 */ 122 public void addTargeter(final InstructionTargeter t) { 123 if (targeters == null) { 124 targeters = new HashSet<>(); 125 } 126 // if (!targeters.contains(t)) 127 targeters.add(t); 128 } 129 130 /** 131 * Delete contents, removes user access. 132 */ 133 void dispose() { 134 next = prev = null; 135 instruction.dispose(); 136 instruction = null; 137 i_position = -1; 138 attributes = null; 139 removeAllTargeters(); 140 } 141 142 /** 143 * Gets attribute of an instruction handle. 144 * 145 * @param key the key object to store/retrieve the attribute. 146 * @return the attribute value. 147 */ 148 public Object getAttribute(final Object key) { 149 return attributes != null ? attributes.get(key) : null; 150 } 151 152 /** 153 * Gets all attributes associated with this handle. 154 * 155 * @return all attributes associated with this handle. 156 */ 157 public Collection<Object> getAttributes() { 158 if (attributes == null) { 159 attributes = new HashMap<>(3); 160 } 161 return attributes.values(); 162 } 163 164 /** 165 * Gets the instruction. 166 * 167 * @return the instruction. 168 */ 169 public final Instruction getInstruction() { 170 return instruction; 171 } 172 173 /** 174 * Gets the next instruction handle. 175 * 176 * @return the next instruction handle. 177 */ 178 public final InstructionHandle getNext() { 179 return next; 180 } 181 182 /** 183 * Gets the position. 184 * 185 * @return the position, the byte code offset of the contained instruction. This is accurate only after 186 * InstructionList.setPositions() has been called. 187 */ 188 public int getPosition() { 189 return i_position; 190 } 191 192 /** 193 * Gets the previous instruction handle. 194 * 195 * @return the previous instruction handle. 196 */ 197 public final InstructionHandle getPrev() { 198 return prev; 199 } 200 201 /** 202 * Gets the targeters. 203 * 204 * @return null, if there are no targeters. 205 */ 206 public InstructionTargeter[] getTargeters() { 207 if (!hasTargeters()) { 208 return EMPTY_INSTRUCTION_TARGETER_ARRAY; 209 } 210 final InstructionTargeter[] t = new InstructionTargeter[targeters.size()]; 211 targeters.toArray(t); 212 return t; 213 } 214 215 /** 216 * Checks if this handle has targeters. 217 * 218 * @return true if this handle has targeters, false otherwise. 219 */ 220 public boolean hasTargeters() { 221 return targeters != null && !targeters.isEmpty(); 222 } 223 224 /** 225 * Remove all targeters, if any. 226 */ 227 public void removeAllTargeters() { 228 if (targeters != null) { 229 targeters.clear(); 230 } 231 } 232 233 /** 234 * Delete an attribute of an instruction handle. 235 * 236 * @param key the key object to retrieve the attribute. 237 */ 238 public void removeAttribute(final Object key) { 239 if (attributes != null) { 240 attributes.remove(key); 241 } 242 } 243 244 /** 245 * Denote this handle isn't referenced anymore by t. 246 * 247 * @param t the instruction targeter. 248 */ 249 public void removeTargeter(final InstructionTargeter t) { 250 if (targeters != null) { 251 targeters.remove(t); 252 } 253 } 254 255 /** 256 * Replace current instruction contained in this handle. Old instruction is disposed using Instruction.dispose(). 257 * 258 * @param i the new instruction. 259 */ 260 public void setInstruction(final Instruction i) { // Overridden in BranchHandle TODO could be package-protected? 261 if (i == null) { 262 throw new ClassGenException("Assigning null to handle"); 263 } 264 if (this.getClass() != BranchHandle.class && i instanceof BranchInstruction) { 265 throw new ClassGenException("Assigning branch instruction " + i + " to plain handle"); 266 } 267 if (instruction != null) { 268 instruction.dispose(); 269 } 270 instruction = i; 271 } 272 273 /** 274 * Sets the next instruction handle. 275 * 276 * @param next the next to set. 277 * @return the next instruction handle. 278 * @since 6.0 279 */ 280 final InstructionHandle setNext(final InstructionHandle next) { 281 this.next = next; 282 return next; 283 } 284 285 /** 286 * Sets the position, the byte code offset of the contained instruction. 287 * 288 * @param pos the position. 289 */ 290 void setPosition(final int pos) { 291 i_position = pos; 292 } 293 294 /** 295 * Sets the previous instruction handle. 296 * 297 * @param prev the prev to set. 298 * @return the previous instruction handle. 299 * @since 6.0 300 */ 301 final InstructionHandle setPrev(final InstructionHandle prev) { 302 this.prev = prev; 303 return prev; 304 } 305 306 /** 307 * Temporarily swap the current instruction, without disturbing anything. Meant to be used by a debugger, implementing 308 * breakpoints. Current instruction is returned. 309 * <p> 310 * Warning: if this is used on a BranchHandle then some methods such as getPosition() will still refer to the original 311 * cached instruction, whereas other BH methods may affect the cache and the replacement instruction. 312 * 313 * @param i the replacement instruction. 314 * @return the old instruction. 315 */ 316 // See BCEL-273 317 // TODO remove this method in any redesign of BCEL 318 public Instruction swapInstruction(final Instruction i) { 319 final Instruction oldInstruction = instruction; 320 instruction = i; 321 return oldInstruction; 322 } 323 324 /** 325 * Gets a string representation of the contained instruction. 326 * 327 * @return a string representation of the contained instruction. 328 */ 329 @Override 330 public String toString() { 331 return toString(true); 332 } 333 334 /** 335 * Gets a verbose string representation of the contained instruction. 336 * 337 * @param verbose whether to be verbose. 338 * @return a (verbose) string representation of the contained instruction. 339 */ 340 public String toString(final boolean verbose) { 341 return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose); 342 } 343 344 /** 345 * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable 346 * length instructions 'setPositions()' performs multiple passes over the instruction list to calculate the correct 347 * (byte) positions and offsets by calling this function. 348 * 349 * @param offset additional offset caused by preceding (variable length) instructions. 350 * @param maxOffset the maximum offset that may be caused by these instructions. 351 * @return additional offset caused by possible change of this instruction's length. 352 */ 353 protected int updatePosition(final int offset, final int maxOffset) { 354 i_position += offset; 355 return 0; 356 } 357}