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   */
18  package org.apache.bcel.generic;
19  
20  import java.io.DataOutputStream;
21  import java.io.IOException;
22  
23  import org.apache.bcel.Const;
24  import org.apache.bcel.util.ByteSequence;
25  
26  /**
27   * Abstract super class for instructions dealing with local variables.
28   *
29   */
30  public abstract class LocalVariableInstruction extends Instruction implements TypedInstruction,
31          IndexedInstruction {
32  
33      /**
34       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
35       */
36      @Deprecated
37      protected int n = -1; // index of referenced variable
38  
39      private short cTag = -1; // compact version, such as ILOAD_0
40      private short canonTag = -1; // canonical tag such as ILOAD
41  
42  
43      private boolean wide() {
44          return n > Const.MAX_BYTE;
45      }
46  
47  
48      /**
49       * Empty constructor needed for Instruction.readInstruction.
50       * Not to be used otherwise.
51       * tag and length are defined in readInstruction and initFromFile, respectively.
52       */
53      LocalVariableInstruction(final short canon_tag, final short c_tag) {
54          super();
55          this.canonTag = canon_tag;
56          this.cTag = c_tag;
57      }
58  
59  
60      /**
61       * Empty constructor needed for Instruction.readInstruction.
62       * Also used by IINC()!
63       */
64      LocalVariableInstruction() {
65      }
66  
67  
68      /**
69       * @param opcode Instruction opcode
70       * @param cTag Instruction number for compact version, ALOAD_0, e.g.
71       * @param n local variable index (unsigned short)
72       */
73      protected LocalVariableInstruction(final short opcode, final short cTag, final int n) {
74          super(opcode, (short) 2);
75          this.cTag = cTag;
76          canonTag = opcode;
77          setIndex(n);
78      }
79  
80  
81      /**
82       * Dump instruction as byte code to stream out.
83       * @param out Output stream
84       */
85      @Override
86      public void dump( final DataOutputStream out ) throws IOException {
87          if (wide()) {
88              out.writeByte(Const.WIDE);
89          }
90          out.writeByte(super.getOpcode());
91          if (super.getLength() > 1) { // Otherwise ILOAD_n, instruction, e.g.
92              if (wide()) {
93                  out.writeShort(n);
94              } else {
95                  out.writeByte(n);
96              }
97          }
98      }
99  
100 
101     /**
102      * Long output format:
103      *
104      * <name of opcode> "["<opcode number>"]"
105      * "("<length of instruction>")" "<"< local variable index>">"
106      *
107      * @param verbose long/short format switch
108      * @return mnemonic for instruction
109      */
110     @Override
111     public String toString( final boolean verbose ) {
112         final short _opcode = super.getOpcode();
113         if (((_opcode >= Const.ILOAD_0) && (_opcode <= Const.ALOAD_3))
114          || ((_opcode >= Const.ISTORE_0) && (_opcode <= Const.ASTORE_3))) {
115             return super.toString(verbose);
116         }
117         return super.toString(verbose) + " " + n;
118     }
119 
120 
121     /**
122      * Read needed data (e.g. index) from file.
123      * <pre>
124      * (ILOAD &lt;= tag &lt;= ALOAD_3) || (ISTORE &lt;= tag &lt;= ASTORE_3)
125      * </pre>
126      */
127     @Override
128     protected void initFromFile( final ByteSequence bytes, final boolean wide ) throws IOException {
129         if (wide) {
130             n = bytes.readUnsignedShort();
131             super.setLength(4);
132         } else {
133             final short _opcode = super.getOpcode();
134             if (((_opcode >= Const.ILOAD) && (_opcode <= Const.ALOAD))
135              || ((_opcode >= Const.ISTORE) && (_opcode <= Const.ASTORE))) {
136                 n = bytes.readUnsignedByte();
137                 super.setLength(2);
138             } else if (_opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2
139                 n = (_opcode - Const.ILOAD_0) % 4;
140                 super.setLength(1);
141             } else { // Assert ISTORE_0 <= tag <= ASTORE_3
142                 n = (_opcode - Const.ISTORE_0) % 4;
143                 super.setLength(1);
144             }
145         }
146     }
147 
148 
149     /**
150      * @return local variable index (n) referred by this instruction.
151      */
152     @Override
153     public final int getIndex() {
154         return n;
155     }
156 
157 
158     /**
159      * Set the local variable index.
160      * also updates opcode and length
161      * TODO Why?
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     /** @return canonical tag for instruction, e.g., ALOAD for ALOAD_0
186      */
187     public short getCanonicalTag() {
188         return canonTag;
189     }
190 
191 
192     /**
193      * Returns the type associated with the instruction -
194      * in case of ALOAD or ASTORE Type.OBJECT is returned.
195      * This is just a bit incorrect, because ALOAD and ASTORE
196      * may work on every ReferenceType (including Type.NULL) and
197      * ASTORE may even work on a ReturnaddressType .
198      * @return type associated with the instruction
199      */
200     @Override
201     public Type getType( final ConstantPoolGen cp ) {
202         switch (canonTag) {
203             case Const.ILOAD:
204             case Const.ISTORE:
205                 return Type.INT;
206             case Const.LLOAD:
207             case Const.LSTORE:
208                 return Type.LONG;
209             case Const.DLOAD:
210             case Const.DSTORE:
211                 return Type.DOUBLE;
212             case Const.FLOAD:
213             case Const.FSTORE:
214                 return Type.FLOAT;
215             case Const.ALOAD:
216             case Const.ASTORE:
217                 return Type.OBJECT;
218             default:
219                 throw new ClassGenException("Unknown case in switch" + canonTag);
220         }
221     }
222 
223     /**
224      * Sets the index of the referenced variable (n) only
225      * @since 6.0
226      * @see #setIndex(int)
227      */
228     final void setIndexOnly(final int n) {
229         this.n = n;
230     }
231 }