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