001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.lowlevel.*;
007import serp.bytecode.visitor.*;
008import serp.util.*;
009
010/**
011 * Represents a <code>try {} catch() {}</code> statement in bytecode.
012 *
013 * @author Abe White
014 */
015public 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}