    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 */
    017package org.apache.bcel.generic;
    018
    019import org.apache.bcel.classfile.CodeException;
    020
    021/**
    022 * This class represents an exception handler, i.e., specifies the region where a handler is active and an instruction
    023 * where the actual handling is done. pool as parameters. Opposed to the JVM specification the end of the handled region
    024 * is set to be inclusive, i.e. all instructions between start and end are protected including the start and end
    025 * instructions (handles) themselves. The end of the region is automatically mapped to be exclusive when calling
    026 * getCodeException(), i.e., there is no difference semantically.
    027 *
    028 * @see MethodGen
    029 * @see CodeException
    030 * @see InstructionHandle
    031 */
    032public final class CodeExceptionGen implements InstructionTargeter, Cloneable {
    033
    034    static final CodeExceptionGen[] EMPTY_ARRAY = {};
    035
    036    private InstructionHandle startPc;
    037    private InstructionHandle endPc;
    038    private InstructionHandle handlerPc;
    039    private ObjectType catchType;
    040
    041    /**
    042     * Add an exception handler, i.e., specify region where a handler is active and an instruction where the actual handling
    043     * is done.
    044     *
    045     * @param startPc Start of handled region (inclusive)
    046     * @param endPc End of handled region (inclusive)
    047     * @param handlerPc Where handling is done
    048     * @param catchType which exception is handled, null for ANY
    049     */
    050    public CodeExceptionGen(final InstructionHandle startPc, final InstructionHandle endPc, final InstructionHandle handlerPc, final ObjectType catchType) {
    051        setStartPC(startPc);
    052        setEndPC(endPc);
    053        setHandlerPC(handlerPc);
    054        this.catchType = catchType;
    055    }
    056
    057    @Override
    058    public Object clone() {
    059        try {
    060            return super.clone();
    061        } catch (final CloneNotSupportedException e) {
    062            throw new Error("Clone Not Supported"); // never happens
    063        }
    064    }
    065
    066    /**
    067     * @return true, if ih is target of this handler
    068     */
    069    @Override
    070    public boolean containsTarget(final InstructionHandle ih) {
    071        return startPc == ih || endPc == ih || handlerPc == ih;
    072    }
    073
    074    /** Gets the type of the Exception to catch, 'null' for ANY. */
    075    public ObjectType getCatchType() {
    076        return catchType;
    077    }
    078
    079    /**
    080     * Get CodeException object.<BR>
    081     *
    082     * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods
    083     * has been called for the instruction list.
    084     *
    085     * @param cp constant pool
    086     */
    087    public CodeException getCodeException(final ConstantPoolGen cp) {
    088        return new CodeException(startPc.getPosition(), endPc.getPosition() + endPc.getInstruction().getLength(), handlerPc.getPosition(),
    089            catchType == null ? 0 : cp.addClass(catchType));
    090    }
    091
    092    /**
    093     * @return end of handled region (inclusive)
    094     */
    095    public InstructionHandle getEndPC() {
    096        return endPc;
    097    }
    098
    099    /**
    100     * @return start of handler
    101     */
    102    public InstructionHandle getHandlerPC() {
    103        return handlerPc;
    104    }
    105
    106    /**
    107     * @return start of handled region (inclusive)
    108     */
    109    public InstructionHandle getStartPC() {
    110        return startPc;
    111    }
    112
    113    /** Sets the type of the Exception to catch. Set 'null' for ANY. */
    114    public void setCatchType(final ObjectType catchType) {
    115        this.catchType = catchType;
    116    }
    117
    118    /*
    119     * Set end of handler
    120     *
    121     * @param endPc End of handled region (inclusive)
    122     */
    123    public void setEndPC(final InstructionHandle endPc) { // TODO could be package-protected?
    124        BranchInstruction.notifyTarget(this.endPc, endPc, this);
    125        this.endPc = endPc;
    126    }
    127
    128    /*
    129     * Set handler code
    130     *
    131     * @param handlerPc Start of handler
    132     */
    133    public void setHandlerPC(final InstructionHandle handlerPc) { // TODO could be package-protected?
    134        BranchInstruction.notifyTarget(this.handlerPc, handlerPc, this);
    135        this.handlerPc = handlerPc;
    136    }
    137
    138    /*
    139     * Set start of handler
    140     *
    141     * @param startPc Start of handled region (inclusive)
    142     */
    143    public void setStartPC(final InstructionHandle startPc) { // TODO could be package-protected?
    144        BranchInstruction.notifyTarget(this.startPc, startPc, this);
    145        this.startPc = startPc;
    146    }
    147
    148    @Override
    149    public String toString() {
    150        return "CodeExceptionGen(" + startPc + ", " + endPc + ", " + handlerPc + ")";
    151    }
    152
    153    /**
    154     * @param oldIh old target, either start or end
    155     * @param newIh new target
    156     */
    157    @Override
    158    public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) {
    159        boolean targeted = false;
    160        if (startPc == oldIh) {
    161            targeted = true;
    162            setStartPC(newIh);
    163        }
    164        if (endPc == oldIh) {
    165            targeted = true;
    166            setEndPC(newIh);
    167        }
    168        if (handlerPc == oldIh) {
    169            targeted = true;
    170            setHandlerPC(newIh);
    171        }
    172        if (!targeted) {
    173            throw new ClassGenException("Not targeting " + oldIh + ", but {" + startPc + ", " + endPc + ", " + handlerPc + "}");
    174        }
    175    }
    176}