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