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 import serp.util.*; 009 010 /** 011 * Represents a <code>try {} catch() {}</code> statement in bytecode. 012 * 013 * @author Abe White 014 */ 015 public class ExceptionHandler implements InstructionPtr, BCEntity, 016 VisitAcceptor { 017 private int _catchIndex = 0; 018 private Code _owner = null; 019 private InstructionPtrStrategy _tryStart = new InstructionPtrStrategy(this); 020 private InstructionPtrStrategy _tryEnd = new InstructionPtrStrategy(this); 021 private InstructionPtrStrategy _tryHandler = new InstructionPtrStrategy 022 (this); 023 024 ExceptionHandler(Code owner) { 025 _owner = owner; 026 } 027 028 /** 029 * Return the owning code block. 030 */ 031 public Code getCode() { 032 return _owner; 033 } 034 035 /////////////////// 036 // Body operations 037 /////////////////// 038 039 /** 040 * Return the instruction marking the beginning of the try {} block. 041 */ 042 public Instruction getTryStart() { 043 return _tryStart.getTargetInstruction(); 044 } 045 046 /** 047 * Set the {@link Instruction} marking the beginning of the try block. 048 * The instruction must already be a part of the method. 049 */ 050 public void setTryStart(Instruction instruction) { 051 _tryStart.setTargetInstruction(instruction); 052 } 053 054 /** 055 * Return the instruction at the end of the try {} block. 056 */ 057 public Instruction getTryEnd() { 058 return _tryEnd.getTargetInstruction(); 059 } 060 061 /** 062 * Set the Instruction at the end of the try block. The 063 * Instruction must already be a part of the method. 064 */ 065 public void setTryEnd(Instruction instruction) { 066 _tryEnd.setTargetInstruction(instruction); 067 } 068 069 ////////////////////// 070 // Handler operations 071 ////////////////////// 072 073 /** 074 * Return the instruction marking the beginning of the catch {} block. 075 */ 076 public Instruction getHandlerStart() { 077 return _tryHandler.getTargetInstruction(); 078 } 079 080 /** 081 * Set the {@link Instruction} marking the beginning of the catch block. 082 * The instruction must already be a part of the method. 083 * WARNING: if this instruction is deleted, the results are undefined. 084 */ 085 public void setHandlerStart(Instruction instruction) { 086 _tryHandler.setTargetInstruction(instruction); 087 } 088 089 //////////////////// 090 // Catch operations 091 //////////////////// 092 093 /** 094 * Return the index into the class {@link ConstantPool} of the 095 * {@link ClassEntry} describing the exception type this handler catches. 096 */ 097 public int getCatchIndex() { 098 return _catchIndex; 099 } 100 101 /** 102 * Set the index into the class {@link ConstantPool} of the 103 * {@link ClassEntry} describing the exception type this handler catches. 104 */ 105 public void setCatchIndex(int catchTypeIndex) { 106 _catchIndex = catchTypeIndex; 107 } 108 109 /** 110 * Return the name of the exception type; returns null for catch-all 111 * clauses used to implement finally blocks. The name will be returned 112 * in a forum suitable for a {@link Class#forName} call. 113 */ 114 public String getCatchName() { 115 if (_catchIndex == 0) 116 return null; 117 118 ClassEntry entry = (ClassEntry) getPool().getEntry(_catchIndex); 119 return getProject().getNameCache().getExternalForm(entry.getNameEntry(). 120 getValue(), false); 121 } 122 123 /** 124 * Return the {@link Class} of the exception type; returns null for 125 * catch-all clauses used to implement finally blocks. 126 */ 127 public Class getCatchType() { 128 String name = getCatchName(); 129 if (name == null) 130 return null; 131 return Strings.toClass(name, getClassLoader()); 132 } 133 134 /** 135 * Return the bytecode of the exception type; returns null for 136 * catch-all clauses used to implement finally blocks. 137 */ 138 public BCClass getCatchBC() { 139 String name = getCatchName(); 140 if (name == null) 141 return null; 142 return getProject().loadClass(name, getClassLoader()); 143 } 144 145 /** 146 * Set the class of the exception type, or null for catch-all clauses used 147 * with finally blocks. 148 */ 149 public void setCatch(String name) { 150 if (name == null) 151 _catchIndex = 0; 152 else 153 _catchIndex = getPool().findClassEntry(getProject().getNameCache(). 154 getInternalForm(name, false), true); 155 } 156 157 /** 158 * Set the class of the exception type, or null for catch-all clauses used 159 * for finally blocks. 160 */ 161 public void setCatch(Class type) { 162 if (type == null) 163 setCatch((String) null); 164 else 165 setCatch(type.getName()); 166 } 167 168 /** 169 * Set the class of the exception type, or null for catch-all clauses used 170 * for finally blocks. 171 */ 172 public void setCatch(BCClass type) { 173 if (type == null) 174 setCatch((String) null); 175 else 176 setCatch(type.getName()); 177 } 178 179 ///////////////////////////////// 180 // InstructionPtr implementation 181 ///////////////////////////////// 182 183 public void updateTargets() { 184 _tryStart.updateTargets(); 185 _tryEnd.updateTargets(); 186 _tryHandler.updateTargets(); 187 } 188 189 public void replaceTarget(Instruction oldTarget, Instruction newTarget) { 190 _tryStart.replaceTarget(oldTarget, newTarget); 191 _tryEnd.replaceTarget(oldTarget, newTarget); 192 _tryHandler.replaceTarget(oldTarget, newTarget); 193 } 194 195 /////////////////////////// 196 // BCEntity implementation 197 /////////////////////////// 198 199 public Project getProject() { 200 return _owner.getProject(); 201 } 202 203 public ConstantPool getPool() { 204 return _owner.getPool(); 205 } 206 207 public ClassLoader getClassLoader() { 208 return _owner.getClassLoader(); 209 } 210 211 public boolean isValid() { 212 return _owner != null; 213 } 214 215 //////////////////////////////// 216 // VisitAcceptor implementation 217 //////////////////////////////// 218 219 public void acceptVisit(BCVisitor visit) { 220 visit.enterExceptionHandler(this); 221 visit.exitExceptionHandler(this); 222 } 223 224 ////////////////// 225 // I/O operations 226 ////////////////// 227 228 void read(ExceptionHandler orig) { 229 _tryStart.setByteIndex(orig._tryStart.getByteIndex()); 230 _tryEnd.setByteIndex(orig._tryEnd.getByteIndex()); 231 _tryHandler.setByteIndex(orig._tryHandler.getByteIndex()); 232 233 // done at a high level so that if the name isn't in our constant pool, 234 // it will be added 235 setCatch(orig.getCatchName()); 236 } 237 238 void read(DataInput in) throws IOException { 239 setTryStart(in.readUnsignedShort()); 240 setTryEnd(in.readUnsignedShort()); 241 setHandlerStart(in.readUnsignedShort()); 242 setCatchIndex(in.readUnsignedShort()); 243 } 244 245 void write(DataOutput out) throws IOException { 246 out.writeShort(getTryStartPc()); 247 out.writeShort(getTryEndPc()); 248 out.writeShort(getHandlerStartPc()); 249 out.writeShort(getCatchIndex()); 250 } 251 252 public void setTryStart(int start) { 253 _tryStart.setByteIndex(start); 254 } 255 256 public int getTryStartPc() { 257 return _tryStart.getByteIndex(); 258 } 259 260 public void setTryEnd(int end) { 261 setTryEnd((Instruction) _owner.getInstruction(end).prev); 262 } 263 264 /** 265 * Return the program counter end position for this exception handler. 266 * This represents an index into the code byte array. 267 */ 268 public int getTryEndPc() { 269 return _tryEnd.getByteIndex() + getTryEnd().getLength(); 270 } 271 272 public void setHandlerStart(int handler) { 273 _tryHandler.setByteIndex(handler); 274 } 275 276 public int getHandlerStartPc() { 277 return _tryHandler.getByteIndex(); 278 } 279 280 void invalidate() { 281 _owner = null; 282 } 283 }