1 /*
   2  * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 package com.sun.org.apache.bcel.internal.generic;
  21 
  22 import java.io.DataOutputStream;
  23 import java.io.IOException;
  24 
  25 import com.sun.org.apache.bcel.internal.Const;
  26 import com.sun.org.apache.bcel.internal.util.ByteSequence;
  27 
  28 /**
  29  * Abstract super class for instructions dealing with local variables.
  30  *
  31  * @LastModified: Jan 2020
  32  */
  33 public abstract class LocalVariableInstruction extends Instruction implements TypedInstruction,
  34         IndexedInstruction {
  35 
  36     private int n = -1; // index of referenced variable
  37     private short c_tag = -1; // compact version, such as ILOAD_0
  38     private short canon_tag = -1; // canonical tag such as ILOAD
  39 
  40 
  41     private boolean wide() {
  42         return n > Const.MAX_BYTE;
  43     }
  44 
  45 
  46     /**
  47      * Empty constructor needed for Instruction.readInstruction.
  48      * Not to be used otherwise.
  49      * tag and length are defined in readInstruction and initFromFile, respectively.
  50      */
  51     LocalVariableInstruction(final short canon_tag, final short c_tag) {
  52         super();
  53         this.canon_tag = canon_tag;
  54         this.c_tag = c_tag;
  55     }
  56 
  57 
  58     /**
  59      * Empty constructor needed for Instruction.readInstruction.
  60      * Also used by IINC()!
  61      */
  62     LocalVariableInstruction() {
  63     }
  64 
  65 
  66     /**
  67      * @param opcode Instruction opcode
  68      * @param c_tag Instruction number for compact version, ALOAD_0, e.g.
  69      * @param n local variable index (unsigned short)
  70      */
  71     protected LocalVariableInstruction(final short opcode, final short c_tag, final int n) {
  72         super(opcode, (short) 2);
  73         this.c_tag = c_tag;
  74         canon_tag = opcode;
  75         setIndex(n);
  76     }
  77 
  78 
  79     /**
  80      * Dump instruction as byte code to stream out.
  81      * @param out Output stream
  82      */
  83     @Override
  84     public void dump( final DataOutputStream out ) throws IOException {
  85         if (wide()) {
  86             out.writeByte(Const.WIDE);
  87         }
  88         out.writeByte(super.getOpcode());
  89         if (super.getLength() > 1) { // Otherwise ILOAD_n, instruction, e.g.
  90             if (wide()) {
  91                 out.writeShort(n);
  92             } else {
  93                 out.writeByte(n);
  94             }
  95         }
  96     }
  97 
  98 
  99     /**
 100      * Long output format:
 101      *
 102      * <name of opcode> "["<opcode number>"]"
 103      * "("<length of instruction>")" "<"< local variable index>">"
 104      *
 105      * @param verbose long/short format switch
 106      * @return mnemonic for instruction
 107      */
 108     @Override
 109     public String toString( final boolean verbose ) {
 110         final short _opcode = super.getOpcode();
 111         if (((_opcode >= Const.ILOAD_0) && (_opcode <= Const.ALOAD_3))
 112          || ((_opcode >= Const.ISTORE_0) && (_opcode <= Const.ASTORE_3))) {
 113             return super.toString(verbose);
 114         }
 115         return super.toString(verbose) + " " + n;
 116     }
 117 
 118 
 119     /**
 120      * Read needed data (e.g. index) from file.
 121      * <pre>
 122      * (ILOAD &lt;= tag &lt;= ALOAD_3) || (ISTORE &lt;= tag &lt;= ASTORE_3)
 123      * </pre>
 124      */
 125     @Override
 126     protected void initFromFile( final ByteSequence bytes, final boolean wide ) throws IOException {
 127         if (wide) {
 128             n = bytes.readUnsignedShort();
 129             super.setLength(4);
 130         } else {
 131             final short _opcode = super.getOpcode();
 132             if (((_opcode >= Const.ILOAD) && (_opcode <= Const.ALOAD))
 133              || ((_opcode >= Const.ISTORE) && (_opcode <= Const.ASTORE))) {
 134                 n = bytes.readUnsignedByte();
 135                 super.setLength(2);
 136             } else if (_opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2
 137                 n = (_opcode - Const.ILOAD_0) % 4;
 138                 super.setLength(1);
 139             } else { // Assert ISTORE_0 <= tag <= ASTORE_3
 140                 n = (_opcode - Const.ISTORE_0) % 4;
 141                 super.setLength(1);
 142             }
 143         }
 144     }
 145 
 146 
 147     /**
 148      * @return local variable index (n) referred by this instruction.
 149      */
 150     @Override
 151     public final int getIndex() {
 152         return n;
 153     }
 154 
 155 
 156     /**
 157      * Set the local variable index.
 158      * also updates opcode and length
 159      * TODO Why?
 160      * @see #setIndexOnly(int)
 161      */
 162     @Override
 163     public void setIndex( final int n ) { // TODO could be package-protected?
 164         if ((n < 0) || (n > Const.MAX_SHORT)) {
 165             throw new ClassGenException("Illegal value: " + n);
 166         }
 167         this.n = n;
 168         // Cannot be < 0 as this is checked above
 169         if (n <= 3) { // Use more compact instruction xLOAD_n
 170             super.setOpcode((short) (c_tag + n));
 171             super.setLength(1);
 172         } else {
 173             super.setOpcode(canon_tag);
 174             if (wide()) {
 175                 super.setLength(4);
 176             } else {
 177                 super.setLength(2);
 178             }
 179         }
 180     }
 181 
 182 
 183     /** @return canonical tag for instruction, e.g., ALOAD for ALOAD_0
 184      */
 185     public short getCanonicalTag() {
 186         return canon_tag;
 187     }
 188 
 189 
 190     /**
 191      * Returns the type associated with the instruction -
 192      * in case of ALOAD or ASTORE Type.OBJECT is returned.
 193      * This is just a bit incorrect, because ALOAD and ASTORE
 194      * may work on every ReferenceType (including Type.NULL) and
 195      * ASTORE may even work on a ReturnaddressType .
 196      * @return type associated with the instruction
 197      */
 198     @Override
 199     public Type getType( final ConstantPoolGen cp ) {
 200         switch (canon_tag) {
 201             case Const.ILOAD:
 202             case Const.ISTORE:
 203                 return Type.INT;
 204             case Const.LLOAD:
 205             case Const.LSTORE:
 206                 return Type.LONG;
 207             case Const.DLOAD:
 208             case Const.DSTORE:
 209                 return Type.DOUBLE;
 210             case Const.FLOAD:
 211             case Const.FSTORE:
 212                 return Type.FLOAT;
 213             case Const.ALOAD:
 214             case Const.ASTORE:
 215                 return Type.OBJECT;
 216             default:
 217                 throw new ClassGenException("Oops: unknown case in switch" + canon_tag);
 218         }
 219     }
 220 
 221     /**
 222      * Sets the index of the referenced variable (n) only
 223      * @since 6.0
 224      * @see #setIndex(int)
 225      */
 226     final void setIndexOnly(final int n) {
 227         this.n = n;
 228     }
 229 }