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 <= tag <= ALOAD_3) || (ISTORE <= tag <= 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 * Sets 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 * <name of opcode> "["<opcode number>"]" "("<length of instruction>")" "<"< local variable 198 * index>">" 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}