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 * Gets 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 return attributes != null ? attributes.get(key) : null; 140 } 141 142 /** 143 * @return all attributes associated with this handle 144 */ 145 public Collection<Object> getAttributes() { 146 if (attributes == null) { 147 attributes = new HashMap<>(3); 148 } 149 return attributes.values(); 150 } 151 152 public final Instruction getInstruction() { 153 return instruction; 154 } 155 156 public final InstructionHandle getNext() { 157 return next; 158 } 159 160 /** 161 * @return the position, i.e., the byte code offset of the contained instruction. This is accurate only after 162 * InstructionList.setPositions() has been called. 163 */ 164 public int getPosition() { 165 return i_position; 166 } 167 168 public final InstructionHandle getPrev() { 169 return prev; 170 } 171 172 /** 173 * @return null, if there are no targeters 174 */ 175 public InstructionTargeter[] getTargeters() { 176 if (!hasTargeters()) { 177 return EMPTY_INSTRUCTION_TARGETER_ARRAY; 178 } 179 final InstructionTargeter[] t = new InstructionTargeter[targeters.size()]; 180 targeters.toArray(t); 181 return t; 182 } 183 184 public boolean hasTargeters() { 185 return targeters != null && !targeters.isEmpty(); 186 } 187 188 /** 189 * Remove all targeters, if any. 190 */ 191 public void removeAllTargeters() { 192 if (targeters != null) { 193 targeters.clear(); 194 } 195 } 196 197 /** 198 * Delete an attribute of an instruction handle. 199 * 200 * @param key the key object to retrieve the attribute 201 */ 202 public void removeAttribute(final Object key) { 203 if (attributes != null) { 204 attributes.remove(key); 205 } 206 } 207 208 /** 209 * Denote this handle isn't referenced anymore by t. 210 */ 211 public void removeTargeter(final InstructionTargeter t) { 212 if (targeters != null) { 213 targeters.remove(t); 214 } 215 } 216 217 /** 218 * Replace current instruction contained in this handle. Old instruction is disposed using Instruction.dispose(). 219 */ 220 public void setInstruction(final Instruction i) { // Overridden in BranchHandle TODO could be package-protected? 221 if (i == null) { 222 throw new ClassGenException("Assigning null to handle"); 223 } 224 if (this.getClass() != BranchHandle.class && i instanceof BranchInstruction) { 225 throw new ClassGenException("Assigning branch instruction " + i + " to plain handle"); 226 } 227 if (instruction != null) { 228 instruction.dispose(); 229 } 230 instruction = i; 231 } 232 233 /** 234 * @param next the next to set 235 * @since 6.0 236 */ 237 final InstructionHandle setNext(final InstructionHandle next) { 238 this.next = next; 239 return next; 240 } 241 242 /** 243 * Sets the position, i.e., the byte code offset of the contained instruction. 244 */ 245 void setPosition(final int pos) { 246 i_position = pos; 247 } 248 249 /** 250 * @param prev the prev to set 251 * @since 6.0 252 */ 253 final InstructionHandle setPrev(final InstructionHandle prev) { 254 this.prev = prev; 255 return prev; 256 } 257 258 /** 259 * Temporarily swap the current instruction, without disturbing anything. Meant to be used by a debugger, implementing 260 * breakpoints. Current instruction is returned. 261 * <p> 262 * Warning: if this is used on a BranchHandle then some methods such as getPosition() will still refer to the original 263 * cached instruction, whereas other BH methods may affect the cache and the replacement instruction. 264 */ 265 // See BCEL-273 266 // TODO remove this method in any redesign of BCEL 267 public Instruction swapInstruction(final Instruction i) { 268 final Instruction oldInstruction = instruction; 269 instruction = i; 270 return oldInstruction; 271 } 272 273 /** 274 * @return a string representation of the contained instruction. 275 */ 276 @Override 277 public String toString() { 278 return toString(true); 279 } 280 281 /** 282 * @return a (verbose) string representation of the contained instruction. 283 */ 284 public String toString(final boolean verbose) { 285 return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose); 286 } 287 288 /** 289 * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable 290 * length instructions 'setPositions()' performs multiple passes over the instruction list to calculate the correct 291 * (byte) positions and offsets by calling this function. 292 * 293 * @param offset additional offset caused by preceding (variable length) instructions 294 * @param maxOffset the maximum offset that may be caused by these instructions 295 * @return additional offset caused by possible change of this instruction's length 296 */ 297 protected int updatePosition(final int offset, final int maxOffset) { 298 i_position += offset; 299 return 0; 300 } 301}