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 <= tag <= ALOAD_3) || (ISTORE <= tag <= 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 // dump() emits the wide prefix only when the index needs it, so match that width here. 149 super.setLength(wide() ? 4 : 2); 150 } else { 151 final short opcode = super.getOpcode(); 152 if (opcode >= Const.ILOAD && opcode <= Const.ALOAD || opcode >= Const.ISTORE && opcode <= Const.ASTORE) { 153 n = bytes.readUnsignedByte(); 154 super.setLength(2); 155 } else { 156 if (opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2 157 n = (opcode - Const.ILOAD_0) % 4; 158 } else { // Assert ISTORE_0 <= tag <= ASTORE_3 159 n = (opcode - Const.ISTORE_0) % 4; 160 } 161 super.setLength(1); 162 } 163 } 164 } 165 166 /** 167 * Sets the local variable index. also updates opcode and length TODO Why? 168 * 169 * @param index local variable index (unsigned short). 170 * @throws ClassGenException Thrown if index is out of bounds. 171 * @see #setIndexOnly(int) 172 */ 173 @Override 174 public void setIndex(final int index) { // TODO could be package-protected? 175 if (!isNonNegativeUShort(index)) { 176 throw new ClassGenException("Illegal value: " + index); 177 } 178 this.n = index; 179 // Cannot be < 0 as this is checked above 180 if (index <= 3) { // Use more compact instruction xLOAD_n 181 super.setOpcode((short) (cTag + index)); 182 super.setLength(1); 183 } else { 184 super.setOpcode(canonTag); 185 if (wide()) { 186 super.setLength(4); 187 } else { 188 super.setLength(2); 189 } 190 } 191 } 192 193 /** 194 * Sets the index of the referenced variable (n) only 195 * 196 * @since 6.0 197 * @see #setIndex(int) 198 */ 199 final void setIndexOnly(final int n) { 200 this.n = n; 201 } 202 203 /** 204 * Long output format: 205 * 206 * <name of opcode> "["<opcode number>"]" "("<length of instruction>")" "<"< local variable 207 * index>">" 208 * 209 * @param verbose long/short format switch. 210 * @return mnemonic for instruction. 211 */ 212 @Override 213 public String toString(final boolean verbose) { 214 final short opcode = super.getOpcode(); 215 if (opcode >= Const.ILOAD_0 && opcode <= Const.ALOAD_3 || opcode >= Const.ISTORE_0 && opcode <= Const.ASTORE_3) { 216 return super.toString(verbose); 217 } 218 return super.toString(verbose) + " " + n; 219 } 220 221 private boolean wide() { 222 return n > Const.MAX_BYTE; 223 } 224}