// See LICENSE for license details. package Chisel.internal import scala.collection.mutable.ArrayBuffer import Chisel._ class ChiselException(message: String, cause: Throwable) extends Exception(message, cause) private[Chisel] object throwException { def apply(s: String, t: Throwable = null): Nothing = throw new ChiselException(s, t) } /** Records and reports runtime errors and warnings. */ private[Chisel] class ErrorLog { def hasErrors: Boolean = errors.exists(_.isFatal) /** Log an error message */ def error(m: => String): Unit = errors += new Error(m, getUserLineNumber) /** Log a warning message */ def warning(m: => String): Unit = errors += new Warning(m, getUserLineNumber) /** Emit an informational message */ def info(m: String): Unit = println(new Info("[%2.3f] %s".format(elapsedTime/1e3, m), None)) // scalastyle:ignore regex /** Prints error messages generated by Chisel at runtime. */ def report(): Unit = errors foreach println // scalastyle:ignore regex /** Throw an exception if any errors have yet occurred. */ def checkpoint(): Unit = if(hasErrors) { import Console._ throwException(errors.map(_ + "\n").reduce(_ + _) + UNDERLINED + "CODE HAS " + errors.filter(_.isFatal).length + RESET + UNDERLINED + " " + RED + "ERRORS" + RESET + UNDERLINED + " and " + errors.filterNot(_.isFatal).length + RESET + UNDERLINED + " " + YELLOW + "WARNINGS" + RESET) } private def findFirstUserFrame(stack: Array[StackTraceElement]): Option[StackTraceElement] = { def isUserCode(ste: StackTraceElement): Boolean = { def isUserModule(c: Class[_]): Boolean = c != null && (c == classOf[Module] || isUserModule(c.getSuperclass)) isUserModule(Class.forName(ste.getClassName)) } stack.indexWhere(isUserCode) match { case x if x < 0 => None case x => Some(stack(x)) } } private def getUserLineNumber = findFirstUserFrame(Thread.currentThread().getStackTrace) private val errors = ArrayBuffer[LogEntry]() private val startTime = System.currentTimeMillis private def elapsedTime: Long = System.currentTimeMillis - startTime } private abstract class LogEntry(msg: => String, line: Option[StackTraceElement]) { def isFatal: Boolean = false def format: String override def toString: String = line match { case Some(l) => s"${format} ${l.getFileName}:${l.getLineNumber}: ${msg} in class ${l.getClassName}" case None => s"${format} ${msg}" } protected def tag(name: String, color: String): String = s"[${color}${name}${Console.RESET}]" } private class Error(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { override def isFatal: Boolean = true def format: String = tag("error", Console.RED) } private class Warning(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { def format: String = tag("warn", Console.YELLOW) } private class Info(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { def format: String = tag("info", Console.MAGENTA) }