diff options
| author | Jack Koenig | 2018-03-22 11:53:51 -0700 |
|---|---|---|
| committer | GitHub | 2018-03-22 11:53:51 -0700 |
| commit | ebb6847e9d01b424424ae11a0067448a4094e46d (patch) | |
| tree | fc87aa9d7b7d437914f6a4c9e20487c0973a1fc3 /src/main | |
| parent | 6ea4ac666e4ce8dfaca1545660f372fccff610f5 (diff) | |
Better bad annotation file error reporting (#771)
* Propagate exceptions from JsonProtocol deserialization
* Add AnnotationFileNotFoundException for better error reporting
* Add AnnotationClassNotFoundException for better error reporting
* Better propagate JSON parsing errors
Also report the file if there is a error deserializing a JSON file
* Make exception for non-array JSON file more explicit
Diffstat (limited to 'src/main')
| -rw-r--r-- | src/main/scala/firrtl/Driver.scala | 25 | ||||
| -rw-r--r-- | src/main/scala/firrtl/annotations/AnnotationUtils.scala | 12 | ||||
| -rw-r--r-- | src/main/scala/firrtl/annotations/JsonProtocol.scala | 34 |
3 files changed, 40 insertions, 31 deletions
diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala index 3ff465e2..f9ba6141 100644 --- a/src/main/scala/firrtl/Driver.scala +++ b/src/main/scala/firrtl/Driver.scala @@ -116,23 +116,22 @@ object Driver { val loadedAnnos = annoFiles.flatMap { file => if (!file.exists) { - throw new FileNotFoundException(s"Annotation file $file not found!") + throw new AnnotationFileNotFoundException(file) } // Try new protocol first - JsonProtocol.deserializeTry(file).getOrElse { - val annos = Try { + JsonProtocol.deserializeTry(file).recoverWith { case jsonException => + // Try old protocol if new one fails + Try { val yaml = io.Source.fromFile(file).getLines().mkString("\n").parseYaml - yaml.convertTo[List[LegacyAnnotation]] + val result = yaml.convertTo[List[LegacyAnnotation]] + val msg = s"$file is a YAML file!\n" + + (" "*9) + "YAML Annotation files are deprecated! Use JSON" + Driver.dramaticWarning(msg) + result + }.orElse { // Propagate original JsonProtocol exception if YAML also fails + Failure(jsonException) } - annos match { - case Success(result) => - val msg = s"$file is a YAML file!\n" + - (" "*9) + "YAML Annotation files are deprecated! Use JSON" - Driver.dramaticWarning(msg) - result - case Failure(_) => throw new InvalidAnnotationFileException(file.toString) - } - } + }.get } val targetDirAnno = List(BlackBoxTargetDirAnno(optionsManager.targetDirName)) diff --git a/src/main/scala/firrtl/annotations/AnnotationUtils.scala b/src/main/scala/firrtl/annotations/AnnotationUtils.scala index 6b7f9a60..517cea26 100644 --- a/src/main/scala/firrtl/annotations/AnnotationUtils.scala +++ b/src/main/scala/firrtl/annotations/AnnotationUtils.scala @@ -3,6 +3,8 @@ package firrtl package annotations +import java.io.File + import org.json4s._ import org.json4s.native.JsonMethods._ import org.json4s.native.Serialization @@ -14,7 +16,15 @@ import firrtl.annotations.AnnotationYamlProtocol._ import firrtl.ir._ import firrtl.Utils.error -class InvalidAnnotationFileException(msg: String) extends FIRRTLException(msg) +case class InvalidAnnotationFileException(file: File, cause: Throwable = null) + extends FIRRTLException(s"$file, see cause below", cause) +case class InvalidAnnotationJSONException(msg: String) extends FIRRTLException(msg) +case class AnnotationFileNotFoundException(file: File) extends FIRRTLException( + s"Annotation file $file not found!" +) +case class AnnotationClassNotFoundException(className: String) extends FIRRTLException( + s"Annotation class $className not found! Please check spelling and classpath" +) object AnnotationUtils { def toYaml(a: LegacyAnnotation): String = a.toYaml.prettyPrint diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala index 5005ccb0..7b2617f5 100644 --- a/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -2,7 +2,7 @@ package firrtl package annotations -import scala.util.Try +import scala.util.{Try, Failure} import org.json4s._ import org.json4s.native.JsonMethods._ @@ -13,18 +13,6 @@ import firrtl.ir._ import firrtl.Utils.error object JsonProtocol { - - // Helper for error messages - private def prettifyJsonInput(in: JsonInput): String = { - def defaultToString(base: String, obj: Any): String = s"$base@${obj.hashCode.toHexString}" - in match { - case FileInput(file) => file.toString - case StringInput(o) => defaultToString("String", o) - case ReaderInput(o) => defaultToString("Reader", o) - case StreamInput(o) => defaultToString("Stream", o) - } - } - class TransformClassSerializer extends CustomSerializer[Class[_ <: Transform]](format => ( { case JString(s) => Class.forName(s).asInstanceOf[Class[_ <: Transform]] }, { case x: Class[_] => JString(x.getName) } @@ -66,19 +54,31 @@ object JsonProtocol { def deserialize(in: JsonInput): Seq[Annotation] = deserializeTry(in).get def deserializeTry(in: JsonInput): Try[Seq[Annotation]] = Try({ - def throwError() = throw new InvalidAnnotationFileException(prettifyJsonInput(in)) val parsed = parse(in) val annos = parsed match { case JArray(objs) => objs - case _ => throwError() + case x => throw new InvalidAnnotationJSONException( + s"Annotations must be serialized as a JArray, got ${x.getClass.getSimpleName} instead!") } // Gather classes so we can deserialize arbitrary Annotations val classes = annos.map({ case JObject(("class", JString(c)) :: tail) => c - case _ => throwError() + case obj => throw new InvalidAnnotationJSONException(s"Expected field 'class' not found! $obj") }).distinct val loaded = classes.map(Class.forName(_).asInstanceOf[Class[_ <: Annotation]]) implicit val formats = jsonFormat(loaded) read[List[Annotation]](in) - }) + }).recoverWith { + // Translate some generic errors to specific ones + case e: java.lang.ClassNotFoundException => + Failure(new AnnotationClassNotFoundException(e.getMessage)) + case e: org.json4s.ParserUtil.ParseException => + Failure(new InvalidAnnotationJSONException(e.getMessage)) + }.recoverWith { // If the input is a file, wrap in InvalidAnnotationFileException + case e => in match { + case FileInput(file) => + Failure(new InvalidAnnotationFileException(file, e)) + case _ => Failure(e) + } + } } |
