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