1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.bcel.generic;
20
21 import java.io.DataOutputStream;
22 import java.io.IOException;
23
24 import org.apache.bcel.Const;
25 import org.apache.bcel.util.ByteSequence;
26
27 /**
28 * Abstract super class for instructions dealing with local variables.
29 */
30 public abstract class LocalVariableInstruction extends Instruction implements TypedInstruction, IndexedInstruction {
31
32 /**
33 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
34 */
35 @Deprecated
36 protected int n = -1; // index of referenced variable
37
38 private short cTag = -1; // compact version, such as ILOAD_0
39 private short canonTag = -1; // canonical tag such as ILOAD
40
41 /**
42 * Empty constructor needed for Instruction.readInstruction. Also used by IINC()!
43 */
44 LocalVariableInstruction() {
45 }
46
47 /**
48 * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. tag and length are defined in
49 * readInstruction and initFromFile, respectively.
50 */
51 LocalVariableInstruction(final short canonTag, final short cTag) {
52 this.canonTag = canonTag;
53 this.cTag = cTag;
54 }
55
56 /**
57 * Constructs a LocalVariableInstruction.
58 *
59 * @param opcode Instruction opcode.
60 * @param cTag Instruction number for compact version, ALOAD_0, for example.
61 * @param n local variable index (unsigned short).
62 */
63 protected LocalVariableInstruction(final short opcode, final short cTag, final int n) {
64 super(opcode, (short) 2);
65 this.cTag = cTag;
66 canonTag = opcode;
67 setIndex(n);
68 }
69
70 /**
71 * Dumps instruction as byte code to stream out.
72 *
73 * @param out Output stream.
74 */
75 @Override
76 public void dump(final DataOutputStream out) throws IOException {
77 if (wide()) {
78 out.writeByte(Const.WIDE);
79 }
80 out.writeByte(super.getOpcode());
81 if (super.getLength() > 1) { // Otherwise ILOAD_n, instruction, for example.
82 if (wide()) {
83 out.writeShort(n);
84 } else {
85 out.writeByte(n);
86 }
87 }
88 }
89
90 /**
91 * Gets the canonical tag for instruction, for example, ALOAD for ALOAD_0.
92 *
93 * @return canonical tag for instruction, for example, ALOAD for ALOAD_0.
94 */
95 public short getCanonicalTag() {
96 return canonTag;
97 }
98
99 /**
100 * @return local variable index (n) referred by this instruction.
101 */
102 @Override
103 public final int getIndex() {
104 return n;
105 }
106
107 /**
108 * Returns the type associated with the instruction - in case of ALOAD or ASTORE Type.OBJECT is returned. This is just a
109 * bit incorrect, because ALOAD and ASTORE may work on every ReferenceType (including Type.NULL) and ASTORE may even
110 * work on a ReturnaddressType.
111 *
112 * @return type associated with the instruction.
113 */
114 @Override
115 public Type getType(final ConstantPoolGen cp) {
116 switch (canonTag) {
117 case Const.ILOAD:
118 case Const.ISTORE:
119 return Type.INT;
120 case Const.LLOAD:
121 case Const.LSTORE:
122 return Type.LONG;
123 case Const.DLOAD:
124 case Const.DSTORE:
125 return Type.DOUBLE;
126 case Const.FLOAD:
127 case Const.FSTORE:
128 return Type.FLOAT;
129 case Const.ALOAD:
130 case Const.ASTORE:
131 return Type.OBJECT;
132 default:
133 throw new ClassGenException("Unknown case in switch" + canonTag);
134 }
135 }
136
137 /**
138 * Reads needed data (for example index) from file.
139 *
140 * <pre>
141 * (ILOAD <= tag <= ALOAD_3) || (ISTORE <= tag <= ASTORE_3)
142 * </pre>
143 */
144 @Override
145 protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException {
146 if (wide) {
147 n = bytes.readUnsignedShort();
148 // dump() emits the wide prefix only when the index needs it, so match that width here.
149 super.setLength(wide() ? 4 : 2);
150 } else {
151 final short opcode = super.getOpcode();
152 if (opcode >= Const.ILOAD && opcode <= Const.ALOAD || opcode >= Const.ISTORE && opcode <= Const.ASTORE) {
153 n = bytes.readUnsignedByte();
154 super.setLength(2);
155 } else {
156 if (opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2
157 n = (opcode - Const.ILOAD_0) % 4;
158 } else { // Assert ISTORE_0 <= tag <= ASTORE_3
159 n = (opcode - Const.ISTORE_0) % 4;
160 }
161 super.setLength(1);
162 }
163 }
164 }
165
166 /**
167 * Sets the local variable index. also updates opcode and length TODO Why?
168 *
169 * @param index local variable index (unsigned short).
170 * @throws ClassGenException Thrown if index is out of bounds.
171 * @see #setIndexOnly(int)
172 */
173 @Override
174 public void setIndex(final int index) { // TODO could be package-protected?
175 if (!isNonNegativeUShort(index)) {
176 throw new ClassGenException("Illegal value: " + index);
177 }
178 this.n = index;
179 // Cannot be < 0 as this is checked above
180 if (index <= 3) { // Use more compact instruction xLOAD_n
181 super.setOpcode((short) (cTag + index));
182 super.setLength(1);
183 } else {
184 super.setOpcode(canonTag);
185 if (wide()) {
186 super.setLength(4);
187 } else {
188 super.setLength(2);
189 }
190 }
191 }
192
193 /**
194 * Sets the index of the referenced variable (n) only
195 *
196 * @since 6.0
197 * @see #setIndex(int)
198 */
199 final void setIndexOnly(final int n) {
200 this.n = n;
201 }
202
203 /**
204 * Long output format:
205 *
206 * <name of opcode> "["<opcode number>"]" "("<length of instruction>")" "<"< local variable
207 * index>">"
208 *
209 * @param verbose long/short format switch.
210 * @return mnemonic for instruction.
211 */
212 @Override
213 public String toString(final boolean verbose) {
214 final short opcode = super.getOpcode();
215 if (opcode >= Const.ILOAD_0 && opcode <= Const.ALOAD_3 || opcode >= Const.ISTORE_0 && opcode <= Const.ASTORE_3) {
216 return super.toString(verbose);
217 }
218 return super.toString(verbose) + " " + n;
219 }
220
221 private boolean wide() {
222 return n > Const.MAX_BYTE;
223 }
224 }