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