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