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