001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */
018package org.apache.bcel.generic;
019
020import org.apache.bcel.classfile.CodeException;
021
022/**
023 * This class represents an exception handler, i.e., specifies the region where a handler is active and an instruction
024 * where the actual handling is done. pool as parameters. Opposed to the JVM specification the end of the handled region
025 * is set to be inclusive, i.e. all instructions between start and end are protected including the start and end
026 * instructions (handles) themselves. The end of the region is automatically mapped to be exclusive when calling
027 * getCodeException(), i.e., there is no difference semantically.
028 *
029 * @see MethodGen
030 * @see CodeException
031 * @see InstructionHandle
032 */
033public final class CodeExceptionGen implements InstructionTargeter, Cloneable {
034
035    static final CodeExceptionGen[] EMPTY_ARRAY = {};
036
037    private InstructionHandle startPc;
038    private InstructionHandle endPc;
039    private InstructionHandle handlerPc;
040    private ObjectType catchType;
041
042    /**
043     * Add an exception handler, i.e., specify region where a handler is active and an instruction where the actual handling
044     * is done.
045     *
046     * @param startPc Start of handled region (inclusive)
047     * @param endPc End of handled region (inclusive)
048     * @param handlerPc Where handling is done
049     * @param catchType which exception is handled, null for ANY
050     */
051    public CodeExceptionGen(final InstructionHandle startPc, final InstructionHandle endPc, final InstructionHandle handlerPc, final ObjectType catchType) {
052        setStartPC(startPc);
053        setEndPC(endPc);
054        setHandlerPC(handlerPc);
055        this.catchType = catchType;
056    }
057
058    @Override
059    public Object clone() {
060        try {
061            return super.clone();
062        } catch (final CloneNotSupportedException e) {
063            throw new Error("Clone Not Supported"); // never happens
064        }
065    }
066
067    /**
068     * @return true, if ih is target of this handler
069     */
070    @Override
071    public boolean containsTarget(final InstructionHandle ih) {
072        return startPc == ih || endPc == ih || handlerPc == ih;
073    }
074
075    /** Gets the type of the Exception to catch, 'null' for ANY. */
076    public ObjectType getCatchType() {
077        return catchType;
078    }
079
080    /**
081     * Get CodeException object.<BR>
082     *
083     * This relies on that the instruction list has already been dumped to byte code or that the `setPositions' methods
084     * has been called for the instruction list.
085     *
086     * @param cp constant pool
087     */
088    public CodeException getCodeException(final ConstantPoolGen cp) {
089        return new CodeException(startPc.getPosition(), endPc.getPosition() + endPc.getInstruction().getLength(), handlerPc.getPosition(),
090            catchType == null ? 0 : cp.addClass(catchType));
091    }
092
093    /**
094     * @return end of handled region (inclusive)
095     */
096    public InstructionHandle getEndPC() {
097        return endPc;
098    }
099
100    /**
101     * @return start of handler
102     */
103    public InstructionHandle getHandlerPC() {
104        return handlerPc;
105    }
106
107    /**
108     * @return start of handled region (inclusive)
109     */
110    public InstructionHandle getStartPC() {
111        return startPc;
112    }
113
114    /** Sets the type of the Exception to catch. Set 'null' for ANY. */
115    public void setCatchType(final ObjectType catchType) {
116        this.catchType = catchType;
117    }
118
119    /*
120     * Set end of handler
121     *
122     * @param endPc End of handled region (inclusive)
123     */
124    public void setEndPC(final InstructionHandle end_pc) { // TODO could be package-protected?
125        BranchInstruction.notifyTarget(this.endPc, end_pc, this);
126        this.endPc = end_pc;
127    }
128
129    /*
130     * Set handler code
131     *
132     * @param handlerPc Start of handler
133     */
134    public void setHandlerPC(final InstructionHandle handler_pc) { // TODO could be package-protected?
135        BranchInstruction.notifyTarget(this.handlerPc, handler_pc, this);
136        this.handlerPc = handler_pc;
137    }
138
139    /*
140     * Set start of handler
141     *
142     * @param startPc Start of handled region (inclusive)
143     */
144    public void setStartPC(final InstructionHandle start_pc) { // TODO could be package-protected?
145        BranchInstruction.notifyTarget(this.startPc, start_pc, this);
146        this.startPc = start_pc;
147    }
148
149    @Override
150    public String toString() {
151        return "CodeExceptionGen(" + startPc + ", " + endPc + ", " + handlerPc + ")";
152    }
153
154    /**
155     * @param old_ih old target, either start or end
156     * @param new_ih new target
157     */
158    @Override
159    public void updateTarget(final InstructionHandle old_ih, final InstructionHandle new_ih) {
160        boolean targeted = false;
161        if (startPc == old_ih) {
162            targeted = true;
163            setStartPC(new_ih);
164        }
165        if (endPc == old_ih) {
166            targeted = true;
167            setEndPC(new_ih);
168        }
169        if (handlerPc == old_ih) {
170            targeted = true;
171            setHandlerPC(new_ih);
172        }
173        if (!targeted) {
174            throw new ClassGenException("Not targeting " + old_ih + ", but {" + startPc + ", " + endPc + ", " + handlerPc + "}");
175        }
176    }
177}