    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 super class 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     * @return local variable index (n) referred by this instruction.
    101     */
    102    @Override
    103    public final int getIndex() {
    104        return n;
    105    }
    106
    107    /**
    108     * Returns the type associated with the instruction - in case of ALOAD or ASTORE Type.OBJECT is returned. This is just a
    109     * bit incorrect, because ALOAD and ASTORE may work on every ReferenceType (including Type.NULL) and ASTORE may even
    110     * work on a ReturnaddressType.
    111     *
    112     * @return type associated with the instruction.
    113     */
    114    @Override
    115    public Type getType(final ConstantPoolGen cp) {
    116        switch (canonTag) {
    117        case Const.ILOAD:
    118        case Const.ISTORE:
    119            return Type.INT;
    120        case Const.LLOAD:
    121        case Const.LSTORE:
    122            return Type.LONG;
    123        case Const.DLOAD:
    124        case Const.DSTORE:
    125            return Type.DOUBLE;
    126        case Const.FLOAD:
    127        case Const.FSTORE:
    128            return Type.FLOAT;
    129        case Const.ALOAD:
    130        case Const.ASTORE:
    131            return Type.OBJECT;
    132        default:
    133            throw new ClassGenException("Unknown case in switch" + canonTag);
    134        }
    135    }
    136
    137    /**
    138     * Reads needed data (for example index) from file.
    139     *
    140     * <pre>
    141     * (ILOAD &lt;= tag &lt;= ALOAD_3) || (ISTORE &lt;= tag &lt;= ASTORE_3)
    142     * </pre>
    143     */
    144    @Override
    145    protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException {
    146        if (wide) {
    147            n = bytes.readUnsignedShort();
    148            super.setLength(4);
    149        } else {
    150            final short opcode = super.getOpcode();
    151            if (opcode >= Const.ILOAD && opcode <= Const.ALOAD || opcode >= Const.ISTORE && opcode <= Const.ASTORE) {
    152                n = bytes.readUnsignedByte();
    153                super.setLength(2);
    154            } else {
    155                if (opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2
    156                    n = (opcode - Const.ILOAD_0) % 4;
    157                } else { // Assert ISTORE_0 <= tag <= ASTORE_3
    158                    n = (opcode - Const.ISTORE_0) % 4;
    159                }
    160                super.setLength(1);
    161            }
    162        }
    163    }
    164
    165    /**
    166     * Sets the local variable index. also updates opcode and length TODO Why?
    167     *
    168     * @see #setIndexOnly(int)
    169     */
    170    @Override
    171    public void setIndex(final int n) { // TODO could be package-protected?
    172        if (n < 0 || n > Const.MAX_SHORT) {
    173            throw new ClassGenException("Illegal value: " + n);
    174        }
    175        this.n = n;
    176        // Cannot be < 0 as this is checked above
    177        if (n <= 3) { // Use more compact instruction xLOAD_n
    178            super.setOpcode((short) (cTag + n));
    179            super.setLength(1);
    180        } else {
    181            super.setOpcode(canonTag);
    182            if (wide()) {
    183                super.setLength(4);
    184            } else {
    185                super.setLength(2);
    186            }
    187        }
    188    }
    189
    190    /**
    191     * Sets the index of the referenced variable (n) only
    192     *
    193     * @since 6.0
    194     * @see #setIndex(int)
    195     */
    196    final void setIndexOnly(final int n) {
    197        this.n = n;
    198    }
    199
    200    /**
    201     * Long output format:
    202     *
    203     * &lt;name of opcode&gt; "["&lt;opcode number&gt;"]" "("&lt;length of instruction&gt;")" "&lt;"&lt; local variable
    204     * index&gt;"&gt;"
    205     *
    206     * @param verbose long/short format switch.
    207     * @return mnemonic for instruction.
    208     */
    209    @Override
    210    public String toString(final boolean verbose) {
    211        final short opcode = super.getOpcode();
    212        if (opcode >= Const.ILOAD_0 && opcode <= Const.ALOAD_3 || opcode >= Const.ISTORE_0 && opcode <= Const.ASTORE_3) {
    213            return super.toString(verbose);
    214        }
    215        return super.toString(verbose) + " " + n;
    216    }
    217
    218    private boolean wide() {
    219        return n > Const.MAX_BYTE;
    220    }
    221}