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 * 017 */ 018package org.apache.bcel.generic; 019 020import java.io.DataOutputStream; 021import java.io.IOException; 022 023import org.apache.bcel.Const; 024import org.apache.bcel.util.ByteSequence; 025 026/** 027 * Abstract super class for instructions dealing with local variables. 028 * 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 canon_tag, final short c_tag) { 052 this.canonTag = canon_tag; 053 this.cTag = c_tag; 054 } 055 056 /** 057 * @param opcode Instruction opcode 058 * @param cTag Instruction number for compact version, ALOAD_0, e.g. 059 * @param n local variable index (unsigned short) 060 */ 061 protected LocalVariableInstruction(final short opcode, final short cTag, final int n) { 062 super(opcode, (short) 2); 063 this.cTag = cTag; 064 canonTag = opcode; 065 setIndex(n); 066 } 067 068 /** 069 * Dump instruction as byte code to stream out. 070 * 071 * @param out Output stream 072 */ 073 @Override 074 public void dump(final DataOutputStream out) throws IOException { 075 if (wide()) { 076 out.writeByte(Const.WIDE); 077 } 078 out.writeByte(super.getOpcode()); 079 if (super.getLength() > 1) { // Otherwise ILOAD_n, instruction, e.g. 080 if (wide()) { 081 out.writeShort(n); 082 } else { 083 out.writeByte(n); 084 } 085 } 086 } 087 088 /** 089 * @return canonical tag for instruction, e.g., ALOAD for ALOAD_0 090 */ 091 public short getCanonicalTag() { 092 return canonTag; 093 } 094 095 /** 096 * @return local variable index (n) referred by this instruction. 097 */ 098 @Override 099 public final int getIndex() { 100 return n; 101 } 102 103 /** 104 * Returns the type associated with the instruction - in case of ALOAD or ASTORE Type.OBJECT is returned. This is just a 105 * bit incorrect, because ALOAD and ASTORE may work on every ReferenceType (including Type.NULL) and ASTORE may even 106 * work on a ReturnaddressType . 107 * 108 * @return type associated with the instruction 109 */ 110 @Override 111 public Type getType(final ConstantPoolGen cp) { 112 switch (canonTag) { 113 case Const.ILOAD: 114 case Const.ISTORE: 115 return Type.INT; 116 case Const.LLOAD: 117 case Const.LSTORE: 118 return Type.LONG; 119 case Const.DLOAD: 120 case Const.DSTORE: 121 return Type.DOUBLE; 122 case Const.FLOAD: 123 case Const.FSTORE: 124 return Type.FLOAT; 125 case Const.ALOAD: 126 case Const.ASTORE: 127 return Type.OBJECT; 128 default: 129 throw new ClassGenException("Unknown case in switch" + canonTag); 130 } 131 } 132 133 /** 134 * Read needed data (e.g. index) from file. 135 * 136 * <pre> 137 * (ILOAD <= tag <= ALOAD_3) || (ISTORE <= tag <= ASTORE_3) 138 * </pre> 139 */ 140 @Override 141 protected void initFromFile(final ByteSequence bytes, final boolean wide) throws IOException { 142 if (wide) { 143 n = bytes.readUnsignedShort(); 144 super.setLength(4); 145 } else { 146 final short opcode = super.getOpcode(); 147 if (opcode >= Const.ILOAD && opcode <= Const.ALOAD || opcode >= Const.ISTORE && opcode <= Const.ASTORE) { 148 n = bytes.readUnsignedByte(); 149 super.setLength(2); 150 } else { 151 if (opcode <= Const.ALOAD_3) { // compact load instruction such as ILOAD_2 152 n = (opcode - Const.ILOAD_0) % 4; 153 } else { // Assert ISTORE_0 <= tag <= ASTORE_3 154 n = (opcode - Const.ISTORE_0) % 4; 155 } 156 super.setLength(1); 157 } 158 } 159 } 160 161 /** 162 * Set the local variable index. also updates opcode and length TODO Why? 163 * 164 * @see #setIndexOnly(int) 165 */ 166 @Override 167 public void setIndex(final int n) { // TODO could be package-protected? 168 if (n < 0 || n > Const.MAX_SHORT) { 169 throw new ClassGenException("Illegal value: " + n); 170 } 171 this.n = n; 172 // Cannot be < 0 as this is checked above 173 if (n <= 3) { // Use more compact instruction xLOAD_n 174 super.setOpcode((short) (cTag + n)); 175 super.setLength(1); 176 } else { 177 super.setOpcode(canonTag); 178 if (wide()) { 179 super.setLength(4); 180 } else { 181 super.setLength(2); 182 } 183 } 184 } 185 186 /** 187 * Sets the index of the referenced variable (n) only 188 * 189 * @since 6.0 190 * @see #setIndex(int) 191 */ 192 final void setIndexOnly(final int n) { 193 this.n = n; 194 } 195 196 /** 197 * Long output format: 198 * 199 * <name of opcode> "["<opcode number>"]" "("<length of instruction>")" "<"< local variable 200 * index>">" 201 * 202 * @param verbose long/short format switch 203 * @return mnemonic for instruction 204 */ 205 @Override 206 public String toString(final boolean verbose) { 207 final short opcode = super.getOpcode(); 208 if (opcode >= Const.ILOAD_0 && opcode <= Const.ALOAD_3 || opcode >= Const.ISTORE_0 && opcode <= Const.ASTORE_3) { 209 return super.toString(verbose); 210 } 211 return super.toString(verbose) + " " + n; 212 } 213 214 private boolean wide() { 215 return n > Const.MAX_BYTE; 216 } 217}