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