    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}