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