/*
 *  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.runtimesupport;

import org.jikesrvm.VM;
import org.jikesrvm.compilers.common.ExceptionTable;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.BasicBlockEnumeration;
import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlock;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.operand.TypeOperand;

/**
 * Encoding of try ranges in the final machinecode and the
 * corresponding exception type and catch block start.
 */
final class OptExceptionTable extends ExceptionTable {

  /**
   * Encode an exception table
   * @param ir the IR to encode the exception table for
   * @return the encoded exception table
   */
  static int[] encode(IR ir) {
    int index = 0;
    int currStartOff, currEndOff;
    int tableSize = countExceptionTableSize(ir);
    int[] eTable = new int[tableSize * 4];

    // For each basic block
    //   See if it has code associated with it and if it has
    //   any reachable exception handlers.
    //   When such a block is found, check the blocks that follow
    //   it in code order to see if this block has the same
    //   Bag of exceptionHandlers as any of its immediate successors.
    //   If so the try region can be expanded to include those
    //   successors. Stop checking successors as soon as a non-match
    //   is found, or a block that doesn't have handlers is found.
    //   Successors that don't have any code associated with them can
    //   be ignored.
    //   If blocks were joined together then when adding the
    //   entries to the eTable it is important to not restrict the
    //   entries to reachable handlers; as the first block may only
    //   throw a subset of the exception types represented by the Bag
    for (BasicBlock bblock = ir.firstBasicBlockInCodeOrder(); bblock != null;) {
      // Iteration is explicit in loop

      int startOff = bblock.firstInstruction().getmcOffset();
      int endOff = bblock.lastInstruction().getmcOffset();
      if (endOff > startOff) {
        if (!bblock.hasExceptionHandlers()) {
          bblock = bblock.nextBasicBlockInCodeOrder();
          continue;
        }

        BasicBlock followonBB;
        BasicBlockEnumeration reachBBe, e;
        boolean joinedBlocks;

        // First make sure at least one of the exception handlers
        // is reachable from this block
        reachBBe = bblock.getReachableExceptionHandlers();
        if (!reachBBe.hasMoreElements()) {
          bblock = bblock.nextBasicBlockInCodeOrder();
          continue;
        }

        currStartOff = startOff;
        currEndOff = endOff;
        joinedBlocks = false;

        for (followonBB = bblock.nextBasicBlockInCodeOrder(); followonBB != null; followonBB =
            followonBB.nextBasicBlockInCodeOrder()) {
          int fStartOff = followonBB.firstInstruction().getmcOffset();
          int fEndOff = followonBB.lastInstruction().getmcOffset();
          // See if followon Block has any code
          if (fEndOff > fStartOff) {
            // See if followon Block has matching handler block bag
            if (followonBB.hasExceptionHandlers() && bblock.isExceptionHandlerEquivalent(followonBB)) {
              currEndOff = fEndOff;
              joinedBlocks = true;
            } else {
              // Can't join any more blocks together
              break;
            }
          }
        }
        // found all the matching followon blocks
        // Now fill in the eTable with the handlers
        if (joinedBlocks) {
          e = bblock.getExceptionHandlers();
        } else {
          e = reachBBe;
        }

        while (e.hasMoreElements()) {
          ExceptionHandlerBasicBlock eBlock = (ExceptionHandlerBasicBlock) e.nextElement();
          for (java.util.Enumeration<TypeOperand> ets = eBlock.getExceptionTypes(); ets.hasMoreElements();) {
            TypeOperand type = ets.nextElement();
            int catchOffset = eBlock.firstInstruction().getmcOffset();
            eTable[index + TRY_START] = currStartOff;
            eTable[index + TRY_END] = currEndOff;
            eTable[index + CATCH_START] = catchOffset;
            try {
              eTable[index + EX_TYPE] = type.getTypeRef().resolve().getId();
            } catch (NoClassDefFoundError except) {
              // Yuck.  If this happens beatup Dave and make him do the right
              // thing. For now, we are forcing early loading of exception
              // types to avoid a bunch of ugly issues in resolving the type
              // when delivering the exception.  The problem is that we
              // currently can't allow a GC while in the midst of delivering
              // an exception and resolving the type reference might entail
              // calling arbitrary classloader code.
              VM.sysWriteln("Trouble resolving a caught exception at compile time:");
              except.printStackTrace(); // sysFail won't print the stack trace
              // that lead to the
              // NoClassDefFoundError.
              VM.sysFail("Unable to resolve caught exception type at compile time");
            }
            index += 4;
          }
        }
        bblock = followonBB;
      } else {
        // No code in bblock
        bblock = bblock.nextBasicBlockInCodeOrder();
      }
    }

    if (index != eTable.length) {              // resize array
      int[] newETable = new int[index];
      for (int i = 0; i < index; i++) {
        newETable[i] = eTable[i];
      }
      eTable = newETable;
    }
    return eTable;
  }

  /**
   * Return an upper bounds on the size of the exception table for an IR.
   */
  private static int countExceptionTableSize(IR ir) {
    int tSize = 0;
    for (BasicBlock bblock = ir.firstBasicBlockInCodeOrder(); bblock != null; bblock =
        bblock.nextBasicBlockInCodeOrder()) {
      if (bblock.hasExceptionHandlers()) {
        for (BasicBlockEnumeration e = bblock.getExceptionHandlers(); e.hasMoreElements();) {
          ExceptionHandlerBasicBlock ebb = (ExceptionHandlerBasicBlock) e.next();
          tSize += ebb.getNumberOfExceptionTableEntries();
        }
      }
    }
    return tSize;
  }
}



