001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.bcel.generic;
020
021import java.io.DataOutputStream;
022import java.io.IOException;
023
024import org.apache.bcel.Const;
025import org.apache.bcel.util.ByteSequence;
026
027/**
028 * Abstract superclass for instructions dealing with local variables.
029 */
030public abstract class LocalVariableInstruction extends Instruction implements TypedInstruction, IndexedInstruction {
031
032    /**
033     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
034     */
035    @Deprecated
036    protected int n = -1; // index of referenced variable
037
038    private short cTag = -1; // compact version, such as ILOAD_0
039    private short canonTag = -1; // canonical tag such as ILOAD
040
041    /**
042     * Empty constructor needed for Instruction.readInstruction. Also used by IINC()!
043     */
044    LocalVariableInstruction() {
045    }
046
047    /**
048     * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. tag and length are defined in
049     * readInstruction and initFromFile, respectively.
050     */
051    LocalVariableInstruction(final short canonTag, final short cTag) {
052        this.canonTag = canonTag;
053        this.cTag = cTag;
054    }
055
056    /**
057     * Constructs a LocalVariableInstruction.
058     *
059     * @param opcode Instruction opcode.
060     * @param cTag Instruction number for compact version, ALOAD_0, for example.
061     * @param n local variable index (unsigned short).
062     */
063    protected LocalVariableInstruction(final short opcode, final short cTag, final int n) {
064        super(opcode, (short) 2);
065        this.cTag = cTag;
066        canonTag = opcode;
067        setIndex(n);
068    }
069
070    /**
071     * Dumps instruction as byte code to stream out.
072     *
073     * @param out Output stream.
074     */
075    @Override
076    public void dump(final DataOutputStream out) throws IOException {
077        if (wide()) {
078            out.writeByte(Const.WIDE);
079        }
080        out.writeByte(super.getOpcode());
081        if (super.getLength() > 1) { // Otherwise ILOAD_n, instruction, for example.
082            if (wide()) {
083                out.writeShort(n);
084            } else {
085                out.writeByte(n);
086            }
087        }
088    }
089
090    /**
091     * Gets the canonical tag for instruction, for example, ALOAD for ALOAD_0.
092     *
093     * @return canonical tag for instruction, for example, ALOAD for ALOAD_0.
094     */
095    public short getCanonicalTag() {
096        return canonTag;
097    }
098
099    /**
100     * Gets the local variable index (n) referred to by this instruction.
101     *
102     * @return local variable index (n) referred by this instruction.
103     */
104    @Override
105    public final int getIndex() {
106        return n;
107    }
108
109    /**
110     * Gets the type associated with the instruction - in case of ALOAD or ASTORE Type.OBJECT is returned. This is just a
111     * bit incorrect, because ALOAD and ASTORE may work on every ReferenceType (including Type.NULL) and ASTORE may even
112     * work on a ReturnaddressType.
113     *
114     * @return type associated with the instruction.
115     */
116    @Override
117    public Type getType(final ConstantPoolGen cp) {
118        switch (canonTag) {
119        case Const.ILOAD:
120        case Const.ISTORE:
121            return Type.INT;
122        case Const.LLOAD:
123        case Const.LSTORE:
124            return Type.LONG;
125        case Const.DLOAD:
126        case Const.DSTORE:
127            return Type.DOUBLE;
128        case Const.FLOAD:
129        case Const.FSTORE:
130            return Type.FLOAT;
131        case Const.ALOAD:
132        case Const.ASTORE:
133            return Type.OBJECT;
134        default:
135            throw new ClassGenException("Unknown case in switch" + canonTag);
136        }
137    }
138
139    /**
140     * Reads needed data (for example index) from file.
141     *
142     * <pre>
143     * (ILOAD &lt;= tag &lt;= ALOAD_3) || (ISTORE &lt;= tag &lt;= ASTORE_3)
144     * </pre>
145     */
146    @Override
147    protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException {
148        if (wide) {
149            n = bytes.readUnsignedShort();
150            // dump() emits the wide prefix only when the index needs it, so match that width here.
151            super.setLength(wide() ? 4 : 2);
152        } else {
153            final short opcode = super.getOpcode();
154            if (opcode >= Const.ILOAD && opcode <= Const.ALOAD || opcode >= Const.ISTORE && opcode <= Const.ASTORE) {
155                n = bytes.readUnsignedByte();
156                super.setLength(2);
157            } else {
158                if (opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2
159                    n = (opcode - Const.ILOAD_0) % 4;
160                } else { // Assert ISTORE_0 <= tag <= ASTORE_3
161                    n = (opcode - Const.ISTORE_0) % 4;
162                }
163                super.setLength(1);
164            }
165        }
166    }
167
168    /**
169     * Sets the local variable index. also updates opcode and length TODO Why?
170     *
171     * @param index local variable index (unsigned short).
172     * @throws ClassGenException Thrown if index is out of bounds.
173     * @see #setIndexOnly(int)
174     */
175    @Override
176    public void setIndex(final int index) { // TODO could be package-protected?
177        if (!isNonNegativeUShort(index)) {
178            throw new ClassGenException("Illegal value: " + index);
179        }
180        this.n = index;
181        // Cannot be < 0 as this is checked above
182        if (index <= 3) { // Use more compact instruction xLOAD_n
183            super.setOpcode((short) (cTag + index));
184            super.setLength(1);
185        } else {
186            super.setOpcode(canonTag);
187            if (wide()) {
188                super.setLength(4);
189            } else {
190                super.setLength(2);
191            }
192        }
193    }
194
195    /**
196     * Sets the index of the referenced variable (n) only
197     *
198     * @since 6.0
199     * @see #setIndex(int)
200     */
201    final void setIndexOnly(final int n) {
202        this.n = n;
203    }
204
205    /**
206     * Long output format:
207     *
208     * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]" "("&lt;length of instruction&gt;")" "&lt;"&lt; local variable
209     * index&gt;"&gt;"
210     *
211     * @param verbose long/short format switch.
212     * @return mnemonic for instruction.
213     */
214    @Override
215    public String toString(final boolean verbose) {
216        final short opcode = super.getOpcode();
217        if (opcode >= Const.ILOAD_0 && opcode <= Const.ALOAD_3 || opcode >= Const.ISTORE_0 && opcode <= Const.ASTORE_3) {
218            return super.toString(verbose);
219        }
220        return super.toString(verbose) + " " + n;
221    }
222
223    private boolean wide() {
224        return n > Const.MAX_BYTE;
225    }
226}