1 /*
2  * Copyright (c) 2017, 2020, 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
36  * around all instructions, i.e., it implements a cell in a
37  * doubly-linked list. From the outside only the next and the
38  * previous instruction (handle) are accessible. One
39  * can traverse the list via an Enumeration returned by
40  * InstructionList.elements().
41  *
42  * @see Instruction
43  * @see BranchHandle
44  * @see InstructionList
45  * @LastModified: Jan 2020
46  */
47 public class InstructionHandle {
48 
49     private InstructionHandle next;
50     private InstructionHandle prev;
51     private Instruction instruction;
52 
53     private int i_position = -1; // byte code offset of instruction
54 
55     private Set<InstructionTargeter> targeters;
56     private Map<Object, Object> attributes;
57 
58 
59     /**
60      * Does nothing.
61      *
62      * @deprecated Does nothing as of 6.3.1.
63      */
64     @Deprecated
addHandle()65     protected void addHandle() {
66         // noop
67     }
68 
getNext()69     public final InstructionHandle getNext() {
70         return next;
71     }
72 
73 
getPrev()74     public final InstructionHandle getPrev() {
75         return prev;
76     }
77 
78 
getInstruction()79     public final Instruction getInstruction() {
80         return instruction;
81     }
82 
83 
84     /**
85      * Replace current instruction contained in this handle.
86      * Old instruction is disposed using Instruction.dispose().
87      */
setInstruction( final Instruction i )88     public void setInstruction( final Instruction i ) { // Overridden in BranchHandle TODO could be package-protected?
89         if (i == null) {
90             throw new ClassGenException("Assigning null to handle");
91         }
92         if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) {
93             throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
94         }
95         if (instruction != null) {
96             instruction.dispose();
97         }
98         instruction = i;
99     }
100 
101 
102     /**
103      * Temporarily swap the current instruction, without disturbing
104      * anything. Meant to be used by a debugger, implementing
105      * breakpoints. Current instruction is returned.
106      * <p>
107      * Warning: if this is used on a BranchHandle then some methods such as
108      * getPosition() will still refer to the original cached instruction, whereas
109      * other BH methods may affect the cache and the replacement instruction.
110      */
111     // See BCEL-273
112     // TODO remove this method in any redesign of BCEL
swapInstruction( final Instruction i )113     public Instruction swapInstruction( final Instruction i ) {
114         final Instruction oldInstruction = instruction;
115         instruction = i;
116         return oldInstruction;
117     }
118 
119 
InstructionHandle(final Instruction i)120     /*private*/protected InstructionHandle(final Instruction i) {
121         setInstruction(i);
122     }
123 
124     /** Factory method.
125      */
getInstructionHandle( final Instruction i )126     static InstructionHandle getInstructionHandle( final Instruction i ) {
127         return new InstructionHandle(i);
128     }
129 
130 
131     /**
132      * Called by InstructionList.setPositions when setting the position for every
133      * instruction. In the presence of variable length instructions `setPositions()'
134      * performs multiple passes over the instruction list to calculate the
135      * correct (byte) positions and offsets by calling this function.
136      *
137      * @param offset additional offset caused by preceding (variable length) instructions
138      * @param max_offset the maximum offset that may be caused by these instructions
139      * @return additional offset caused by possible change of this instruction's length
140      */
updatePosition( final int offset, final int max_offset )141     protected int updatePosition( final int offset, final int max_offset ) {
142         i_position += offset;
143         return 0;
144     }
145 
146 
147     /** @return the position, i.e., the byte code offset of the contained
148      * instruction. This is accurate only after
149      * InstructionList.setPositions() has been called.
150      */
getPosition()151     public int getPosition() {
152         return i_position;
153     }
154 
155 
156     /** Set the position, i.e., the byte code offset of the contained
157      * instruction.
158      */
setPosition( final int pos )159     void setPosition( final int pos ) {
160         i_position = pos;
161     }
162 
163 
164     /**
165      * Delete contents, i.e., remove user access.
166      */
dispose()167     void dispose() {
168         next = prev = null;
169         instruction.dispose();
170         instruction = null;
171         i_position = -1;
172         attributes = null;
173         removeAllTargeters();
174     }
175 
176 
177     /** Remove all targeters, if any.
178      */
removeAllTargeters()179     public void removeAllTargeters() {
180         if (targeters != null) {
181             targeters.clear();
182         }
183     }
184 
185 
186     /**
187      * Denote this handle isn't referenced anymore by t.
188      */
removeTargeter( final InstructionTargeter t )189     public void removeTargeter( final InstructionTargeter t ) {
190         if (targeters != null) {
191             targeters.remove(t);
192         }
193     }
194 
195 
196     /**
197      * Denote this handle is being referenced by t.
198      */
addTargeter( final InstructionTargeter t )199     public void addTargeter( final InstructionTargeter t ) {
200         if (targeters == null) {
201             targeters = new HashSet<>();
202         }
203         //if(!targeters.contains(t))
204         targeters.add(t);
205     }
206 
207 
hasTargeters()208     public boolean hasTargeters() {
209         return (targeters != null) && (targeters.size() > 0);
210     }
211 
212 
213     /**
214      * @return null, if there are no targeters
215      */
getTargeters()216     public InstructionTargeter[] getTargeters() {
217         if (!hasTargeters()) {
218             return new InstructionTargeter[0];
219         }
220         final InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
221         targeters.toArray(t);
222         return t;
223     }
224 
225 
226     /** @return a (verbose) string representation of the contained instruction.
227      */
toString( final boolean verbose )228     public String toString( final boolean verbose ) {
229         return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
230     }
231 
232 
233     /** @return a string representation of the contained instruction.
234      */
235     @Override
toString()236     public String toString() {
237         return toString(true);
238     }
239 
240 
241     /** Add an attribute to an instruction handle.
242      *
243      * @param key the key object to store/retrieve the attribute
244      * @param attr the attribute to associate with this handle
245      */
addAttribute( final Object key, final Object attr )246     public void addAttribute( final Object key, final Object attr ) {
247         if (attributes == null) {
248             attributes = new HashMap<>(3);
249         }
250         attributes.put(key, attr);
251     }
252 
253 
254     /** Delete an attribute of an instruction handle.
255      *
256      * @param key the key object to retrieve the attribute
257      */
removeAttribute( final Object key )258     public void removeAttribute( final Object key ) {
259         if (attributes != null) {
260             attributes.remove(key);
261         }
262     }
263 
264 
265     /** Get attribute of an instruction handle.
266      *
267      * @param key the key object to store/retrieve the attribute
268      */
getAttribute( final Object key )269     public Object getAttribute( final Object key ) {
270         if (attributes != null) {
271             return attributes.get(key);
272         }
273         return null;
274     }
275 
276 
277     /** @return all attributes associated with this handle
278      */
getAttributes()279     public Collection<Object> getAttributes() {
280         if (attributes == null) {
281             attributes = new HashMap<>(3);
282         }
283         return attributes.values();
284     }
285 
286 
287     /** Convenience method, simply calls accept() on the contained instruction.
288      *
289      * @param v Visitor object
290      */
accept( final Visitor v )291     public void accept( final Visitor v ) {
292         instruction.accept(v);
293     }
294 
295 
296     /**
297      * @param next the next to set
298      * @ since 6.0
299      */
setNext(final InstructionHandle next)300     final InstructionHandle setNext(final InstructionHandle next) {
301         this.next = next;
302         return next;
303     }
304 
305 
306     /**
307      * @param prev the prev to set
308      * @ since 6.0
309      */
setPrev(final InstructionHandle prev)310     final InstructionHandle setPrev(final InstructionHandle prev) {
311         this.prev = prev;
312         return prev;
313     }
314 }
315