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 */
017package org.apache.bcel.generic;
018
019import java.io.DataOutputStream;
020import java.io.IOException;
021
022import org.apache.bcel.Const;
023import org.apache.bcel.util.ByteSequence;
024
025/**
026 * Abstract super class for instructions dealing with local variables.
027 */
028public abstract class LocalVariableInstruction extends Instruction implements TypedInstruction, IndexedInstruction {
029
030    /**
031     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
032     */
033    @Deprecated
034    protected int n = -1; // index of referenced variable
035
036    private short cTag = -1; // compact version, such as ILOAD_0
037    private short canonTag = -1; // canonical tag such as ILOAD
038
039    /**
040     * Empty constructor needed for Instruction.readInstruction. Also used by IINC()!
041     */
042    LocalVariableInstruction() {
043    }
044
045    /**
046     * Empty constructor needed for Instruction.readInstruction. Not to be used otherwise. tag and length are defined in
047     * readInstruction and initFromFile, respectively.
048     */
049    LocalVariableInstruction(final short canonTag, final short cTag) {
050        this.canonTag = canonTag;
051        this.cTag = cTag;
052    }
053
054    /**
055     * @param opcode Instruction opcode
056     * @param cTag Instruction number for compact version, ALOAD_0, e.g.
057     * @param n local variable index (unsigned short)
058     */
059    protected LocalVariableInstruction(final short opcode, final short cTag, final int n) {
060        super(opcode, (short) 2);
061        this.cTag = cTag;
062        canonTag = opcode;
063        setIndex(n);
064    }
065
066    /**
067     * Dump instruction as byte code to stream out.
068     *
069     * @param out Output stream
070     */
071    @Override
072    public void dump(final DataOutputStream out) throws IOException {
073        if (wide()) {
074            out.writeByte(Const.WIDE);
075        }
076        out.writeByte(super.getOpcode());
077        if (super.getLength() > 1) { // Otherwise ILOAD_n, instruction, e.g.
078            if (wide()) {
079                out.writeShort(n);
080            } else {
081                out.writeByte(n);
082            }
083        }
084    }
085
086    /**
087     * @return canonical tag for instruction, e.g., ALOAD for ALOAD_0
088     */
089    public short getCanonicalTag() {
090        return canonTag;
091    }
092
093    /**
094     * @return local variable index (n) referred by this instruction.
095     */
096    @Override
097    public final int getIndex() {
098        return n;
099    }
100
101    /**
102     * Returns the type associated with the instruction - in case of ALOAD or ASTORE Type.OBJECT is returned. This is just a
103     * bit incorrect, because ALOAD and ASTORE may work on every ReferenceType (including Type.NULL) and ASTORE may even
104     * work on a ReturnaddressType .
105     *
106     * @return type associated with the instruction
107     */
108    @Override
109    public Type getType(final ConstantPoolGen cp) {
110        switch (canonTag) {
111        case Const.ILOAD:
112        case Const.ISTORE:
113            return Type.INT;
114        case Const.LLOAD:
115        case Const.LSTORE:
116            return Type.LONG;
117        case Const.DLOAD:
118        case Const.DSTORE:
119            return Type.DOUBLE;
120        case Const.FLOAD:
121        case Const.FSTORE:
122            return Type.FLOAT;
123        case Const.ALOAD:
124        case Const.ASTORE:
125            return Type.OBJECT;
126        default:
127            throw new ClassGenException("Unknown case in switch" + canonTag);
128        }
129    }
130
131    /**
132     * Read needed data (e.g. index) from file.
133     *
134     * <pre>
135     * (ILOAD &lt;= tag &lt;= ALOAD_3) || (ISTORE &lt;= tag &lt;= ASTORE_3)
136     * </pre>
137     */
138    @Override
139    protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException {
140        if (wide) {
141            n = bytes.readUnsignedShort();
142            super.setLength(4);
143        } else {
144            final short opcode = super.getOpcode();
145            if (opcode >= Const.ILOAD && opcode <= Const.ALOAD || opcode >= Const.ISTORE && opcode <= Const.ASTORE) {
146                n = bytes.readUnsignedByte();
147                super.setLength(2);
148            } else {
149                if (opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2
150                    n = (opcode - Const.ILOAD_0) % 4;
151                } else { // Assert ISTORE_0 <= tag <= ASTORE_3
152                    n = (opcode - Const.ISTORE_0) % 4;
153                }
154                super.setLength(1);
155            }
156        }
157    }
158
159    /**
160     * Sets the local variable index. also updates opcode and length TODO Why?
161     *
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     * Sets the index of the referenced variable (n) only
186     *
187     * @since 6.0
188     * @see #setIndex(int)
189     */
190    final void setIndexOnly(final int n) {
191        this.n = n;
192    }
193
194    /**
195     * Long output format:
196     *
197     * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]" "("&lt;length of instruction&gt;")" "&lt;"&lt; local variable
198     * index&gt;"&gt;"
199     *
200     * @param verbose long/short format switch
201     * @return mnemonic for instruction
202     */
203    @Override
204    public String toString(final boolean verbose) {
205        final short opcode = super.getOpcode();
206        if (opcode >= Const.ILOAD_0 && opcode <= Const.ALOAD_3 || opcode >= Const.ISTORE_0 && opcode <= Const.ASTORE_3) {
207            return super.toString(verbose);
208        }
209        return super.toString(verbose) + " " + n;
210    }
211
212    private boolean wide() {
213        return n > Const.MAX_BYTE;
214    }
215}