    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     * Set 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}