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