001 package serp.bytecode; 002 003 import java.io.*; 004 import java.util.*; 005 006 import serp.bytecode.lowlevel.*; 007 import serp.bytecode.visitor.*; 008 009 /** 010 * An opcode in a method of a class. 011 * 012 * @author Abe White 013 */ 014 public class Instruction extends CodeEntry implements BCEntity, VisitAcceptor { 015 private Code _owner = null; 016 private int _opcode = Constants.NOP; 017 018 Instruction(Code owner) { 019 _owner = owner; 020 } 021 022 Instruction(Code owner, int opcode) { 023 _owner = owner; 024 _opcode = opcode; 025 } 026 027 /** 028 * Return the code block that owns this instruction. 029 */ 030 public Code getCode() { 031 return _owner; 032 } 033 034 /** 035 * Return the name of this instruction. 036 */ 037 public String getName() { 038 return Constants.OPCODE_NAMES[_opcode]; 039 } 040 041 /** 042 * Return the opcode this instruction represents. 043 */ 044 public int getOpcode() { 045 return _opcode; 046 } 047 048 /** 049 * Set the opcode this instruction represents. For internal use only. 050 * 051 * @return this instruction, for method chaining 052 */ 053 Instruction setOpcode(int opcode) { 054 _opcode = opcode; 055 return this; 056 } 057 058 /** 059 * Return the index in the method code byte block at which this opcode 060 * starts. Note that this information may be out of date if the code 061 * block has been modified since last read/written. 062 */ 063 public int getByteIndex() { 064 if (_owner != null) 065 return _owner.getByteIndex(this); 066 return 0; 067 } 068 069 /** 070 * Notification that a change has been made to this instruction that 071 * alters the structure of the code block, invalidating byte indexes. 072 */ 073 void invalidateByteIndexes() { 074 if (_owner != null) 075 _owner.invalidateByteIndexes(); 076 } 077 078 /** 079 * Return the line number of this instruction, or null if none. This 080 * method is subject to the validity constraints of {@link #getByteIndex}. 081 * 082 * @see LineNumberTable#getLineNumber(Instruction) 083 */ 084 public LineNumber getLineNumber() { 085 LineNumberTable table = _owner.getLineNumberTable(false); 086 if (table == null) 087 return null; 088 return table.getLineNumber(this); 089 } 090 091 /** 092 * Return the length in bytes of this opcode, including all arguments. 093 * For many opcodes this method relies on an up-to-date byte index. 094 */ 095 int getLength() { 096 return 1; 097 } 098 099 /** 100 * Return the logical number of stack positions changed by this 101 * instruction. In other words, ignore weirdness with longs and doubles 102 * taking two stack positions. 103 */ 104 public int getLogicalStackChange() { 105 return getStackChange(); 106 } 107 108 /** 109 * Return the number of stack positions this instruction pushes 110 * or pops during its execution. 111 * 112 * @return 0 if the stack is not affected by this instruction, a 113 * positive number if it pushes onto the stack, and a negative 114 * number if it pops from the stack 115 */ 116 public int getStackChange() { 117 return 0; 118 } 119 120 /** 121 * Instructions are equal if their opcodes are the same. Subclasses 122 * should override this method to perform a template comparison: 123 * instructions should compare equal to other instructions of the same 124 * type where the data is either the same or the data is unset. 125 */ 126 public boolean equalsInstruction(Instruction other) { 127 if (other == this) 128 return true; 129 return other.getOpcode() == getOpcode(); 130 } 131 132 public Project getProject() { 133 return _owner.getProject(); 134 } 135 136 public ConstantPool getPool() { 137 return _owner.getPool(); 138 } 139 140 public ClassLoader getClassLoader() { 141 return _owner.getClassLoader(); 142 } 143 144 public boolean isValid() { 145 return _owner != null; 146 } 147 148 public void acceptVisit(BCVisitor visit) { 149 } 150 151 void invalidate() { 152 _owner = null; 153 } 154 155 /** 156 * Copy the given instruction data. 157 */ 158 void read(Instruction orig) { 159 } 160 161 /** 162 * Read the arguments for this opcode from the given stream. 163 * This method should be overridden by opcodes that take arguments. 164 */ 165 void read(DataInput in) throws IOException { 166 } 167 168 /** 169 * Write the arguments for this opcode to the given stream. 170 * This method should be overridden by opcodes that take arguments. 171 */ 172 void write(DataOutput out) throws IOException { 173 } 174 }