View Javadoc
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 &lt;= tag &lt;= ALOAD_3) || (ISTORE &lt;= tag &lt;= 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             super.setLength(4);
149         } else {
150             final short opcode = super.getOpcode();
151             if (opcode >= Const.ILOAD && opcode <= Const.ALOAD || opcode >= Const.ISTORE && opcode <= Const.ASTORE) {
152                 n = bytes.readUnsignedByte();
153                 super.setLength(2);
154             } else {
155                 if (opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2
156                     n = (opcode - Const.ILOAD_0) % 4;
157                 } else { // Assert ISTORE_0 <= tag <= ASTORE_3
158                     n = (opcode - Const.ISTORE_0) % 4;
159                 }
160                 super.setLength(1);
161             }
162         }
163     }
164 
165     /**
166      * Sets the local variable index. also updates opcode and length TODO Why?
167      *
168      * @see #setIndexOnly(int)
169      */
170     @Override
171     public void setIndex(final int n) { // TODO could be package-protected?
172         if (n < 0 || n > Const.MAX_SHORT) {
173             throw new ClassGenException("Illegal value: " + n);
174         }
175         this.n = n;
176         // Cannot be < 0 as this is checked above
177         if (n <= 3) { // Use more compact instruction xLOAD_n
178             super.setOpcode((short) (cTag + n));
179             super.setLength(1);
180         } else {
181             super.setOpcode(canonTag);
182             if (wide()) {
183                 super.setLength(4);
184             } else {
185                 super.setLength(2);
186             }
187         }
188     }
189 
190     /**
191      * Sets the index of the referenced variable (n) only
192      *
193      * @since 6.0
194      * @see #setIndex(int)
195      */
196     final void setIndexOnly(final int n) {
197         this.n = n;
198     }
199 
200     /**
201      * Long output format:
202      *
203      * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]" "("&lt;length of instruction&gt;")" "&lt;"&lt; local variable
204      * index&gt;"&gt;"
205      *
206      * @param verbose long/short format switch.
207      * @return mnemonic for instruction.
208      */
209     @Override
210     public String toString(final boolean verbose) {
211         final short opcode = super.getOpcode();
212         if (opcode >= Const.ILOAD_0 && opcode <= Const.ALOAD_3 || opcode >= Const.ISTORE_0 && opcode <= Const.ASTORE_3) {
213             return super.toString(verbose);
214         }
215         return super.toString(verbose) + " " + n;
216     }
217 
218     private boolean wide() {
219         return n > Const.MAX_BYTE;
220     }
221 }