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    }