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 }