/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.compilers.opt.ir;

import static org.jikesrvm.compilers.opt.driver.OptConstants.MAYBE;
import static org.jikesrvm.compilers.opt.driver.OptConstants.NO;
import static org.jikesrvm.compilers.opt.driver.OptConstants.YES;

import java.util.Enumeration;

import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.opt.ClassLoaderProxy;
import org.jikesrvm.compilers.opt.inlining.InlineSequence;
import org.jikesrvm.compilers.opt.ir.operand.TypeOperand;
import org.jikesrvm.compilers.opt.liveness.LiveSet;

/**
 * A basic block that marks the start of an exception handler.
 * Exception Handler Basic Block; acronym EHBB.
 */
public final class ExceptionHandlerBasicBlock extends BasicBlock {

  /**
   * The RVMType(s) of the exception(s) caught by this block.
   */
  private TypeOperand[] exceptionTypes;

  /**
   * The liveness information at the beginning of this block
   *  NOTE: If we decide to store this for all blocks, we should move
   *  this field to BasicBlock (the parent class)
   */
  private LiveSet liveSet;

  /**
   * Creates a new exception handler basic block at the specified location,
   * which catches the specified type of exception.
   *
   * @param loc   Bytecode index to create basic block at
   * @param position  The inline context for this basic block
   * @param type  The exception type
   * @param cfg   The ControlFlowGraph that will contain the basic block
   */
  public ExceptionHandlerBasicBlock(int loc, InlineSequence position, TypeOperand type, ControlFlowGraph cfg) {
    super(loc, position, cfg);
    exceptionTypes = new TypeOperand[1];
    exceptionTypes[0] = type;
    setExceptionHandlerBasicBlock();
    liveSet = null;
  }

  /**
   * Add a new exception type to an extant exception handler block.
   * Do filtering of duplicates internally for efficiency.
   * NOTE: this routine is only intended to be called by
   * {@link org.jikesrvm.compilers.opt.bc2ir.BC2IR}.
   *
   * @param et the exception type to be added
   */
  public void addCaughtException(TypeOperand et) {
    for (TypeOperand exceptionType : exceptionTypes) {
      if (exceptionType.similar(et)) return;
    }
    TypeOperand[] newets = new TypeOperand[exceptionTypes.length + 1];
    for (int i = 0; i < exceptionTypes.length; i++) {
      newets[i] = exceptionTypes[i];
    }
    newets[exceptionTypes.length] = et;
    exceptionTypes = newets;
  }

  /**
   * Return YES/NO/MAYBE values that answer the question is it possible for
   * this handler block to catch an exception of the type et.
   *
   * @param cand the TypeReference of the exception in question.
   * @return YES, NO, MAYBE
   */
  public byte mayCatchException(TypeReference cand) {
    boolean seenMaybe = false;
    byte t;
    for (TypeOperand exceptionType : exceptionTypes) {
      t = ClassLoaderProxy.includesType(exceptionType.getTypeRef(), cand);
      if (t == YES) return YES;
      seenMaybe |= (t == MAYBE);
      t = ClassLoaderProxy.includesType(cand, exceptionType.getTypeRef());
      if (t == YES) return YES;
      seenMaybe |= (t == MAYBE);
    }
    return seenMaybe ? MAYBE : NO;
  }

  /**
   * Return YES/NO/MAYBE values that answer the question is it guarenteed that
   * this handler block will catch an exception of type <code>cand</code>
   *
   * @param cand  the TypeReference of the exception in question.
   * @return YES, NO, MAYBE
   */
  public byte mustCatchException(TypeReference cand) {
    boolean seenMaybe = false;
    byte t;
    for (TypeOperand exceptionType : exceptionTypes) {
      t = ClassLoaderProxy.includesType(exceptionType.getTypeRef(), cand);
      if (t == YES) return YES;
      seenMaybe |= (t == MAYBE);
    }
    if (seenMaybe) {
      return MAYBE;
    } else {
      return NO;
    }
  }

  /**
   * Return an Enumeration of the caught exception types.
   * Mainly intended for creation of exception tables during
   * final assembly. Most other clients shouldn't care about this
   * level of detail.
   */
  public Enumeration<TypeOperand> getExceptionTypes() {
    return new Enumeration<TypeOperand>() {
      private int idx = 0;

      public boolean hasMoreElements() {
        return idx != exceptionTypes.length;
      }

      public TypeOperand nextElement() {
        try {
          return exceptionTypes[idx++];
        } catch (ArrayIndexOutOfBoundsException e) {
          throw new java.util.NoSuchElementException("ExceptionHandlerBasicBlock.getExceptionTypes");
        }
      }
    };
  }

  /**
   * Get how many table entires this EHBB needs.
   * Really only of interest during final assembly.
   *
   * @see org.jikesrvm.compilers.opt.runtimesupport.OptExceptionTable
   *
   * @return the number of table entries for this basic block
   */
  public int getNumberOfExceptionTableEntries() {
    return exceptionTypes.length;
  }

  /**
   * Returns the set of registers live before the first instruction of
   * this basic block
   *
   * @return the set of registers live before the first instruction of
   * this basic block
   */
  public LiveSet getLiveSet() {
    return liveSet;
  }

  /**
   * Set the set of registers live before the first instruction of
   * this basic block
   *
   * @param   liveSet The set of registers live before the first instruction of
   * this basic block
   */
  public void setLiveSet(LiveSet liveSet) {
    this.liveSet = liveSet;
  }

  /**
   * Return a string representation of the basic block
   * (augment {@link BasicBlock#toString} with
   * the exceptions caught by this handler block).
   *
   * @return a string representation of the block
   */
  public String toString() {
    String exmsg = " (catches ";
    for (int i = 0; i < exceptionTypes.length - 1; i++) {
      exmsg = exmsg + exceptionTypes[i].toString() + ", ";
    }
    exmsg = exmsg + exceptionTypes[exceptionTypes.length - 1].toString();
    exmsg = exmsg + " for";
    BasicBlockEnumeration in = getIn();
    while (in.hasMoreElements()) {
      exmsg = exmsg + " " + in.next().toString();
    }
    exmsg = exmsg + ")";

    return super.toString() + exmsg;
  }
}
