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

import java.io.PrintWriter;
import java.io.StringWriter;
import org.jikesrvm.classloader.TypeReference;

/**
 * Use this exception if we encounter a runtime error in the dynamic
 * optimizing compiler.  The caller can recover by calling the
 * non-optimizing compiler instead (or by reverting to the previous
 * version of compiled code).
 */
public class OptimizingCompilerException extends RuntimeException {
  /** Support for exception serialization */
  static final long serialVersionUID = -868535710873341956L;

  /**
   * Capture illegal upcasts from magic types to java.lang.Object
   */
  public static class IllegalUpcast extends RuntimeException {
    /** Support for exception serialization */
    static final long serialVersionUID = -847866659938089530L;
    /** Unboxed type that was attempted to convert to an Object */
    final transient TypeReference magicType;

    public IllegalUpcast(TypeReference type) {
      super("Illegal upcast from " + type + " to java.lang.Object");
      magicType = type;
    }
  }

  /**
   * When running in the RVM, typically optimizing compiler
   * exceptions are caught, optionally a message is printed, and we
   * fallback to using the baseline compiler.  However, this
   * may not be desirable when running regression testing because
   * an optimizing compiler exception may be a symptom of a serious failure.
   * Thus, the code throwing the exception can use an optional boolean value
   * to indicate if the exception is "normal" or if it should be treated
   * as a fatal failure for the purpose of regression testing.
   */
  public boolean isFatal = true;

  public OptimizingCompilerException() { }

  /**
   * @param   b is the exception fatal?
   */
  public OptimizingCompilerException(boolean b) {
    isFatal = b;
  }

  /**
   * @param  err message describing reason for exception
   */
  public OptimizingCompilerException(String err) {
    super(err);
  }

  /**
   * @param   err message descrining reason for exception
   * @param   b is the exception fatal?
   */
  public OptimizingCompilerException(String err, boolean b) {
    super(err);
    isFatal = b;
  }

  /**
   * @param   module opt compiler module in which exception was raised
   * @param   err message describing reason for exception
   */
  public OptimizingCompilerException(String module, String err) {
    super("ERROR produced in module:" + module + "\n    " + err + "\n");
  }

  /**
   * @param   module opt compiler module in which exception was raised
   * @param   err1 message describing reason for exception
   * @param   err2 message describing reason for exception
   */
  public OptimizingCompilerException(String module, String err1, String err2) {
    super("ERROR produced in module:" + module + "\n    " + err1 + " " + err2 + "\n");
  }

  /**
   * @param   module opt compiler module in which exception was raised
   * @param   err1 message describing reason for exception
   * @param   obj  object to print describing reason for exception
   */
  public OptimizingCompilerException(String module, String err1, Object obj) {
    this(module, err1, obj.toString());
  }

  /**
   * @param   module opt compiler module in which exception was raised
   * @param   err1 message describing reason for exception
   * @param   val  integer to print describing reason for exception
   */
  public OptimizingCompilerException(String module, String err1, int val) {
    this(module, err1, Integer.toString(val));
  }

  /**
   * @param   module opt compiler module in which exception was raised
   * @param   err1 message describing reason for exception
   * @param   err2 message describing reason for exception
   * @param   err3 message describing reason for exception
   */
  public OptimizingCompilerException(String module, String err1, String err2, String err3) {
    super("ERROR produced in module:" + module + "\n    " + err1 + " " + err2 + "\n" + err3 + "\n");
  }

  /**
   * @param   module opt compiler module in which exception was raised
   * @param   err1 message describing reason for exception
   * @param   err2 message describing reason for exception
   * @param   obj  object to print describing reason for exception
   */
  public OptimizingCompilerException(String module, String err1, String err2, Object obj) {
    this(module, err1, err2, obj.toString());
  }

  /**
   * @param   module opt compiler module in which exception was raised
   * @param   err1 message describing reason for exception
   * @param   err2 message describing reason for exception
   * @param   val  integer to print describing reason for exception
   */
  OptimizingCompilerException(String module, String err1, String err2, int val) {
    this(module, err1, err2, Integer.toString(val));
  }

  /**
   * Use the UNREACHABLE methods to mark code that should never execute
   * eg, unexpected cases of switch statments and nested if/then/else
   * @exception OptimizingCompilerException
   */
  public static void UNREACHABLE() throws OptimizingCompilerException {
    throw new OptimizingCompilerException("Executed UNREACHABLE code");
  }

  /**
   * Use the UNREACHABLE methods to mark code that should never execute
   * eg, unexpected cases of switch statments and nested if/then/else
   * @param module module in which exception occured
   * @exception OptimizingCompilerException
   */
  public static void UNREACHABLE(String module) throws OptimizingCompilerException {
    throw new OptimizingCompilerException(module, "Executed UNREACHABLE code");
  }

  /**
   * Use the UNREACHABLE methods to mark code that should never execute
   * eg, unexpected cases of switch statments and nested if/then/else
   * @param   module opt compiler module in which exception was raised
   * @param   err1 message describing reason for exception
   * @exception OptimizingCompilerException
   */
  public static void UNREACHABLE(String module, String err1) throws OptimizingCompilerException {
    throw new OptimizingCompilerException(module, "Executed UNREACHABLE code", err1);
  }

  /**
   * Use the UNREACHABLE methods to mark code that should never execute
   * eg, unexpected cases of switch statments and nested if/then/else
   * @param   module opt compiler module in which exception was raised
   * @param   err1 message describing reason for exception
   * @param   err2 message describing reason for exception
   * @exception OptimizingCompilerException
   */
  public static void UNREACHABLE(String module, String err1, String err2) throws OptimizingCompilerException {
    throw new OptimizingCompilerException(module, "Executed UNREACHABLE code", err1, err2);
  }

  /**
   * Incomplete function in IA32 port.
   * @exception OptimizingCompilerException
   */
  public static void TODO() throws OptimizingCompilerException {
    throw new OptimizingCompilerException("Unsupported function in IA32 port");
  }

  /**
   * Incomplete function in IA32 port.
   * @param   module opt compiler module in which exception was raised
   * @exception OptimizingCompilerException
   */
  public static void TODO(String module) throws OptimizingCompilerException {
    throw new OptimizingCompilerException(module, "Unsupported function in IA32 port");
  }

  /**
   * Return a string that is the printout of level stackframes in the stacktrace.
   * @param level the number of levels to print
   * @return n-level dump of stacktrace
   */
  public String trace(int level) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    printStackTrace(pw);
    int count = 0, i = 0;
    StringBuffer sb = sw.getBuffer();
    for (; i < sb.length() && count < level + 1; i++) {
      if (sb.charAt(i) == '\n') {
        count++;
      }
    }
    sb.setLength(i);
    return sb.toString();
  }
}



