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