1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 package com.sun.org.apache.bcel.internal.generic; 21 22 import java.util.Collection; 23 import java.util.HashMap; 24 import java.util.HashSet; 25 import java.util.Map; 26 import java.util.Set; 27 28 import com.sun.org.apache.bcel.internal.classfile.Utility; 29 30 /** 31 * Instances of this class give users a handle to the instructions contained in 32 * an InstructionList. Instruction objects may be used more than once within a 33 * list, this is useful because it saves memory and may be much faster. 34 * 35 * Within an InstructionList an InstructionHandle object is wrapped around all 36 * instructions, i.e., it implements a cell in a doubly-linked list. From the 37 * outside only the next and the previous instruction (handle) are accessible. 38 * One can traverse the list via an Enumeration returned by 39 * InstructionList.elements(). 40 * 41 * @version $Id: InstructionHandle.java 1749603 2016-06-21 20:50:19Z ggregory $ 42 * @see Instruction 43 * @see BranchHandle 44 * @see InstructionList 45 */ 46 public class InstructionHandle { 47 48 private InstructionHandle next; 49 private InstructionHandle prev; 50 private Instruction instruction; 51 52 private int i_position = -1; // byte code offset of instruction 53 54 private Set<InstructionTargeter> targeters; 55 private Map<Object, Object> attributes; 56 57 public final InstructionHandle getNext() { 58 return next; 59 } 60 61 public final InstructionHandle getPrev() { 62 return prev; 63 } 64 65 public final Instruction getInstruction() { 66 return instruction; 67 } 68 69 /** 70 * Replace current instruction contained in this handle. Old instruction is 71 * disposed using Instruction.dispose(). 72 */ 73 public void setInstruction(final Instruction i) { // Overridden in BranchHandle TODO could be package-protected? 74 if (i == null) { 75 throw new ClassGenException("Assigning null to handle"); 76 } 77 if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) { 78 throw new ClassGenException("Assigning branch instruction " + i + " to plain handle"); 79 } 80 if (instruction != null) { 81 instruction.dispose(); 82 } 83 instruction = i; 84 } 85 86 /** 87 * Temporarily swap the current instruction, without disturbing anything. 88 * Meant to be used by a debugger, implementing breakpoints. Current 89 * instruction is returned. 90 * <p> 91 * Warning: if this is used on a BranchHandle then some methods such as 92 * getPosition() will still refer to the original cached instruction, 93 * whereas other BH methods may affect the cache and the replacement 94 * instruction. 95 */ 96 // See BCEL-273 97 // TODO remove this method in any redesign of BCEL 98 public Instruction swapInstruction(final Instruction i) { 99 final Instruction oldInstruction = instruction; 100 instruction = i; 101 return oldInstruction; 102 } 103 104 105 /*private*/ 106 protected InstructionHandle(final Instruction i) { 107 setInstruction(i); 108 } 109 110 private static InstructionHandle ih_list = null; // List of reusable handles 111 112 /** 113 * Factory method. 114 */ 115 static InstructionHandle getInstructionHandle(final Instruction i) { 116 if (ih_list == null) { 117 return new InstructionHandle(i); 118 } 119 final InstructionHandle ih = ih_list; 120 ih_list = ih.next; 121 ih.setInstruction(i); 122 return ih; 123 } 124 125 /** 126 * Called by InstructionList.setPositions when setting the position for 127 * every instruction. In the presence of variable length instructions 128 * `setPositions()' performs multiple passes over the instruction list to 129 * calculate the correct (byte) positions and offsets by calling this 130 * function. 131 * 132 * @param offset additional offset caused by preceding (variable length) 133 * instructions 134 * @param max_offset the maximum offset that may be caused by these 135 * instructions 136 * @return additional offset caused by possible change of this instruction's 137 * length 138 */ 139 protected int updatePosition(final int offset, final int max_offset) { 140 i_position += offset; 141 return 0; 142 } 143 144 /** 145 * @return the position, i.e., the byte code offset of the contained 146 * instruction. This is accurate only after InstructionList.setPositions() 147 * has been called. 148 */ 149 public int getPosition() { 150 return i_position; 151 } 152 153 /** 154 * Set the position, i.e., the byte code offset of the contained 155 * instruction. 156 */ 157 void setPosition(final int pos) { 158 i_position = pos; 159 } 160 161 /** 162 * Overridden in BranchHandle 163 */ 164 protected void addHandle() { 165 next = ih_list; 166 ih_list = this; 167 } 168 169 /** 170 * Delete contents, i.e., remove user access and make handle reusable. 171 */ 172 void dispose() { 173 next = prev = null; 174 instruction.dispose(); 175 instruction = null; 176 i_position = -1; 177 attributes = null; 178 removeAllTargeters(); 179 addHandle(); 180 } 181 182 /** 183 * Remove all targeters, if any. 184 */ 185 public void removeAllTargeters() { 186 if (targeters != null) { 187 targeters.clear(); 188 } 189 } 190 191 /** 192 * Denote this handle isn't referenced anymore by t. 193 */ 194 public void removeTargeter(final InstructionTargeter t) { 195 if (targeters != null) { 196 targeters.remove(t); 197 } 198 } 199 200 /** 201 * Denote this handle is being referenced by t. 202 */ 203 public void addTargeter(final InstructionTargeter t) { 204 if (targeters == null) { 205 targeters = new HashSet<>(); 206 } 207 //if(!targeters.contains(t)) 208 targeters.add(t); 209 } 210 211 public boolean hasTargeters() { 212 return (targeters != null) && (targeters.size() > 0); 213 } 214 215 /** 216 * @return null, if there are no targeters 217 */ 218 public InstructionTargeter[] getTargeters() { 219 if (!hasTargeters()) { 220 return new InstructionTargeter[0]; 221 } 222 final InstructionTargeter[] t = new InstructionTargeter[targeters.size()]; 223 targeters.toArray(t); 224 return t; 225 } 226 227 /** 228 * @return a (verbose) string representation of the contained instruction. 229 */ 230 public String toString(final boolean verbose) { 231 return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose); 232 } 233 234 /** 235 * @return a string representation of the contained instruction. 236 */ 237 @Override 238 public String toString() { 239 return toString(true); 240 } 241 242 /** 243 * Add an attribute to an instruction handle. 244 * 245 * @param key the key object to store/retrieve the attribute 246 * @param attr the attribute to associate with this handle 247 */ 248 public void addAttribute(final Object key, final Object attr) { 249 if (attributes == null) { 250 attributes = new HashMap<>(3); 251 } 252 attributes.put(key, attr); 253 } 254 255 /** 256 * Delete an attribute of an instruction handle. 257 * 258 * @param key the key object to retrieve the attribute 259 */ 260 public void removeAttribute(final Object key) { 261 if (attributes != null) { 262 attributes.remove(key); 263 } 264 } 265 266 /** 267 * Get attribute of an instruction handle. 268 * 269 * @param key the key object to store/retrieve the attribute 270 */ 271 public Object getAttribute(final Object key) { 272 if (attributes != null) { 273 return attributes.get(key); 274 } 275 return null; 276 } 277 278 /** 279 * @return all attributes associated with this handle 280 */ 281 public Collection<Object> getAttributes() { 282 if (attributes == null) { 283 attributes = new HashMap<>(3); 284 } 285 return attributes.values(); 286 } 287 288 /** 289 * Convenience method, simply calls accept() on the contained instruction. 290 * 291 * @param v Visitor object 292 */ 293 public void accept(final Visitor v) { 294 instruction.accept(v); 295 } 296 297 /** 298 * @param next the next to set 299 * @ since 6.0 300 */ 301 final InstructionHandle setNext(final InstructionHandle next) { 302 this.next = next; 303 return next; 304 } 305 306 /** 307 * @param prev the prev to set 308 * @ since 6.0 309 */ 310 final InstructionHandle setPrev(final InstructionHandle prev) { 311 this.prev = prev; 312 return prev; 313 } 314 }