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.io.DataOutputStream;
23 import java.io.IOException;
24 
25 import com.sun.org.apache.bcel.internal.util.ByteSequence;
26 
27 /**
28  * Abstract super class for branching instructions like GOTO, IFEQ, etc..
29  * Branch instructions may have a variable length, namely GOTO, JSR,
30  * LOOKUPSWITCH and TABLESWITCH.
31  *
32  * @see InstructionList
33  * @LastModified: July 2020
34  */
35 public abstract class BranchInstruction extends Instruction implements InstructionTargeter {
36 
37     private int index; // Branch target relative to this instruction
38     private InstructionHandle target; // Target object in instruction list
39     private int position; // Byte code offset
40 
41     /**
42      * Empty constructor needed for the Class.newInstance() statement in
43      * Instruction.readInstruction(). Not to be used otherwise.
44      */
BranchInstruction()45     BranchInstruction() {
46     }
47 
48 
49     /** Common super constructor
50      * @param opcode Instruction opcode
51      * @param target instruction to branch to
52      */
BranchInstruction(final short opcode, final InstructionHandle target)53     protected BranchInstruction(final short opcode, final InstructionHandle target) {
54         super(opcode, (short) 3);
55         setTarget(target);
56     }
57 
58 
59     /**
60      * Dump instruction as byte code to stream out.
61      * @param out Output stream
62      */
63     @Override
dump( final DataOutputStream out )64     public void dump( final DataOutputStream out ) throws IOException {
65         out.writeByte(super.getOpcode());
66         index = getTargetOffset();
67         if (!isValidShort(index)) {
68             throw new ClassGenException("Branch target offset too large for short: " + index);
69         }
70         out.writeShort(index); // May be negative, i.e., point backwards
71     }
72 
73 
74     /**
75      * @param _target branch target
76      * @return the offset to  `target' relative to this instruction
77      */
getTargetOffset( final InstructionHandle _target )78     protected int getTargetOffset( final InstructionHandle _target ) {
79         if (_target == null) {
80             throw new ClassGenException("Target of " + super.toString(true)
81                     + " is invalid null handle");
82         }
83         final int t = _target.getPosition();
84         if (t < 0) {
85             throw new ClassGenException("Invalid branch target position offset for "
86                     + super.toString(true) + ":" + t + ":" + _target);
87         }
88         return t - position;
89     }
90 
91 
92     /**
93      * @return the offset to this instruction's target
94      */
getTargetOffset()95     protected int getTargetOffset() {
96         return getTargetOffset(target);
97     }
98 
99 
100     /**
101      * Called by InstructionList.setPositions when setting the position for every
102      * instruction. In the presence of variable length instructions `setPositions'
103      * performs multiple passes over the instruction list to calculate the
104      * correct (byte) positions and offsets by calling this function.
105      *
106      * @param offset additional offset caused by preceding (variable length) instructions
107      * @param max_offset the maximum offset that may be caused by these instructions
108      * @return additional offset caused by possible change of this instruction's length
109      */
updatePosition( final int offset, final int max_offset )110     protected int updatePosition( final int offset, final int max_offset ) {
111         position += offset;
112         return 0;
113     }
114 
115 
116     /**
117      * Long output format:
118      *
119      * &lt;position in byte code&gt;
120      * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]"
121      * "("&lt;length of instruction&gt;")"
122      * "&lt;"&lt;target instruction&gt;"&gt;" "@"&lt;branch target offset&gt;
123      *
124      * @param verbose long/short format switch
125      * @return mnemonic for instruction
126      */
127     @Override
toString( final boolean verbose )128     public String toString( final boolean verbose ) {
129         final String s = super.toString(verbose);
130         String t = "null";
131         if (verbose) {
132             if (target != null) {
133                 if (target.getInstruction() == this) {
134                     t = "<points to itself>";
135                 } else if (target.getInstruction() == null) {
136                     t = "<null instruction!!!?>";
137                 } else {
138                     // I'm more interested in the address of the target then
139                     // the instruction located there.
140                     //t = target.getInstruction().toString(false); // Avoid circles
141                     t = "" + target.getPosition();
142                 }
143             }
144         } else {
145             if (target != null) {
146                 index = target.getPosition();
147                 // index = getTargetOffset();  crashes if positions haven't been set
148                 // t = "" + (index + position);
149                 t = "" + index;
150             }
151         }
152         return s + " -> " + t;
153     }
154 
155 
156     /**
157      * Read needed data (e.g. index) from file. Conversion to a InstructionHandle
158      * is done in InstructionList(byte[]).
159      *
160      * @param bytes input stream
161      * @param wide wide prefix?
162      * @see InstructionList
163      */
164     @Override
initFromFile( final ByteSequence bytes, final boolean wide )165     protected void initFromFile( final ByteSequence bytes, final boolean wide ) throws IOException {
166         super.setLength(3);
167         index = bytes.readShort();
168     }
169 
170 
171     /**
172      * @return target offset in byte code
173      */
getIndex()174     public final int getIndex() {
175         return index;
176     }
177 
178 
179     /**
180      * @return target of branch instruction
181      */
getTarget()182     public InstructionHandle getTarget() {
183         return target;
184     }
185 
186 
187     /**
188      * Set branch target
189      * @param target branch target
190      */
setTarget( final InstructionHandle target )191     public void setTarget( final InstructionHandle target ) {
192         notifyTarget(this.target, target, this);
193         this.target = target;
194     }
195 
196 
197     /**
198      * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen, LineNumberGen
199      */
notifyTarget( final InstructionHandle old_ih, final InstructionHandle new_ih, final InstructionTargeter t )200     static void notifyTarget( final InstructionHandle old_ih, final InstructionHandle new_ih,
201             final InstructionTargeter t ) {
202         if (old_ih != null) {
203             old_ih.removeTargeter(t);
204         }
205         if (new_ih != null) {
206             new_ih.addTargeter(t);
207         }
208     }
209 
210 
211     /**
212      * @param old_ih old target
213      * @param new_ih new target
214      */
215     @Override
updateTarget( final InstructionHandle old_ih, final InstructionHandle new_ih )216     public void updateTarget( final InstructionHandle old_ih, final InstructionHandle new_ih ) {
217         if (target == old_ih) {
218             setTarget(new_ih);
219         } else {
220             throw new ClassGenException("Not targeting " + old_ih + ", but " + target);
221         }
222     }
223 
224 
225     /**
226      * @return true, if ih is target of this instruction
227      */
228     @Override
containsTarget( final InstructionHandle ih )229     public boolean containsTarget( final InstructionHandle ih ) {
230         return target == ih;
231     }
232 
233     /**
234      * Updates the opcode. Before changing the opcode, reset the target so that
235      * the old instruction is removed from the HashSet and the new one then added.
236      * @param opcode the opcode
237      */
238     @Override
setOpcode( final short opcode )239     void setOpcode( final short opcode ) {
240         if (target == null) {
241             super.setOpcode(opcode);
242         } else {
243             // reset target before changing the opcode
244             InstructionHandle t = target;
245             setTarget(null);
246             super.setOpcode(opcode);
247             setTarget(t);
248         }
249     }
250 
251     /**
252      * Inform target that it's not targeted anymore.
253      */
254     @Override
dispose()255     void dispose() {
256         setTarget(null);
257         index = -1;
258         position = -1;
259     }
260 
261 
262     /**
263      * @return the position
264      * @since 6.0
265      */
getPosition()266     protected int getPosition() {
267         return position;
268     }
269 
270 
271     /**
272      * @param position the position to set
273      * @since 6.0
274      */
setPosition(final int position)275     protected void setPosition(final int position) {
276         this.position = position;
277     }
278 
279 
280     /**
281      * @param index the index to set
282      * @since 6.0
283      */
setIndex(final int index)284     protected void setIndex(final int index) {
285         this.index = index;
286     }
287 
288 }
289